-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(agent-plan): team builder (#912)
* feat(agent-plan): team builder * feat(multiagent): team builder - 1 * feat(multiagent): team builder - 2 * feat(tool):add tool registry * feat(m78): add m78 client --------- Co-authored-by: wangyingjie3 <wangyingjie3@xiaomi.com>
1 parent
27593d6
commit aef6761
Showing
38 changed files
with
2,577 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ dependency-reduced-pom.xml | |
*.diff | ||
.DS_Store | ||
.idea/ | ||
.vscode/ | ||
*.iml | ||
logs/ | ||
.project | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<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/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>run.mone</groupId> | ||
<artifactId>ai</artifactId> | ||
<version>1.4-jdk20-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>m78</artifactId> | ||
|
||
<properties> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.squareup.okhttp3</groupId> | ||
<artifactId>okhttp</artifactId> | ||
<version>4.12.0</version> | ||
<optional>true</optional> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.google.code.gson</groupId> | ||
<artifactId>gson</artifactId> | ||
<version>2.9.1</version> | ||
<optional>true</optional> | ||
</dependency> | ||
<!-- FIXME: 讲道理client里不应该引入lombok,先这样吧... --> | ||
<dependency> | ||
<groupId>org.projectlombok</groupId> | ||
<artifactId>lombok</artifactId> | ||
<version>1.18.30</version> | ||
<optional>true</optional> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.commons</groupId> | ||
<artifactId>commons-lang3</artifactId> | ||
<version>3.12.0</version> | ||
<optional>true</optional> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.commons</groupId> | ||
<artifactId>commons-collections4</artifactId> | ||
<version>4.4</version> | ||
<optional>true</optional> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>com.google.guava</groupId> | ||
<artifactId>guava</artifactId> | ||
<version>33.2.0-jre</version> | ||
<optional>true</optional> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>ch.qos.logback</groupId> | ||
<artifactId>logback-classic</artifactId> | ||
<version>1.2.0</version> | ||
<optional>true</optional> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>ch.qos.logback</groupId> | ||
<artifactId>logback-core</artifactId> | ||
<optional>true</optional> | ||
<version>1.2.9</version> | ||
</dependency> | ||
|
||
|
||
<dependency> | ||
<groupId>org.junit.jupiter</groupId> | ||
<artifactId>junit-jupiter</artifactId> | ||
<version>5.8.2</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<version>3.11.0</version> | ||
<configuration> | ||
<compilerVersion>8</compilerVersion> | ||
<source>8</source> | ||
<target>8</target> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
16 changes: 16 additions & 0 deletions
16
jcommon/ai/m78/src/main/java/run.mone.m78.client/M78Client.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package run.mone.m78.client; | ||
|
||
import run.mone.m78.client.model.ClientType; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 10:36 | ||
*/ | ||
public interface M78Client { | ||
|
||
String getName(); | ||
|
||
ClientType getClientType(); | ||
|
||
|
||
} |
42 changes: 42 additions & 0 deletions
42
jcommon/ai/m78/src/main/java/run.mone.m78.client/M78ClientBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package run.mone.m78.client; | ||
|
||
import run.mone.m78.client.bot.BotHttpClient; | ||
import run.mone.m78.client.bot.BotWsClient; | ||
import run.mone.m78.client.flow.FlowHttpClient; | ||
import run.mone.m78.client.flow.FlowWsClient; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 10:46 | ||
*/ | ||
public class M78ClientBuilder { | ||
|
||
private String m78BotHttpEndpoint = ""; | ||
|
||
private String m78BotWsEndpoint = ""; | ||
|
||
private String m78FlowHttpEndpoint = ""; | ||
|
||
private String m78FlowWsEndpoint = ""; | ||
|
||
public BotHttpClient buildBotHttpClient() { | ||
// TODO | ||
return null; | ||
} | ||
|
||
|
||
public BotWsClient buildBotWsClient() { | ||
// TODO | ||
return null; | ||
} | ||
|
||
public FlowHttpClient buildFlowHttpClient() { | ||
// TODO | ||
return null; | ||
} | ||
|
||
public FlowWsClient buildFlowWsClient() { | ||
// TODO | ||
return null; | ||
} | ||
} |
273 changes: 273 additions & 0 deletions
273
jcommon/ai/m78/src/main/java/run.mone.m78.client/bot/BotHttpClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
package run.mone.m78.client.bot; | ||
|
||
import com.google.gson.Gson; | ||
import com.google.gson.JsonElement; | ||
import com.google.gson.JsonObject; | ||
import com.google.gson.JsonParser; | ||
import lombok.extern.slf4j.Slf4j; | ||
import okhttp3.MediaType; | ||
import okhttp3.OkHttpClient; | ||
import okhttp3.Request; | ||
import okhttp3.RequestBody; | ||
import okhttp3.Response; | ||
import org.apache.commons.lang3.StringUtils; | ||
import run.mone.m78.client.model.ClientType; | ||
import run.mone.m78.client.M78Client; | ||
import run.mone.m78.client.model.History; | ||
import run.mone.m78.client.model.M78BotReq; | ||
|
||
import java.util.Optional; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* <p>BotHttpClient类实现了M78Client接口,负责与M78Bot接口进行HTTP通信。 | ||
* 该类提供了构建HTTP请求、发送请求并处理响应的功能。 | ||
* <blockquote><pre> | ||
* 主要功能包括: | ||
* - 通过callBot方法调用M78Bot接口并返回响应结果。 | ||
* - 提供获取客户端名称和类型的方法。 | ||
* - 提供一个内部Builder类用于构建BotHttpClient实例。 | ||
* </pre></blockquote> | ||
* 该类使用了OkHttpClient进行HTTP请求,并支持通过JsonObject传递额外的参数信息。 | ||
* 需要传递token进行授权。 | ||
* | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 10:36 | ||
* @see M78Client | ||
* @see ClientType | ||
* @see M78BotReq | ||
* @see JsonObject | ||
* @see OkHttpClient | ||
*/ | ||
@Slf4j | ||
public class BotHttpClient implements M78Client { | ||
|
||
private String name = ClientType.BOT_HTTP.getTypeName(); | ||
|
||
private String url = "https://mone.test.mi.com/open-apis/ai-plugin-new/feature/router/probot/query"; | ||
|
||
private String token; | ||
|
||
private String model; | ||
|
||
private final ClientType type = ClientType.BOT_HTTP; | ||
|
||
private long timeout = 60; // second | ||
|
||
public BotHttpClient() { | ||
// default constructor | ||
} | ||
|
||
private BotHttpClient(Builder builder) { | ||
name = builder.name; | ||
url = builder.url; | ||
token = builder.token; | ||
timeout = builder.timeout; | ||
} | ||
|
||
|
||
public String callBot(M78BotReq botReq, JsonObject jsonObject) { | ||
return callBot(botReq, jsonObject, null); | ||
} | ||
|
||
|
||
public static Function<String, String> DEFAULT_FUNCTION = res -> { | ||
log.info("res:{}", res); | ||
JsonObject resObj = JsonParser.parseString(JsonParser.parseString(res) | ||
.getAsJsonObject() | ||
.get("data").getAsString()) | ||
.getAsJsonObject() | ||
.get("result") | ||
.getAsJsonObject(); | ||
JsonElement data = null; | ||
if (resObj.has("data")) { | ||
data = resObj.get("data"); | ||
} else { | ||
data = resObj.get("content"); | ||
} | ||
if (data.isJsonPrimitive()) { | ||
return data.getAsString(); | ||
} | ||
return data.toString(); | ||
}; | ||
|
||
/** | ||
* 调用M78Bot接口并返回响应结果 | ||
* | ||
* @param botReq 包含请求信息的M78BotReq对象 | ||
* @param jsonObject 额外的参数信息,JsonObject类型 | ||
* @return 接口响应结果字符串,若发生异常则返回空字符串 | ||
*/ | ||
public String callBot(M78BotReq botReq, JsonObject jsonObject, Function<String, String> function) { | ||
OkHttpClient client = new OkHttpClient.Builder() | ||
.connectTimeout(timeout, TimeUnit.SECONDS) | ||
.readTimeout(timeout, TimeUnit.SECONDS) | ||
.writeTimeout(timeout, TimeUnit.SECONDS) | ||
.build(); | ||
|
||
// 使用 Gson 的 JsonObject 构建请求体 | ||
JsonObject mainObject = new JsonObject(); | ||
mainObject.addProperty("userName", botReq.getUserName()); | ||
mainObject.addProperty("botId", botReq.getBotId()); | ||
mainObject.addProperty("input", botReq.getInput()); | ||
|
||
if (StringUtils.isNotBlank(this.model)) { | ||
mainObject.addProperty("model", this.model); | ||
} | ||
|
||
if (StringUtils.isNotEmpty(botReq.getToken())) { | ||
mainObject.addProperty("token", botReq.getToken()); | ||
} | ||
|
||
if (jsonObject != null) { | ||
if (jsonObject.has("history")) { | ||
JsonElement history = jsonObject.remove("history"); | ||
mainObject.add("history", history); | ||
} | ||
if (jsonObject.size() > 0) { | ||
mainObject.add("params", jsonObject); | ||
} | ||
} | ||
|
||
String jsonBody = mainObject.toString(); | ||
|
||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), jsonBody); | ||
|
||
log.debug("BotHttpClient callBot jsonBody:{}", jsonBody); | ||
|
||
Request request = new Request.Builder() | ||
.url(url) | ||
.post(body) | ||
.addHeader("Accept", "application/json, text/plain, */*") | ||
.addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8") | ||
.addHeader("Cache-Control", "no-cache") | ||
.addHeader("Connection", "keep-alive") | ||
.addHeader("Content-Type", "application/json") | ||
.addHeader("Authorization", Optional.ofNullable(token).orElseThrow(() -> new IllegalArgumentException("须传递token!"))) | ||
.build(); | ||
|
||
try { | ||
Response response = client.newCall(request).execute(); | ||
String res = response.body().string(); | ||
if (null == function) { | ||
return res; | ||
} | ||
return function.apply(res); | ||
} catch (Throwable e) { | ||
log.error("callBot error", e); | ||
} | ||
return ""; | ||
} | ||
|
||
|
||
public String callBot(M78BotReq botReq, JsonObject jsonObject, History history, Function<String, String> function) { | ||
OkHttpClient client = new OkHttpClient.Builder() | ||
.connectTimeout(timeout, TimeUnit.SECONDS) | ||
.callTimeout(timeout, TimeUnit.SECONDS) | ||
.readTimeout(timeout, TimeUnit.SECONDS) | ||
.writeTimeout(timeout, TimeUnit.SECONDS) | ||
.build(); | ||
|
||
// 使用 Gson 的 JsonObject 构建请求体 | ||
JsonObject mainObject = new JsonObject(); | ||
mainObject.addProperty("userName", botReq.getUserName()); | ||
mainObject.addProperty("botId", botReq.getBotId()); | ||
mainObject.addProperty("input", botReq.getInput()); | ||
|
||
if (null != history) { | ||
mainObject.add("history", new Gson().toJsonTree(history.getMessages())); | ||
} | ||
|
||
if (jsonObject != null) { | ||
if (jsonObject.size() > 0) { | ||
mainObject.add("params", jsonObject); | ||
} | ||
} | ||
|
||
String jsonBody = mainObject.toString(); | ||
|
||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), jsonBody); | ||
|
||
Request request = new Request.Builder() | ||
.url(url) | ||
.post(body) | ||
.addHeader("Accept", "application/json, text/plain, */*") | ||
.addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8") | ||
.addHeader("Cache-Control", "no-cache") | ||
.addHeader("Connection", "keep-alive") | ||
.addHeader("Content-Type", "application/json") | ||
.addHeader("Authorization", Optional.ofNullable(token).orElseThrow(() -> new IllegalArgumentException("须传递token!"))) | ||
.build(); | ||
|
||
try { | ||
Response response = client.newCall(request).execute(); | ||
String res = response.body().string(); | ||
if (null == function) { | ||
return res; | ||
} | ||
return function.apply(res); | ||
} catch (Throwable e) { | ||
log.error("callBot error", e); | ||
} | ||
return ""; | ||
} | ||
|
||
|
||
@Override | ||
public String getName() { | ||
return StringUtils.isNotBlank(name) ? name : type.getTypeName(); | ||
} | ||
|
||
@Override | ||
public ClientType getClientType() { | ||
return type; | ||
} | ||
|
||
public static BotHttpClient.Builder builder() { | ||
return new BotHttpClient.Builder(); | ||
} | ||
|
||
|
||
public static final class Builder { | ||
private String name = ClientType.BOT_HTTP.getTypeName(); | ||
private String url = "https://mone.test.mi.com/open-apis/ai-plugin-new/feature/router/probot/query"; | ||
private String token; | ||
|
||
private String model; | ||
|
||
private long timeout = 60; // second | ||
|
||
public Builder() { | ||
} | ||
|
||
public Builder name(String val) { | ||
name = val; | ||
return this; | ||
} | ||
|
||
public Builder url(String val) { | ||
url = val; | ||
return this; | ||
} | ||
|
||
public Builder token(String val) { | ||
token = val; | ||
return this; | ||
} | ||
|
||
public Builder model(String val) { | ||
model = val; | ||
return this; | ||
} | ||
|
||
public Builder timeout(long val) { | ||
timeout = val; | ||
return this; | ||
} | ||
|
||
public BotHttpClient build() { | ||
return new BotHttpClient(this); | ||
} | ||
} | ||
} |
256 changes: 256 additions & 0 deletions
256
jcommon/ai/m78/src/main/java/run.mone.m78.client/bot/BotWsClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
package run.mone.m78.client.bot; | ||
|
||
import com.google.common.base.Stopwatch; | ||
import com.google.gson.JsonObject; | ||
import com.google.gson.JsonParser; | ||
import lombok.extern.slf4j.Slf4j; | ||
import okhttp3.OkHttpClient; | ||
import okhttp3.Request; | ||
import okhttp3.Response; | ||
import okhttp3.WebSocket; | ||
import okhttp3.WebSocketListener; | ||
import org.apache.commons.lang3.StringUtils; | ||
import run.mone.m78.client.M78Client; | ||
import run.mone.m78.client.model.M78Message; | ||
import run.mone.m78.client.model.M78MessageCategory; | ||
import run.mone.m78.client.model.M78MessageType; | ||
import run.mone.m78.client.model.ClientType; | ||
import run.mone.m78.client.util.Base64Utils; | ||
import run.mone.m78.client.util.GsonUtils; | ||
|
||
import java.util.UUID; | ||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import java.util.function.Consumer; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 10:32 | ||
*/ | ||
@Slf4j | ||
public class BotWsClient implements M78Client { | ||
|
||
private String name; | ||
|
||
private final ClientType type = ClientType.BOT_WS; | ||
private WebSocket ws; | ||
|
||
private String projectName; | ||
|
||
private String url = "ws://10.38.204.190:8076/ws/bot/abc"; | ||
|
||
private String token = ""; | ||
|
||
private CountDownLatch latch; | ||
|
||
private BotWsClient(Builder builder) { | ||
name = builder.name; | ||
projectName = builder.projectName; | ||
url = builder.url; | ||
token = builder.token; | ||
latch = builder.latch; | ||
} | ||
|
||
public void start(Consumer<M78Message> consumer) { | ||
OkHttpClient client = new OkHttpClient(); | ||
Request request = new Request.Builder() | ||
.url(url) | ||
.header("athena-token", token) | ||
.build(); | ||
|
||
StringBuilder sb = new StringBuilder(); | ||
|
||
WebSocketListener listener = new WebSocketListener() { | ||
|
||
Stopwatch sw = Stopwatch.createStarted(); | ||
|
||
private AtomicBoolean cancel = new AtomicBoolean(false); | ||
|
||
|
||
@Override | ||
public void onOpen(WebSocket webSocket, Response response) { | ||
log.info("ws open"); | ||
} | ||
|
||
|
||
@Override | ||
public void onMessage(WebSocket webSocket, String text) { | ||
log.info("Received:{}", text); | ||
JsonObject msg = JsonParser.parseString(text).getAsJsonObject(); | ||
String type = GsonUtils.get(msg, "type", ""); | ||
String messageType = GsonUtils.get(msg, "messageType", ""); | ||
|
||
if (StringUtils.isBlank(messageType)) { | ||
log.warn("no valid message type, will discard!"); | ||
return; | ||
} | ||
if (messageType.startsWith("BOT")) { | ||
dispatchBotMsg(webSocket, messageType, msg); | ||
} else if (messageType.startsWith("FLOW")) { | ||
dispatchFlowMsg(webSocket, messageType, msg); | ||
} | ||
|
||
} | ||
|
||
@Override | ||
public void onClosing(WebSocket webSocket, int code, String reason) { | ||
webSocket.close(1000, null); | ||
log.info("Closing: " + code + " / " + reason); | ||
} | ||
|
||
@Override | ||
public void onFailure(WebSocket webSocket, Throwable t, Response response) { | ||
log.error("Error: " + t.getMessage()); | ||
} | ||
|
||
private void dispatchBotMsg(WebSocket webSocket, String messageType, JsonObject msg) { | ||
//发生错误了 | ||
if (messageType.equals("BOT_STREAM_FAILURE")) { | ||
String message = GsonUtils.get(msg, "message", ""); | ||
String id = GsonUtils.get(msg, "id", UUID.randomUUID().toString()); | ||
consumer.accept(M78Message.builder().projectName(projectName).text(message).type(M78MessageType.failure).id(id).build()); | ||
if (null != latch) { | ||
latch.countDown(); | ||
} | ||
return; | ||
} | ||
|
||
if (messageType.equals("BOT_STREAM_BEGIN")) { | ||
String id = GsonUtils.get(msg, "id", UUID.randomUUID().toString()); | ||
consumer.accept(M78Message.builder().projectName(projectName).text("").type(M78MessageType.begin).id(id).build()); | ||
return; | ||
} | ||
|
||
if (messageType.equals("BOT_STREAM_RESULT")) { | ||
log.info("BOT_STREAM_RESULT:{}", sb); | ||
String id = GsonUtils.get(msg, "id", UUID.randomUUID().toString()); | ||
consumer.accept(M78Message.builder().projectName(projectName).text(sb.toString()).type(M78MessageType.success).id(id).build()); | ||
webSocket.close(1000, null); | ||
if (null != latch) { | ||
latch.countDown(); | ||
} | ||
return; | ||
} | ||
|
||
if (messageType.equals("BOT_STREAM_EVENT")) { | ||
String message = GsonUtils.get(msg, "message", ""); | ||
//解决中文乱码的问题 | ||
message = Base64Utils.decodeBase64String(message); | ||
log.info("message:{}", message); | ||
sb.append(message); | ||
//如果被取消了,则不再追加内容了 | ||
if (!this.cancel.get()) { | ||
String id = GsonUtils.get(msg, "id", UUID.randomUUID().toString()); | ||
consumer.accept(M78Message.builder().projectName(projectName).code(false).text(message).type(M78MessageType.process).id(id).build()); | ||
} | ||
} | ||
|
||
if (messageType.equals("BOT_RESULT")) { | ||
String message = GsonUtils.get(msg, "data", ""); | ||
log.info("message:{}", message); | ||
if (!this.cancel.get()) { | ||
String id = GsonUtils.get(msg, "id", UUID.randomUUID().toString()); | ||
consumer.accept(M78Message.builder().projectName(projectName).code(false).text("").type(M78MessageType.begin).id(id).build()); | ||
consumer.accept(M78Message.builder().projectName(projectName).code(false).text(message).type(M78MessageType.process).id(id).build()); | ||
consumer.accept(M78Message.builder().projectName(projectName).code(false).text("").type(M78MessageType.success).id(id).build()); | ||
} | ||
} | ||
} | ||
|
||
private void dispatchFlowMsg(WebSocket webSocket, String messageType, JsonObject msg) { | ||
if (messageType.equals("FLOW_EXECUTE_STATUS") | ||
|| messageType.equals("FLOW_EXECUTE_FAILURE")) { | ||
String message = msg.toString(); | ||
String id = GsonUtils.get(msg, "id", UUID.randomUUID().toString()); | ||
consumer.accept(M78Message.builder() | ||
.projectName(projectName) | ||
.text(message) | ||
.category(M78MessageCategory.flow) | ||
.type(M78MessageType.process) | ||
.id(id) | ||
.build()); | ||
if (null != latch) { | ||
latch.countDown(); | ||
} | ||
return; | ||
} | ||
|
||
if (messageType.equals("FLOW_EXECUTE_MESSAGE")) { | ||
// NOT USED FOR NOW | ||
} | ||
} | ||
}; | ||
ws = client.newWebSocket(request, listener); | ||
log.info("init finish"); | ||
} | ||
|
||
|
||
//发送消息 | ||
public void send(JsonObject req) { | ||
ws.send(req.toString()); | ||
} | ||
|
||
//发送消息 - 1 | ||
public void send(String req) { | ||
ws.send(req); | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return StringUtils.isNotBlank(name) ? name : type.getTypeName(); | ||
} | ||
|
||
@Override | ||
public ClientType getClientType() { | ||
return type; | ||
} | ||
|
||
public static BotWsClient.Builder builder() { | ||
return new BotWsClient.Builder(); | ||
} | ||
|
||
|
||
public static final class Builder { | ||
private String name; | ||
private String projectName; | ||
private String url = "ws://10.38.204.190:8076/ws/bot/abc"; | ||
private String token; | ||
private CountDownLatch latch; | ||
|
||
private Builder() { | ||
} | ||
|
||
public static Builder builder() { | ||
return new Builder(); | ||
} | ||
|
||
public Builder name(String val) { | ||
name = val; | ||
return this; | ||
} | ||
|
||
public Builder projectName(String val) { | ||
projectName = val; | ||
return this; | ||
} | ||
|
||
public Builder url(String val) { | ||
url = val; | ||
return this; | ||
} | ||
|
||
public Builder token(String val) { | ||
token = val; | ||
return this; | ||
} | ||
|
||
public Builder latch(CountDownLatch val) { | ||
latch = val; | ||
return this; | ||
} | ||
|
||
public BotWsClient build() { | ||
return new BotWsClient(this); | ||
} | ||
} | ||
} |
110 changes: 110 additions & 0 deletions
110
jcommon/ai/m78/src/main/java/run.mone.m78.client/bot/DefaultBotMsgConsumer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package run.mone.m78.client.bot; | ||
|
||
import com.google.common.base.Stopwatch; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.jetbrains.annotations.NotNull; | ||
import run.mone.m78.client.flow.DefaultFlowMsgConsumer; | ||
import run.mone.m78.client.model.M78Message; | ||
import run.mone.m78.client.model.M78MessageType; | ||
|
||
import java.util.Map; | ||
import java.util.function.Consumer; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 16:39 | ||
*/ | ||
@Slf4j | ||
public class DefaultBotMsgConsumer implements Consumer<M78Message> { | ||
|
||
private final DefaultFlowMsgConsumer flowMsgConsumer = new DefaultFlowMsgConsumer(); | ||
|
||
private Stopwatch sw; | ||
|
||
public DefaultBotMsgConsumer(Stopwatch sw) { | ||
this.sw = sw; | ||
} | ||
|
||
@Override | ||
public void accept(M78Message msg) { | ||
log.info("接收到消息:{}", msg); | ||
switch (msg.getCategory()) { | ||
case bot: | ||
handleBotCategory(msg); | ||
break; | ||
case flow: | ||
handleFlowCategory(msg); | ||
break; | ||
default: | ||
log.warn("未知消息类型:{}, 不做处理", msg.getCategory()); | ||
} | ||
} | ||
|
||
private void handleBotCategory(M78Message msg) { | ||
doHandleBotCategory(msg); | ||
} | ||
|
||
|
||
private void handleFlowCategory(M78Message msg) { | ||
if (doHandleFlowCategory(msg)) { | ||
return; | ||
} | ||
flowMsgConsumer.accept(msg); | ||
} | ||
|
||
protected void doHandleBotCategory(M78Message msg) { | ||
processGeneratedMsg(msg, Context.builder().build()); | ||
} | ||
|
||
protected boolean doHandleFlowCategory(M78Message msg) { | ||
return false; | ||
} | ||
|
||
|
||
protected void processGeneratedMsg(M78Message msg, Context context) { | ||
handleBeginMsg(msg, context); | ||
handleContentMsg(msg, context); | ||
handleEndMsg(msg, context); | ||
handleError(msg, context); | ||
} | ||
|
||
protected void handleBeginMsg(@NotNull M78Message msg, Context context) { | ||
log.info("handleBeginMsg: {}, context:{}", msg.getId(), context); | ||
if (msg.getType().equals(M78MessageType.begin)) { | ||
// TODO you may override this | ||
} | ||
} | ||
|
||
protected void handleContentMsg(M78Message msg, Context context) { | ||
log.info("handleContentMsg: {}, context:{}", msg.getId(), context); | ||
if (msg.getType().equals(M78MessageType.process)) { | ||
// TODO you may override this | ||
} | ||
} | ||
|
||
protected void handleEndMsg(M78Message msg, Context context) { | ||
log.info("handleEndMsg: {}, context:{}", msg.getId(), context); | ||
if (msg.getType().equals(M78MessageType.success)) { | ||
// TODO you may override this | ||
} | ||
} | ||
|
||
protected void handleError(M78Message msg, Context context) { | ||
log.info("handleError: {}, context:{}", msg.getId(), context); | ||
if (msg.getType().equals(M78MessageType.failure)) { | ||
// TODO you may override this | ||
} | ||
} | ||
|
||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@Data | ||
@Builder | ||
public static class Context { | ||
private Map<String, Object> context; | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
jcommon/ai/m78/src/main/java/run.mone.m78.client/flow/DefaultFlowMsgConsumer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package run.mone.m78.client.flow; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import run.mone.m78.client.model.M78Message; | ||
import run.mone.m78.client.model.M78MessageType; | ||
|
||
import java.util.function.Consumer; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 16:40 | ||
*/ | ||
@Slf4j | ||
public class DefaultFlowMsgConsumer implements Consumer<M78Message> { | ||
|
||
@Override | ||
public void accept(M78Message m78Message) { | ||
int msgLength = m78Message.getMessage().length(); | ||
log.info("receive m78 message type:{}, size:{}", m78Message.getType().name(), msgLength); | ||
handleFlowMsg(m78Message, m78Message.getType()); | ||
} | ||
|
||
protected void handleFlowMsg(M78Message m78Message, M78MessageType messageType) { | ||
switch (messageType) { | ||
case process: | ||
handleFlowExecuteStatus(m78Message); | ||
break; | ||
case failure: | ||
handleFlowExecuteFailure(m78Message); | ||
break; | ||
default: | ||
log.warn("message type:{}, not a valid type, will discard!", messageType); | ||
} | ||
} | ||
|
||
protected void handleFlowExecuteFailure(M78Message m78Message) { | ||
// send this to visual | ||
// TODO | ||
} | ||
|
||
private void handleFlowExecuteStatus(M78Message m78Message) { | ||
// send this to visual | ||
// TODO | ||
} | ||
|
||
} |
150 changes: 150 additions & 0 deletions
150
jcommon/ai/m78/src/main/java/run.mone.m78.client/flow/FlowHttpClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package run.mone.m78.client.flow; | ||
|
||
import com.google.gson.JsonElement; | ||
import com.google.gson.JsonObject; | ||
import com.google.gson.JsonParser; | ||
import lombok.extern.slf4j.Slf4j; | ||
import okhttp3.MediaType; | ||
import okhttp3.OkHttpClient; | ||
import okhttp3.Request; | ||
import okhttp3.RequestBody; | ||
import okhttp3.Response; | ||
import org.apache.commons.lang3.StringUtils; | ||
import run.mone.m78.client.model.ClientType; | ||
import run.mone.m78.client.M78Client; | ||
import run.mone.m78.client.model.M78FlowReq; | ||
import run.mone.m78.client.util.GsonUtils; | ||
|
||
import java.util.Optional; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 10:36 | ||
*/ | ||
@Slf4j | ||
public class FlowHttpClient implements M78Client { | ||
|
||
private String name = ClientType.FLOW_HTTP.getTypeName(); | ||
|
||
private String url = "https://mone.test.mi.com/open-apis/ai-plugin-new/feature/router/flow/querySync"; | ||
|
||
private String token; | ||
|
||
private long timeout = 60; | ||
|
||
private final ClientType type = ClientType.FLOW_HTTP; | ||
|
||
public FlowHttpClient() { | ||
// default constructor | ||
} | ||
|
||
private FlowHttpClient(Builder builder) { | ||
name = builder.name; | ||
url = builder.url; | ||
token = builder.token; | ||
timeout = builder.timeout; | ||
} | ||
|
||
public static FlowHttpClient.Builder builder() { | ||
return new FlowHttpClient.Builder(); | ||
} | ||
|
||
public String callFlow(M78FlowReq flowReq, JsonObject jsonObject) { | ||
OkHttpClient client = new OkHttpClient.Builder() | ||
.connectTimeout(timeout, TimeUnit.SECONDS) | ||
.readTimeout(timeout, TimeUnit.SECONDS) | ||
.writeTimeout(timeout, TimeUnit.SECONDS) | ||
.build(); | ||
|
||
// 使用 Gson 的 JsonObject 构建请求体 | ||
JsonObject mainObject = new JsonObject(); | ||
mainObject.addProperty("userName", flowReq.getUserName()); | ||
mainObject.addProperty("flowId", flowReq.getFlowId()); | ||
mainObject.add("input", GsonUtils.GSON.toJsonTree(flowReq.getInputs())); | ||
|
||
if (jsonObject != null) { | ||
if (jsonObject.has("history")) { | ||
JsonElement history = jsonObject.remove("history"); | ||
mainObject.add("history", history); | ||
} | ||
if (jsonObject.size() > 0) { | ||
mainObject.add("params", jsonObject); | ||
} | ||
} | ||
|
||
String jsonBody = mainObject.toString(); | ||
|
||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), jsonBody); | ||
|
||
Request request = new Request.Builder() | ||
.url(url) | ||
.post(body) | ||
.addHeader("Accept", "application/json, text/plain, */*") | ||
.addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8") | ||
.addHeader("Cache-Control", "no-cache") | ||
.addHeader("Connection", "keep-alive") | ||
.addHeader("Content-Type", "application/json") | ||
.addHeader("Authorization", Optional.ofNullable(token).orElseThrow(() -> new IllegalArgumentException("须传递token!"))) | ||
.build(); | ||
|
||
try { | ||
Response response = client.newCall(request).execute(); | ||
return response.body().string(); | ||
} catch (Throwable e) { | ||
log.error("callFlow error", e); | ||
} | ||
return ""; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return StringUtils.isNotBlank(name) ? name : type.getTypeName(); | ||
} | ||
|
||
@Override | ||
public ClientType getClientType() { | ||
return type; | ||
} | ||
|
||
public static final class Builder { | ||
|
||
private String name = ClientType.FLOW_HTTP.getTypeName(); | ||
|
||
private String url = "https://mone.test.mi.com/open-apis/ai-plugin-new/feature/router/flow/querySync"; | ||
private String token; | ||
|
||
public long timeout = 60; | ||
|
||
private Builder() { | ||
} | ||
|
||
public static Builder builder() { | ||
return new Builder(); | ||
} | ||
|
||
public Builder name(String val) { | ||
name = val; | ||
return this; | ||
} | ||
|
||
public Builder url(String val) { | ||
url = val; | ||
return this; | ||
} | ||
|
||
public Builder token(String val) { | ||
token = val; | ||
return this; | ||
} | ||
|
||
public Builder timeout(long val) { | ||
timeout = val; | ||
return this; | ||
} | ||
|
||
public FlowHttpClient build() { | ||
return new FlowHttpClient(this); | ||
} | ||
} | ||
} |
285 changes: 285 additions & 0 deletions
285
jcommon/ai/m78/src/main/java/run.mone.m78.client/flow/FlowWsClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,285 @@ | ||
package run.mone.m78.client.flow; | ||
|
||
import com.google.common.base.Stopwatch; | ||
import com.google.gson.JsonObject; | ||
import com.google.gson.JsonParser; | ||
import lombok.extern.slf4j.Slf4j; | ||
import okhttp3.OkHttpClient; | ||
import okhttp3.Request; | ||
import okhttp3.Response; | ||
import okhttp3.WebSocket; | ||
import okhttp3.WebSocketListener; | ||
import org.apache.commons.lang3.StringUtils; | ||
import run.mone.m78.client.M78Client; | ||
import run.mone.m78.client.model.ClientType; | ||
import run.mone.m78.client.model.M78FlowCMD; | ||
import run.mone.m78.client.model.M78FlowOperateType; | ||
import run.mone.m78.client.model.M78FlowReq; | ||
import run.mone.m78.client.model.M78Message; | ||
import run.mone.m78.client.model.M78MessageCategory; | ||
import run.mone.m78.client.model.M78MessageType; | ||
import run.mone.m78.client.util.GsonUtils; | ||
|
||
import javax.annotation.Nonnull; | ||
import java.util.Map; | ||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import java.util.function.Consumer; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/11/24 17:47 | ||
*/ | ||
@Slf4j | ||
public class FlowWsClient implements M78Client { | ||
|
||
private String name = ClientType.FLOW_WS.getTypeName(); | ||
|
||
private ClientType type = ClientType.FLOW_WS; | ||
|
||
private WebSocket ws; | ||
|
||
private String projectName; | ||
|
||
private String url = "ws://10.38.204.190:8076/ws/flow/stream/access"; | ||
|
||
|
||
private CountDownLatch latch; | ||
|
||
private FlowWsClient(Builder builder) { | ||
projectName = builder.projectName; | ||
url = builder.url; | ||
latch = builder.latch; | ||
} | ||
|
||
/** | ||
* 初始化WebSocket连接并处理消息 | ||
* | ||
* @param consumer 消息消费者,用于处理接收到的消息 | ||
* @param userName 用户名,用于请求头中的x-account字段 | ||
*/ | ||
public void start(Consumer<M78Message> consumer, String userName) { | ||
OkHttpClient client = new OkHttpClient(); | ||
Request request = new Request.Builder() | ||
.url(url) | ||
.header("x-account", userName) | ||
.build(); | ||
|
||
StringBuilder sb = new StringBuilder(); | ||
|
||
WebSocketListener listener = new WebSocketListener() { | ||
|
||
Stopwatch sw = Stopwatch.createStarted(); | ||
|
||
private AtomicBoolean cancel = new AtomicBoolean(false); | ||
|
||
|
||
@Override | ||
public void onOpen(WebSocket webSocket, Response response) { | ||
log.info("ws open"); | ||
onClientInit(webSocket, response, consumer); | ||
} | ||
|
||
@Override | ||
public void onMessage(WebSocket webSocket, String text) { | ||
log.info("Received:{}", text); | ||
onMessageReceived(webSocket, text, consumer); | ||
} | ||
|
||
@Override | ||
public void onClosing(WebSocket webSocket, int code, String reason) { | ||
log.info("M78WsClient Closing, code: {}, reason:{}", code, reason); | ||
onClientClose(webSocket, code, reason, consumer); | ||
webSocket.close(1000, null); | ||
} | ||
|
||
@Override | ||
public void onFailure(WebSocket webSocket, Throwable t, Response response) { | ||
onClientError(webSocket, t, response, consumer); | ||
log.error("M78WsClient Error: {}, response:{}", t.getMessage(), response.toString()); | ||
} | ||
}; | ||
ws = client.newWebSocket(request, listener); | ||
log.info("init finish"); | ||
} | ||
|
||
|
||
/** | ||
* 客户端初始化方法,可以被重写 | ||
* | ||
* @param webSocket WebSocket对象 | ||
* @param response 响应对象 | ||
* @param consumer 消费者对象,用于处理M78Message消息 | ||
*/ | ||
public void onClientInit(WebSocket webSocket, Response response, Consumer<M78Message> consumer) { | ||
// you may override this | ||
} | ||
|
||
/** | ||
* 处理接收到的WebSocket消息, 可以被重写 | ||
* 默认行为是将消息处理委托给 Consumer<M78Message> consumer | ||
* | ||
* @param webSocket WebSocket对象 | ||
* @param text 接收到的消息文本 | ||
* @param consumer 消费处理M78Message对象的消费者 | ||
*/ | ||
public void onMessageReceived(WebSocket webSocket, String text, Consumer<M78Message> consumer) { | ||
// you may override this | ||
JsonObject msg = JsonParser.parseString(text).getAsJsonObject(); | ||
consumer.accept(M78Message.builder() | ||
.text(msg.toString()) | ||
.category(M78MessageCategory.flow) | ||
.type(M78MessageType.process) | ||
.build()); | ||
} | ||
|
||
/** | ||
* 当客户端关闭连接时调用此方法, 可以被重写 | ||
* | ||
* @param webSocket WebSocket对象,表示客户端连接 | ||
* @param code 关闭连接的状态码 | ||
* @param reason 关闭连接的原因 | ||
* @param consumer 消费者对象,用于处理M78Message消息 | ||
*/ | ||
public void onClientClose(WebSocket webSocket, int code, String reason, Consumer<M78Message> consumer) { | ||
// you may override this | ||
} | ||
|
||
/** | ||
* 处理WebSocket客户端错误的回调方法,可以被重写 | ||
* | ||
* @param webSocket WebSocket对象 | ||
* @param t 异常对象 | ||
* @param response 响应对象 | ||
* @param consumer 消费者对象,用于处理M78Message | ||
*/ | ||
public void onClientError(WebSocket webSocket, Throwable t, Response response, Consumer<M78Message> consumer) { | ||
// you may override this | ||
} | ||
|
||
public void executeFlow(String userName, String flowId, Map<String, Object> inputs) { | ||
try { | ||
M78FlowReq req = M78FlowReq.builder() | ||
.userName(userName) | ||
.flowId(flowId) | ||
.inputs(inputs) | ||
.operateCmd(M78FlowOperateType.TEST_FLOW) | ||
.build(); | ||
operateFlow(req); | ||
} catch (Throwable e) { | ||
log.error("Error executing flow:", e); | ||
} | ||
} | ||
|
||
public void resumeFlow(String userName, String flowId, String flowRecordId) { | ||
try { | ||
M78FlowReq req = M78FlowReq.builder() | ||
.userName(userName) | ||
.flowId(flowId) | ||
.flowRecordId(flowRecordId) | ||
.operateCmd(M78FlowOperateType.OPERATE_FLOW) | ||
.cmd(M78FlowCMD.MANUAL_CONFIRM_FLOW) | ||
.build(); | ||
operateFlow(req); | ||
} catch (Throwable e) { | ||
log.error("resumeFlow error:", e); | ||
} | ||
} | ||
|
||
public void terminateFlow(String userName, String flowId, String flowRecordId) { | ||
try { | ||
M78FlowReq req = M78FlowReq.builder() | ||
.userName(userName) | ||
.flowId(flowId) | ||
.flowRecordId(flowRecordId) | ||
.operateCmd(M78FlowOperateType.OPERATE_FLOW) | ||
.cmd(M78FlowCMD.CANCEL_FLOW) | ||
.build(); | ||
operateFlow(req); | ||
} catch (Throwable e) { | ||
log.error("Failed to terminate flow:", e); | ||
} | ||
} | ||
|
||
public void getFlowStatus(String userName, String flowId, String flowRecordId) { | ||
try { | ||
M78FlowReq req = M78FlowReq.builder() | ||
.userName(userName) | ||
.flowId(flowId) | ||
.flowRecordId(flowRecordId) | ||
.operateCmd(M78FlowOperateType.GET_STATUS) | ||
.build(); | ||
operateFlow(req); | ||
} catch (Throwable e) { | ||
log.error("getFlowStatus error", e); | ||
} | ||
} | ||
|
||
protected void operateFlow(@Nonnull M78FlowReq req) { | ||
try { | ||
if (StringUtils.isBlank(req.getOperateCmd())) { | ||
log.error("empty operateCmd! WILL DO NOTHING!"); | ||
return; | ||
} | ||
send(GsonUtils.GSON.toJson(req)); | ||
} catch (Throwable e) { | ||
log.error("Error operating flow", e); | ||
} | ||
} | ||
|
||
//发送消息 | ||
protected void send(String req) { | ||
ws.send(req); | ||
} | ||
|
||
private static String get(JsonObject obj, String key, String defaultValue) { | ||
if (obj.has(key)) { | ||
return obj.get(key).getAsString(); | ||
} | ||
return defaultValue; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return StringUtils.isNotBlank(name) ? name : type.getTypeName(); | ||
} | ||
|
||
@Override | ||
public ClientType getClientType() { | ||
return type; | ||
} | ||
|
||
public static FlowWsClient.Builder builder() { | ||
return new FlowWsClient.Builder(); | ||
} | ||
|
||
public static final class Builder { | ||
private String projectName; | ||
private String url = "ws://10.38.204.190:8076/ws/flow/stream/access"; | ||
private CountDownLatch latch; | ||
|
||
private Builder() { | ||
} | ||
|
||
|
||
public Builder projectName(String val) { | ||
projectName = val; | ||
return this; | ||
} | ||
|
||
public Builder url(String val) { | ||
url = val; | ||
return this; | ||
} | ||
|
||
public Builder latch(CountDownLatch val) { | ||
latch = val; | ||
return this; | ||
} | ||
|
||
public FlowWsClient build() { | ||
return new FlowWsClient(this); | ||
} | ||
} | ||
} | ||
|
49 changes: 49 additions & 0 deletions
49
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/ClientType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package run.mone.m78.client.model; | ||
|
||
import java.util.Arrays; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 10:37 | ||
*/ | ||
public enum ClientType { | ||
|
||
UNKNOWN(-1, "unknown"), | ||
BOT_HTTP(0, "bot_http"), | ||
BOT_WS(1, "bot_ws"), | ||
|
||
FLOW_HTTP(2, "flow_http"), | ||
|
||
FLOW_WS(3, "flow_ws"); | ||
|
||
private final int code; | ||
private final String typeName; | ||
|
||
private static final Map<Integer, ClientType> valMap = Arrays.stream(values()).collect(Collectors.toMap(ClientType::getCode, Function.identity())); | ||
|
||
private static final Map<String, ClientType> nameMap = Arrays.stream(values()).collect(Collectors.toMap(ClientType::getTypeName, Function.identity())); | ||
|
||
ClientType(int code, String typeName) { | ||
this.code = code; | ||
this.typeName = typeName; | ||
} | ||
|
||
public int getCode() { | ||
return code; | ||
} | ||
|
||
public String getTypeName() { | ||
return typeName; | ||
} | ||
|
||
public static ClientType getTypeByCode(int code) { | ||
return valMap.getOrDefault(code, UNKNOWN); | ||
} | ||
|
||
public static ClientType getTypeByName(String name) { | ||
return nameMap.getOrDefault(name, UNKNOWN); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/History.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package run.mone.m78.client.model; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* @author goodjava@qq.com | ||
* @date 2024/9/10 12:58 | ||
*/ | ||
@Data | ||
@Builder | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
public class History { | ||
|
||
private List<Message> messages; | ||
|
||
|
||
} |
25 changes: 25 additions & 0 deletions
25
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/M78BotReq.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package run.mone.m78.client.model; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 15:24 | ||
*/ | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@Data | ||
@Builder | ||
public class M78BotReq { | ||
|
||
private String botId; | ||
|
||
private String userName; | ||
|
||
private String input; | ||
|
||
private String token; | ||
} |
19 changes: 19 additions & 0 deletions
19
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/M78Data.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package run.mone.m78.client.model; | ||
|
||
import lombok.Data; | ||
|
||
/** | ||
* @author goodjava@qq.com | ||
* @date 2024/9/10 10:14 | ||
*/ | ||
@Data | ||
public class M78Data<D> { | ||
|
||
private int code; | ||
|
||
private String message; | ||
|
||
private D data; | ||
|
||
|
||
} |
16 changes: 16 additions & 0 deletions
16
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/M78FlowCMD.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package run.mone.m78.client.model; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 14:43 | ||
*/ | ||
public class M78FlowCMD { | ||
|
||
public static final String CANCEL_FLOW = "cancelFlow"; | ||
|
||
public static final String MANUAL_CONFIRM_FLOW = "manualConfirmFlow"; | ||
|
||
public static final String GOTO_FLOW = "gotoFlow"; | ||
|
||
public static final String MODIFY_PARAM = "modifyParam"; | ||
} |
14 changes: 14 additions & 0 deletions
14
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/M78FlowOperateType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package run.mone.m78.client.model; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 14:40 | ||
*/ | ||
public class M78FlowOperateType { | ||
|
||
public static final String GET_STATUS = "getStatus"; | ||
|
||
public static final String TEST_FLOW = "testFlow"; | ||
|
||
public static final String OPERATE_FLOW = "operateFlow"; | ||
} |
31 changes: 31 additions & 0 deletions
31
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/M78FlowReq.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package run.mone.m78.client.model; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
import java.util.Map; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 14:38 | ||
*/ | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@Data | ||
@Builder | ||
public class M78FlowReq { | ||
|
||
private String userName; | ||
|
||
private String flowId; | ||
|
||
private String flowRecordId; | ||
|
||
private String operateCmd; | ||
|
||
private String cmd; | ||
|
||
private Map<String, Object> inputs; | ||
} |
33 changes: 33 additions & 0 deletions
33
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/M78Message.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package run.mone.m78.client.model; | ||
|
||
import lombok.Builder; | ||
import lombok.Data; | ||
|
||
import java.io.Serializable; | ||
|
||
/** | ||
* @author goodjava@qq.com | ||
* @date 2023/6/9 14:32 | ||
*/ | ||
@Data | ||
@Builder | ||
public class M78Message implements Serializable { | ||
|
||
private M78MessageType type; | ||
|
||
private String message; | ||
|
||
@Builder.Default | ||
private M78MessageCategory category = M78MessageCategory.bot; | ||
|
||
private String id; | ||
|
||
private String text; | ||
|
||
private String projectName; | ||
|
||
//代表是否是编码(```code```) | ||
@Builder.Default | ||
private boolean code = true; | ||
|
||
} |
10 changes: 10 additions & 0 deletions
10
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/M78MessageCategory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package run.mone.m78.client.model; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/15/24 17:16 | ||
*/ | ||
public enum M78MessageCategory { | ||
bot, | ||
flow; | ||
} |
14 changes: 14 additions & 0 deletions
14
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/M78MessageType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package run.mone.m78.client.model; | ||
|
||
/** | ||
* @author goodjava@qq.com | ||
* @date 2023/6/9 14:33 | ||
*/ | ||
public enum M78MessageType { | ||
|
||
begin, | ||
process, | ||
success, | ||
failure, | ||
|
||
} |
22 changes: 22 additions & 0 deletions
22
jcommon/ai/m78/src/main/java/run.mone.m78.client/model/Message.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package run.mone.m78.client.model; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
/** | ||
* @author goodjava@qq.com | ||
* @date 2024/9/10 12:59 | ||
*/ | ||
@Data | ||
@Builder | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
public class Message { | ||
|
||
private String role; | ||
|
||
private String content; | ||
|
||
} |
15 changes: 15 additions & 0 deletions
15
jcommon/ai/m78/src/main/java/run.mone.m78.client/util/Base64Utils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package run.mone.m78.client.util; | ||
|
||
import java.nio.charset.Charset; | ||
import java.util.Base64; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 14:34 | ||
*/ | ||
public class Base64Utils { | ||
|
||
public static String decodeBase64String(String str) { | ||
return new String(Base64.getDecoder().decode(str.getBytes(Charset.forName("utf8"))), Charset.forName("utf8")); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
jcommon/ai/m78/src/main/java/run.mone.m78.client/util/GsonUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package run.mone.m78.client.util; | ||
|
||
import com.google.gson.Gson; | ||
import com.google.gson.JsonObject; | ||
|
||
import java.util.function.Supplier; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 14:35 | ||
*/ | ||
public class GsonUtils { | ||
|
||
public static final Gson GSON = new Gson(); | ||
|
||
|
||
public static boolean get(JsonObject obj, String key, boolean defaultValue) { | ||
if (obj.has(key)) { | ||
return Boolean.valueOf(obj.get(key).getAsString()); | ||
} | ||
return defaultValue; | ||
} | ||
|
||
public static String get(JsonObject obj, String key, String defaultValue) { | ||
if (obj.has(key)) { | ||
return obj.get(key).getAsString(); | ||
} | ||
return defaultValue; | ||
} | ||
|
||
public static String get(JsonObject obj, String key, Supplier<String> supplier) { | ||
if (obj.has(key)) { | ||
return obj.get(key).getAsString(); | ||
} | ||
return supplier.get(); | ||
} | ||
|
||
} |
185 changes: 185 additions & 0 deletions
185
jcommon/ai/m78/src/test/java/run.mone.m78.client.test/BotHttpClientTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
package run.mone.m78.client.test; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
import com.google.common.collect.Lists; | ||
import com.google.gson.Gson; | ||
import com.google.gson.JsonObject; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.junit.jupiter.api.Test; | ||
import run.mone.m78.client.bot.BotHttpClient; | ||
import run.mone.m78.client.model.History; | ||
import run.mone.m78.client.model.M78BotReq; | ||
import run.mone.m78.client.model.Message; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/22/24 15:57 | ||
*/ | ||
public class BotHttpClientTest { | ||
|
||
@Test | ||
public void testBotHttpClient() { | ||
BotHttpClient client = BotHttpClient.builder() | ||
// .url("http://127.0.0.1:8077/open-apis/v1/ai-plugin-new/feature/router/probot/query") | ||
.token("e1c51eaa-2f39-4cbc-8ec0-b6ab9d117a02").build(); | ||
String res = client.callBot(M78BotReq.builder() | ||
.botId("100093") | ||
.userName("wangyingjie3") | ||
.input("北京今天的天气?") | ||
.build(), null); | ||
System.out.println(res); | ||
} | ||
|
||
@Test | ||
public void testBotHttpClientWithHistory() { | ||
BotHttpClient client = BotHttpClient.builder() | ||
// .url("http://127.0.0.1:8077/open-apis/v1/ai-plugin-new/feature/router/probot/query") | ||
.token("e1c51eaa-2f39-4cbc-8ec0-b6ab9d117a02").build(); | ||
JsonObject jsonObject = new JsonObject(); | ||
jsonObject.addProperty("desc", " "); | ||
jsonObject.addProperty("input", "a+b=?"); | ||
jsonObject.add("history", new Gson().toJsonTree(Lists.newArrayList( | ||
ImmutableMap.of("role", "user", "content", "a=1"), | ||
ImmutableMap.of("role", "user", "content", "b=2") | ||
))); | ||
String res = client.callBot(M78BotReq.builder() | ||
.botId("130228") | ||
.userName("wangyingjie3") | ||
.input("hi") | ||
.build(), jsonObject, BotHttpClient.DEFAULT_FUNCTION); | ||
System.out.println(res); | ||
} | ||
|
||
@Test | ||
public void testBotHttpClientWithHistory2() { | ||
BotHttpClient client = BotHttpClient.builder() | ||
.url("http://127.0.0.1:8077/open-apis/v1/ai-plugin-new/feature/router/probot/query") | ||
.token("e1c51eaa-2f39-4cbc-8ec0-b6ab9d117a02").build(); | ||
JsonObject jsonObject = new JsonObject(); | ||
jsonObject.addProperty("desc", ""); | ||
jsonObject.addProperty("input", "a+b=?"); | ||
String res = client.callBot(M78BotReq.builder() | ||
.botId("130228") | ||
.userName("wangyingjie3") | ||
.input("hi") | ||
.build(), jsonObject, History.builder() | ||
.messages(Lists.newArrayList(Message.builder().role("user").content("a=1").build(), | ||
Message.builder().role("user").content("b=2").build())).build(), BotHttpClient.DEFAULT_FUNCTION); | ||
System.out.println(res); | ||
} | ||
|
||
|
||
@Test | ||
public void testBotHttpClientWithHistory3() { | ||
List<String> list = new ArrayList<>(); | ||
// String topic = "11.2 为什么大于 11.11"; | ||
// String topic = "陆地上最大的生物是?"; | ||
// String topic = "如何写好代码?"; | ||
// String topic = "一个博客系统有那些必要的模块"; | ||
// String topic = "人如何才能长寿"; | ||
String topic = "书店系统前后端接口如何对齐,讨论都需要哪些接口"; | ||
String input = "我们来讨论下:" + topic; | ||
int round = 5; | ||
for (int i = 0; i < round; i++) { | ||
String res = callBotWithInput("小明", input, list.stream().collect(Collectors.joining("\n")), topic, round, i + 1, "你是一名vue前端", "1.你需要列出后端给你提供的接口描述 2.你会充分采纳前端的意见 3.如果有些接口你觉得你实现更好,你可以建议给后端"); | ||
if (StringUtils.isNotEmpty(input)) { | ||
list.add("小明:" + input); | ||
input = ""; | ||
} | ||
list.add("小红:" + res); | ||
res = callBotWithInput("小红", "", list.stream().collect(Collectors.joining("\n")), topic, round, i + 1, "你是一名java后端", "1.你会列出具体的接口,比如/book/add 2.你会采纳前端提的需求,并给他生成接口 3.如果前端要求你去掉某个接口,你尽量同意 4.列出接口"); | ||
list.add("小明:" + res); | ||
} | ||
|
||
} | ||
|
||
private static String callBotWithInput(String name, String input, String history, String topic, int round, int curRound, String character_setting, String main_points) { | ||
BotHttpClient client = BotHttpClient.builder() | ||
.url("http://127.0.0.1:8077/open-apis/v1/ai-plugin-new/feature/router/probot/query") | ||
.token("e1c51eaa-2f39-4cbc-8ec0-b6ab9d117a02").build(); | ||
JsonObject jsonObject = new JsonObject(); | ||
jsonObject.addProperty("team", "小明 小红"); | ||
jsonObject.addProperty("_history", history); | ||
jsonObject.addProperty("topic", topic); | ||
jsonObject.addProperty("desc", ""); | ||
jsonObject.addProperty("name", name); | ||
jsonObject.addProperty("character_setting", character_setting); | ||
jsonObject.addProperty("main_points", main_points); | ||
jsonObject.addProperty("input", input); | ||
jsonObject.addProperty("round", round); | ||
jsonObject.addProperty("cur_round", curRound); | ||
|
||
String res = client.callBot(M78BotReq.builder() | ||
.token("61d8661d-0663-48b4-8da3-40c08b0e8453") | ||
.botId("130228") | ||
.userName("wangyingjie3") | ||
.input("") | ||
.build(), jsonObject, BotHttpClient.DEFAULT_FUNCTION); | ||
return res; | ||
} | ||
|
||
|
||
@Test | ||
public void testBotHttpClient2() { | ||
BotHttpClient client = BotHttpClient.builder().url("http://127.0.0.1:8077/open-apis/v1/ai-plugin-new/feature/router/probot/query").token("6804ec8c-e348-4f36-a881-dd4e8741a3ab").build(); | ||
JsonObject jsonObject = new JsonObject(); | ||
jsonObject.addProperty("agent", "1.教师 2.日语翻译 3.程序员 4.客服 5.测试 "); | ||
jsonObject.addProperty("input", "有个文档要翻译成日语"); | ||
String res = client.callBot(M78BotReq.builder() | ||
.botId("160222") | ||
.userName("zhangzhiyong1") | ||
.input("") | ||
.build(), jsonObject); | ||
System.out.println(res); | ||
} | ||
|
||
@Test | ||
public void testBotJsonFix() { | ||
BotHttpClient client = BotHttpClient.builder().url("http://10.38.204.190:8076/open-apis/v1/ai-plugin-new/feature/router/probot/query").token("").build(); | ||
JsonObject jsonObject = new JsonObject(); | ||
jsonObject.addProperty("json", "[\"name\":\"123\"]\n"); | ||
String res = client.callBot(M78BotReq.builder() | ||
.botId("130244") | ||
.userName("zhangzhiyong1") | ||
.input("") | ||
.build(), jsonObject); | ||
System.out.println(res); | ||
} | ||
|
||
@Test | ||
public void testBotJsonReq() { | ||
BotHttpClient client = BotHttpClient.builder() | ||
.url("http://10.38.204.190:8076/open-apis/v1/ai-plugin-new/feature/router/probot/query") | ||
.token("6804ec8c-e348-4f36-a881-dd4e8741a3ab").build(); | ||
JsonObject jsonObject = new JsonObject(); | ||
jsonObject.addProperty("request", "下雨了\n" + | ||
"{\"weather\":\"\"}"); | ||
String res = client.callBot(M78BotReq.builder() | ||
.botId("160224") | ||
.userName("zhangzhiyong1") | ||
.input("") | ||
.build(), jsonObject); | ||
System.out.println(res); | ||
} | ||
|
||
@Test | ||
public void testFlowChoose() { | ||
BotHttpClient client = BotHttpClient.builder().url("http://10.38.204.190:8076/open-apis/v1/ai-plugin-new/feature/router/probot/query").token("").build(); | ||
JsonObject jsonObject = new JsonObject(); | ||
jsonObject.addProperty("flow", "2.读取代办事项\n" + | ||
"3.编写业务代码\n" + | ||
"6.制定健身计划\n"); | ||
jsonObject.addProperty("input", "帮我开发一个网站"); | ||
String res = client.callBot(M78BotReq.builder() | ||
.botId("160223") | ||
.userName("zhangzhiyong1") | ||
.input("") | ||
.build(), jsonObject); | ||
System.out.println(res); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
jcommon/ai/m78/src/test/java/run.mone.m78.client.test/BotWsClientTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package run.mone.m78.client.test; | ||
|
||
import com.google.gson.JsonObject; | ||
import org.junit.jupiter.api.Test; | ||
import run.mone.m78.client.bot.BotWsClient; | ||
|
||
import java.util.UUID; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 9/11/24 11:03 | ||
*/ | ||
public class BotWsClientTest { | ||
|
||
@Test | ||
public void testBotWsClient() throws InterruptedException { | ||
String botId = "100265"; | ||
String token = "ac6524cf-6053-4888-8464-40b3fb04dc92"; | ||
String input = "你好,1+1等于几呀?"; | ||
BotWsClient botWsClient = BotWsClient.builder() | ||
.url("ws://10.38.204.190:8076/ws/bot/abc") | ||
.token(token) | ||
.build(); | ||
botWsClient.start(System.out::println); | ||
|
||
JsonObject jsonReq = new JsonObject(); | ||
jsonReq.addProperty("botId", botId); | ||
jsonReq.addProperty("token", token); | ||
jsonReq.addProperty("input", input); | ||
jsonReq.addProperty("topicId", UUID.randomUUID().toString()); | ||
botWsClient.send(jsonReq.toString()); | ||
TimeUnit.SECONDS.sleep(20); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
jcommon/ai/m78/src/test/java/run.mone.m78.client.test/FLowHttpClientTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package run.mone.m78.client.test; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
import org.junit.jupiter.api.Test; | ||
import run.mone.m78.client.bot.BotHttpClient; | ||
import run.mone.m78.client.flow.FlowHttpClient; | ||
import run.mone.m78.client.model.M78BotReq; | ||
import run.mone.m78.client.model.M78FlowReq; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/26/24 15:04 | ||
*/ | ||
public class FLowHttpClientTest { | ||
@Test | ||
public void testBotHttpClient() { | ||
FlowHttpClient client = FlowHttpClient.builder().token("e1c51eaa-2f39-4cbc-8ec0-b6ab9d117a02").build(); | ||
String res = client.callFlow(M78FlowReq.builder() | ||
.flowId("127") | ||
.userName("wangyingjie3") | ||
.inputs(ImmutableMap.of("scale", "256")) | ||
.build(), null); | ||
System.out.println(res); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
jcommon/ai/m78/src/test/java/run.mone.m78.client.test/FlowWsClientTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package run.mone.m78.client.test; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import run.mone.m78.client.flow.FlowWsClient; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
|
||
/** | ||
* @author HawickMason@xiaomi.com | ||
* @date 8/26/24 2:18 PM | ||
*/ | ||
public class FlowWsClientTest { | ||
|
||
/** | ||
* 测试执行流程的方法,连通性测试,本地m78会报找不到agent | ||
* | ||
* @throws InterruptedException 如果线程在等待、睡眠或占用时被中断 | ||
*/ | ||
@Test | ||
public void testExecuteFlow() throws InterruptedException { | ||
FlowWsClient flowWsClient = FlowWsClient.builder().build(); | ||
Map<String, Object> inputs = new HashMap<>(); | ||
inputs.put("scale", "256"); | ||
String userName = "wangyingjie3"; | ||
assertDoesNotThrow(() -> { | ||
flowWsClient.start(System.out::println, userName); | ||
flowWsClient.executeFlow(userName, "127", inputs); | ||
}); | ||
TimeUnit.SECONDS.sleep(20); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
jcommon/hive/src/main/java/run/mone/hive/actions/ActionFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package run.mone.hive.actions; | ||
|
||
public class ActionFactory { | ||
|
||
public static Action createAction(String actionType) { | ||
return switch (actionType.toLowerCase()) { | ||
case "writecode" -> new WriteCode(); | ||
case "writeprd" -> new WritePRD(); | ||
case "writetest" -> new WriteTest(); | ||
case "writedesign" -> new WriteDesign(); | ||
case "debugerror" -> new DebugError(); | ||
case "runcode" -> new RunCode(); | ||
case "summarizecode" -> new SummarizeCode(); | ||
default -> throw new IllegalArgumentException("Unknown action type: " + actionType); | ||
}; | ||
} | ||
|
||
public static boolean isValidActionType(String actionType) { | ||
try { | ||
createAction(actionType); | ||
return true; | ||
} catch (IllegalArgumentException e) { | ||
return false; | ||
} | ||
} | ||
} |
121 changes: 121 additions & 0 deletions
121
jcommon/hive/src/main/java/run/mone/hive/planner/OptimalTeamStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
|
||
package run.mone.hive.planner; | ||
|
||
import run.mone.hive.Team; | ||
import run.mone.hive.actions.ActionFactory; | ||
import run.mone.hive.context.Context; | ||
import run.mone.hive.llm.LLM; | ||
import run.mone.hive.roles.Role; | ||
import run.mone.hive.roles.RoleFactory; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
public class OptimalTeamStrategy implements PlanningStrategy { | ||
|
||
private final Context context; | ||
private final LLM llm; | ||
private final Map<String, List<String>> roleToActionsMap; | ||
private TeamBuilder teamBuilder; | ||
|
||
public OptimalTeamStrategy(TeamBuilder teamBuilder, Context context, LLM llm) { | ||
this.teamBuilder = teamBuilder; | ||
this.context = context; | ||
this.llm = llm; | ||
this.roleToActionsMap = new HashMap<>(); | ||
initializeRoleToActionsMap(); | ||
} | ||
|
||
@Override | ||
public Team planTeam(String requirement) { | ||
return this.buildTeam(requirement); | ||
} | ||
|
||
private void initializeRoleToActionsMap() { | ||
// 为每种角色预设可能需要的actions | ||
roleToActionsMap.put("engineer", Arrays.asList( | ||
"writecode", "writetest", "debugerror", "runcode" | ||
)); | ||
|
||
roleToActionsMap.put("productmanager", Arrays.asList( | ||
"writeprd", "writereview" | ||
)); | ||
|
||
roleToActionsMap.put("architect", Arrays.asList( | ||
"writedesign", "reviewcode" | ||
)); | ||
|
||
roleToActionsMap.put("qaengineer", Arrays.asList( | ||
"writetest", "runcode", "debugerror" | ||
)); | ||
} | ||
|
||
public Team buildTeam(String requirement) { | ||
// 创建新的team实例 | ||
Team team = new Team(context); | ||
|
||
// 分析需求,确定需要的角色 | ||
Set<String> requiredRoles = analyzeRequirement(requirement); | ||
|
||
// 为每个需要的角色创建实例 | ||
for (String roleType : requiredRoles) { | ||
Role role = RoleFactory.createRole(roleType, llm); | ||
|
||
// 为角色分配合适的actions | ||
List<String> actionTypes = roleToActionsMap.get(roleType.toLowerCase()); | ||
if (actionTypes != null) { | ||
for (String actionType : actionTypes) { | ||
if (ActionFactory.isValidActionType(actionType)) { | ||
role.getActions().add(ActionFactory.createAction(actionType)); | ||
} | ||
} | ||
} | ||
|
||
// 将角色添加到team中 | ||
team.hire(Collections.singletonList(role)); | ||
} | ||
|
||
// 记录team | ||
teamBuilder.rememberTeam(requirement, team); | ||
|
||
return team; | ||
} | ||
|
||
private Set<String> analyzeRequirement(String requirement) { | ||
// TODO:这里可以使用LLM来分析需求,确定需要哪些角色 | ||
// 简单起见,这里使用一些关键词匹配 | ||
Set<String> roles = new HashSet<>(); | ||
|
||
requirement = requirement.toLowerCase(); | ||
|
||
if (requirement.contains("code") || requirement.contains("develop") || | ||
requirement.contains("implement")) { | ||
roles.add("engineer"); | ||
} | ||
|
||
if (requirement.contains("design") || requirement.contains("architect")) { | ||
roles.add("architect"); | ||
} | ||
|
||
if (requirement.contains("test") || requirement.contains("quality")) { | ||
roles.add("qaengineer"); | ||
} | ||
|
||
if (requirement.contains("product") || requirement.contains("requirement")) { | ||
roles.add("productmanager"); | ||
} | ||
|
||
// 确保至少有一个角色 | ||
if (roles.isEmpty()) { | ||
roles.add("engineer"); // 默认添加工程师角色 | ||
} | ||
|
||
return roles; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
jcommon/hive/src/main/java/run/mone/hive/planner/PlanningStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
|
||
package run.mone.hive.planner; | ||
|
||
import run.mone.hive.Team; | ||
|
||
public interface PlanningStrategy { | ||
Team planTeam(String requirement); | ||
} |
37 changes: 37 additions & 0 deletions
37
jcommon/hive/src/main/java/run/mone/hive/planner/TeamBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package run.mone.hive.planner; | ||
|
||
import run.mone.hive.Team; | ||
import run.mone.hive.actions.ActionFactory; | ||
import run.mone.hive.roles.Role; | ||
import run.mone.hive.roles.RoleFactory; | ||
import run.mone.hive.context.Context; | ||
import run.mone.hive.llm.LLM; | ||
|
||
import java.util.*; | ||
|
||
public class TeamBuilder { | ||
|
||
private PlanningStrategy planningStrategy; // 可以扩展为不同的策略 | ||
private Map<String, Team> teams; | ||
|
||
public TeamBuilder(Context context, LLM llm) { | ||
this.planningStrategy = new OptimalTeamStrategy(this, context, llm); | ||
this.teams = new HashMap<>(); | ||
} | ||
|
||
public void rememberTeam(String task, Team team) { | ||
teams.put(task, team); | ||
} | ||
|
||
public void setPlanningStrategy(PlanningStrategy planningStrategy) { | ||
this.planningStrategy = planningStrategy; | ||
} | ||
|
||
public Team planTeam(String requirement) { | ||
if (teams.containsKey(requirement)) { | ||
return teams.get(requirement); | ||
} | ||
return planningStrategy.planTeam(requirement); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
jcommon/hive/src/main/java/run/mone/hive/roles/RoleFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package run.mone.hive.roles; | ||
|
||
import run.mone.hive.roles.*; | ||
import run.mone.hive.llm.LLM; | ||
|
||
public class RoleFactory { | ||
|
||
public static Role createRole(String roleType, LLM llm) { | ||
return switch (roleType.toLowerCase()) { | ||
case "engineer" -> new Engineer(); | ||
// case "productmanager" -> new ProductManager(); | ||
case "architect" -> new Architect(); | ||
// case "qaengineer" -> new QaEngineer(); | ||
case "writer" -> new Writer("Writer"); | ||
case "teacher" -> new Teacher(null); | ||
default -> throw new IllegalArgumentException("Unknown role type: " + roleType); | ||
}; | ||
} | ||
|
||
public static boolean isValidRoleType(String roleType) { | ||
try { | ||
createRole(roleType, null); | ||
return true; | ||
} catch (IllegalArgumentException e) { | ||
return false; | ||
} | ||
} | ||
} |
242 changes: 242 additions & 0 deletions
242
jcommon/hive/src/main/java/run/mone/hive/tools/ToolConvertor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
package run.mone.hive.tools; | ||
|
||
import com.github.javaparser.JavaParser; | ||
import com.github.javaparser.ast.CompilationUnit; | ||
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; | ||
import com.github.javaparser.ast.body.MethodDeclaration; | ||
import lombok.extern.slf4j.Slf4j; | ||
import com.github.javaparser.StaticJavaParser; | ||
import com.github.javaparser.javadoc.Javadoc; | ||
|
||
import java.lang.reflect.Method; | ||
import java.lang.reflect.Parameter; | ||
import java.util.*; | ||
|
||
@Slf4j | ||
public class ToolConvertor { | ||
|
||
public static Map<String, Object> convertCodeToToolSchema(Object obj, List<String> include) { | ||
if (obj == null) { | ||
return new HashMap<>(); | ||
} | ||
|
||
Class<?> clazz = obj instanceof Class ? (Class<?>) obj : obj.getClass(); | ||
String docString = getClassJavadoc(clazz); | ||
|
||
Map<String, Object> schema = new HashMap<>(); | ||
|
||
if (obj instanceof Class) { | ||
schema.put("type", "class"); | ||
schema.put("description", cleanDocString(docString)); | ||
|
||
Map<String, Object> methods = new HashMap<>(); | ||
for (Method method : clazz.getDeclaredMethods()) { | ||
if (shouldSkipMethod(method, include)) { | ||
continue; | ||
} | ||
String methodDoc = getMethodJavadoc(method); | ||
if (methodDoc != null) { | ||
methods.put(method.getName(), functionDocstringToSchema(method, methodDoc)); | ||
} | ||
} | ||
schema.put("methods", methods); | ||
} else { | ||
schema = functionDocstringToSchema(obj.getClass().getDeclaredMethods()[0], docString); | ||
} | ||
|
||
return schema; | ||
} | ||
|
||
public static Map<String, Map<String, Object>> convertCodeToToolSchemaAst(String code) { | ||
Map<String, Map<String, Object>> schemas = new HashMap<>(); | ||
|
||
try { | ||
JavaParser parser = new JavaParser(); | ||
CompilationUnit cu = parser.parse(code).getResult().orElse(null); | ||
if (cu == null) { | ||
return schemas; | ||
} | ||
|
||
CodeVisitor visitor = new CodeVisitor(code); | ||
visitor.visit(cu, null); | ||
schemas = visitor.getToolSchemas(); | ||
|
||
} catch (Exception e) { | ||
log.error("Failed to parse code: {}", e.getMessage()); | ||
} | ||
|
||
return schemas; | ||
} | ||
|
||
private static Map<String, Object> functionDocstringToSchema(Method method, String docString) { | ||
Map<String, Object> schema = new HashMap<>(); | ||
|
||
schema.put("type", method.isSynthetic() ? "async_function" : "function"); | ||
|
||
Javadoc javadoc = StaticJavaParser.parseJavadoc(docString); | ||
|
||
schema.put("description", javadoc.getDescription().toText().trim()); | ||
schema.put("signature", getFunctionSignature(method)); | ||
|
||
Map<String, String> params = new HashMap<>(); | ||
javadoc.getBlockTags().forEach(tag -> { | ||
if (tag.getTagName().equals("param")) { | ||
String[] parts = tag.getContent().toText().split("\\s+", 2); | ||
if (parts.length == 2) { | ||
params.put(parts[0], parts[1].trim()); | ||
} | ||
} | ||
}); | ||
schema.put("parameters", params); | ||
|
||
return schema; | ||
} | ||
|
||
private static String getFunctionSignature(Method method) { | ||
StringBuilder signature = new StringBuilder(); | ||
signature.append("("); | ||
|
||
Parameter[] parameters = method.getParameters(); | ||
for (int i = 0; i < parameters.length; i++) { | ||
Parameter param = parameters[i]; | ||
signature.append(param.getType().getSimpleName()) | ||
.append(" ") | ||
.append(param.getName()); | ||
if (i < parameters.length - 1) { | ||
signature.append(", "); | ||
} | ||
} | ||
|
||
signature.append(") -> ") | ||
.append(method.getReturnType().getSimpleName()); | ||
|
||
return signature.toString(); | ||
} | ||
|
||
private static boolean shouldSkipMethod(Method method, List<String> include) { | ||
String name = method.getName(); | ||
return (name.startsWith("_") && !name.equals("__init__")) || | ||
(include != null && !include.contains(name)); | ||
} | ||
|
||
private static String getClassJavadoc(Class<?> clazz) { | ||
try { | ||
return clazz.getAnnotation(Documentation.class).value(); | ||
} catch (Exception e) { | ||
return ""; | ||
} | ||
} | ||
|
||
private static String getMethodJavadoc(Method method) { | ||
try { | ||
return method.getAnnotation(Documentation.class).value(); | ||
} catch (Exception e) { | ||
return null; | ||
} | ||
} | ||
|
||
private static String cleanDocString(String docString) { | ||
return docString.replaceAll("\\s+", " ").trim(); | ||
} | ||
} | ||
|
||
@Slf4j | ||
class CodeVisitor { | ||
private final String sourceCode; | ||
private final Map<String, Map<String, Object>> toolSchemas = new HashMap<>(); | ||
|
||
public CodeVisitor(String sourceCode) { | ||
this.sourceCode = sourceCode; | ||
} | ||
|
||
public void visit(CompilationUnit cu, Void arg) { | ||
cu.findAll(ClassOrInterfaceDeclaration.class).forEach(this::visitClass); | ||
cu.findAll(MethodDeclaration.class).forEach(this::visitMethod); | ||
} | ||
|
||
private void visitClass(ClassOrInterfaceDeclaration n) { | ||
Map<String, Object> classSchemas = new HashMap<>(); | ||
classSchemas.put("type", "class"); | ||
classSchemas.put("description", cleanJavadoc(n.getJavadoc().orElse(null))); | ||
|
||
Map<String, Object> methods = new HashMap<>(); | ||
n.getMethods().forEach(method -> { | ||
if (!shouldSkipMethod(method)) { | ||
methods.put(method.getNameAsString(), getMethodSchema(method)); | ||
} | ||
}); | ||
|
||
classSchemas.put("methods", methods); | ||
classSchemas.put("code", n.toString()); | ||
toolSchemas.put(n.getNameAsString(), classSchemas); | ||
} | ||
|
||
private void visitMethod(MethodDeclaration n) { | ||
if (shouldSkipMethod(n)) { | ||
return; | ||
} | ||
|
||
Map<String, Object> methodSchema = getMethodSchema(n); | ||
methodSchema.put("code", n.toString()); | ||
toolSchemas.put(n.getNameAsString(), methodSchema); | ||
} | ||
|
||
private Map<String, Object> getMethodSchema(MethodDeclaration method) { | ||
Map<String, Object> schema = new HashMap<>(); | ||
|
||
schema.put("type", method.isDefault() ? "async_function" : "function"); | ||
schema.put("description", cleanJavadoc(method.getJavadoc().orElse(null))); | ||
schema.put("signature", getMethodSignature(method)); | ||
schema.put("parameters", parseJavadocParameters(method.getJavadoc().orElse(null))); | ||
|
||
return schema; | ||
} | ||
|
||
private String getMethodSignature(MethodDeclaration method) { | ||
return method.getDeclarationAsString(false, false, true); | ||
} | ||
|
||
private boolean shouldSkipMethod(MethodDeclaration method) { | ||
String name = method.getNameAsString(); | ||
return name.startsWith("_") && !name.equals("__init__"); | ||
} | ||
|
||
private String cleanJavadoc(com.github.javaparser.javadoc.Javadoc javadoc) { | ||
if (javadoc == null) { | ||
return ""; | ||
} | ||
return javadoc.getDescription().toText().replaceAll("\\s+", " ").trim(); | ||
} | ||
|
||
private Map<String, String> parseJavadocParameters(com.github.javaparser.javadoc.Javadoc javadoc) { | ||
Map<String, String> params = new HashMap<>(); | ||
if (javadoc == null) { | ||
return params; | ||
} | ||
|
||
// Parse @param and @return tags | ||
javadoc.getBlockTags().forEach(tag -> { | ||
String tagName = tag.getTagName(); | ||
String content = tag.getContent().toText(); | ||
|
||
if ("param".equals(tagName)) { | ||
String[] parts = content.split("\\s+", 2); | ||
if (parts.length == 2) { | ||
params.put(parts[0], parts[1].trim()); | ||
} | ||
} else if ("return".equals(tagName)) { | ||
params.put("return", content.trim()); | ||
} | ||
}); | ||
|
||
return params; | ||
} | ||
|
||
public Map<String, Map<String, Object>> getToolSchemas() { | ||
return toolSchemas; | ||
} | ||
} | ||
|
||
@interface Documentation { | ||
String value(); | ||
} |
216 changes: 216 additions & 0 deletions
216
jcommon/hive/src/main/java/run/mone/hive/tools/ToolRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
package run.mone.hive.tools; | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnore; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; | ||
import lombok.Data; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.*; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
@Slf4j | ||
@Data | ||
public class ToolRegistry { | ||
private Map<String, Tool> tools = new ConcurrentHashMap<>(); | ||
private Map<String, Map<String, Tool>> toolsByTags = new ConcurrentHashMap<>(); | ||
|
||
private static final ToolRegistry INSTANCE = new ToolRegistry(); | ||
private static final String TOOL_SCHEMA_PATH = "schemas/tools/"; | ||
private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); | ||
|
||
private ToolRegistry() {} | ||
|
||
public static ToolRegistry getInstance() { | ||
return INSTANCE; | ||
} | ||
|
||
public void registerTool( | ||
String toolName, | ||
String toolPath, | ||
Map<String, Object> schemas, | ||
String schemaPath, | ||
String toolCode, | ||
List<String> tags, | ||
Object toolSourceObject, | ||
List<String> includeFunctions, | ||
boolean verbose | ||
) { | ||
if (hasTool(toolName)) { | ||
return; | ||
} | ||
|
||
schemaPath = schemaPath.isEmpty() ? | ||
TOOL_SCHEMA_PATH + toolName + ".yml" : schemaPath; | ||
|
||
if (schemas == null) { | ||
schemas = makeSchema(toolSourceObject, includeFunctions, schemaPath); | ||
} | ||
|
||
if (schemas == null || schemas.isEmpty()) { | ||
return; | ||
} | ||
|
||
schemas.put("tool_path", toolPath); | ||
|
||
try { | ||
validateSchema(schemas); | ||
} catch (Exception e) { | ||
log.warn("Schema validation failed for {}: {}", toolName, e.getMessage()); | ||
} | ||
|
||
tags = tags != null ? tags : new ArrayList<>(); | ||
Tool tool = new Tool(toolName, toolPath, schemas, toolCode, tags); | ||
tools.put(toolName, tool); | ||
|
||
for (String tag : tags) { | ||
toolsByTags.computeIfAbsent(tag, k -> new HashMap<>()) | ||
.put(toolName, tool); | ||
} | ||
|
||
if (verbose) { | ||
log.info("{} registered", toolName); | ||
log.info("Schema made at {}, can be used for checking", schemaPath); | ||
} | ||
} | ||
|
||
public boolean hasTool(String key) { | ||
return tools.containsKey(key); | ||
} | ||
|
||
public Tool getTool(String key) { | ||
return tools.get(key); | ||
} | ||
|
||
public Map<String, Tool> getToolsByTag(String tag) { | ||
return toolsByTags.getOrDefault(tag, new HashMap<>()); | ||
} | ||
|
||
public Map<String, Tool> getAllTools() { | ||
return tools; | ||
} | ||
|
||
public boolean hasToolTag(String tag) { | ||
return toolsByTags.containsKey(tag); | ||
} | ||
|
||
public List<String> getToolTags() { | ||
return new ArrayList<>(toolsByTags.keySet()); | ||
} | ||
|
||
private Map<String, Object> makeSchema(Object toolSourceObject, List<String> include, String path) { | ||
try { | ||
Files.createDirectories(Paths.get(path).getParent()); | ||
Map<String, Object> schema = ToolConvertor.convertCodeToToolSchema( | ||
toolSourceObject, include); | ||
yamlMapper.writeValue(new File(path), schema); | ||
return schema; | ||
} catch (Exception e) { | ||
log.error("Failed to make schema: {}", e.getMessage()); | ||
return new HashMap<>(); | ||
} | ||
} | ||
|
||
private void validateSchema(Map<String, Object> schema) { | ||
// TODO: Implement schema validation | ||
} | ||
|
||
@Target(ElementType.TYPE) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface RegisterTool { | ||
String[] tags() default {}; | ||
String schemaPath() default ""; | ||
} | ||
|
||
@Data | ||
public static class Tool { | ||
private String name; | ||
private String path; | ||
private Map<String, Object> schemas; | ||
private String code; | ||
private List<String> tags; | ||
|
||
public Tool(String name, String path, Map<String, Object> schemas, | ||
String code, List<String> tags) { | ||
this.name = name; | ||
this.path = path; | ||
this.schemas = schemas; | ||
this.code = code; | ||
this.tags = tags; | ||
} | ||
} | ||
|
||
public static Map<String, Tool> validateToolNames(List<String> tools) { | ||
if (tools == null) { | ||
throw new IllegalArgumentException("tools must be a list"); | ||
} | ||
|
||
Map<String, Tool> validTools = new HashMap<>(); | ||
for (String key : tools) { | ||
if (new File(key).isDirectory() || new File(key).isFile()) { | ||
validTools.putAll(registerToolsFromPath(key)); | ||
} else if (INSTANCE.hasTool(key)) { | ||
validTools.put(key, INSTANCE.getTool(key)); | ||
} else if (INSTANCE.hasToolTag(key)) { | ||
validTools.putAll(INSTANCE.getToolsByTag(key)); | ||
} else { | ||
log.warn("Invalid tool name or tool type name: {}, skipped", key); | ||
} | ||
} | ||
return validTools; | ||
} | ||
|
||
public static Map<String, Tool> registerToolsFromPath(String path) { | ||
Map<String, Tool> toolsRegistered = new HashMap<>(); | ||
File file = new File(path); | ||
|
||
if (file.isFile()) { | ||
toolsRegistered.putAll(registerToolsFromFile(path)); | ||
} else if (file.isDirectory()) { | ||
File[] files = file.listFiles(); | ||
if (files != null) { | ||
for (File f : files) { | ||
toolsRegistered.putAll(registerToolsFromFile(f.getPath())); | ||
} | ||
} | ||
} | ||
return toolsRegistered; | ||
} | ||
|
||
private static Map<String, Tool> registerToolsFromFile(String filePath) { | ||
String fileName = Paths.get(filePath).getFileName().toString(); | ||
if (!fileName.endsWith(".java") || fileName.equals("setup.java") | ||
|| fileName.startsWith("test")) { | ||
return new HashMap<>(); | ||
} | ||
|
||
Map<String, Tool> registeredTools = new HashMap<>(); | ||
try { | ||
String code = Files.readString(Path.of(filePath)); | ||
Map<String, Map<String, Object>> toolSchemas = | ||
ToolConvertor.convertCodeToToolSchemaAst(code); | ||
|
||
for (Map.Entry<String, Map<String, Object>> entry : toolSchemas.entrySet()) { | ||
String name = entry.getKey(); | ||
Map<String, Object> schemas = entry.getValue(); | ||
String toolCode = (String) schemas.remove("code"); | ||
|
||
INSTANCE.registerTool(name, filePath, schemas, "", toolCode, | ||
null, null, null, false); | ||
registeredTools.put(name, INSTANCE.getTool(name)); | ||
} | ||
} catch (IOException e) { | ||
log.error("Failed to read file: {}", filePath, e); | ||
} | ||
return registeredTools; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters