refactor: optimize llms

This commit is contained in:
Michael Yang 2024-03-11 13:05:35 +08:00
parent f7ab096796
commit 39dc6e8f5b
28 changed files with 654 additions and 394 deletions

View File

@ -15,26 +15,21 @@
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<okhttp.version>4.9.3</okhttp.version>
<fastjson.version>2.0.45</fastjson.version>
</properties>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-sse</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>

View File

@ -40,7 +40,7 @@ public class SimpleSplitter implements Splitter {
List<Document> texts = new ArrayList<>(textArray.length);
for (String textString : textArray) {
Document newText = new Document();
newText.setMetadataMap(text.getMetadataMap());
newText.setMetadatas(text.getMetadatas());
newText.setContent(textString);
texts.add(newText);
}

View File

@ -24,7 +24,7 @@ import com.agentsflex.prompt.SimplePrompt;
public interface Llm extends Embeddings {
default String chat(String prompt) {
MessageResponse<?> chat = chat(new SimplePrompt(prompt));
MessageResponse<AiMessage> chat = chat(new SimplePrompt(prompt));
return chat != null ? chat.getMessage().getContent() : null;
}

View File

@ -17,13 +17,15 @@ package com.agentsflex.llm.client;
import com.agentsflex.functions.Function;
import com.agentsflex.llm.ChatContext;
import com.agentsflex.llm.Llm;
import com.agentsflex.llm.MessageListener;
import com.agentsflex.llm.MessageResponse;
import com.agentsflex.llm.Llm;
import com.agentsflex.llm.response.AiMessageResponse;
import com.agentsflex.llm.response.FunctionMessageResponse;
import com.agentsflex.message.AiMessage;
import com.agentsflex.message.FunctionMessage;
import com.agentsflex.parser.AiMessageParser;
import com.agentsflex.parser.FunctionMessageParser;
import com.agentsflex.prompt.FunctionPrompt;
import com.agentsflex.prompt.HistoriesPrompt;
import com.agentsflex.prompt.Prompt;
@ -72,12 +74,12 @@ public class BaseLlmClientListener implements LlmClientListener {
@Override
public void onMessage(LlmClient client, String response) {
if (isFunctionCalling) {
FunctionMessage functionInfo = functionInfoParser.parseMessage(response);
FunctionMessage functionInfo = functionInfoParser.parse(response);
List<Function<?>> functions = ((FunctionPrompt) prompt).getFunctions();
MessageResponse<?> r = new FunctionMessageResponse(functions, functionInfo);
messageListener.onMessage(context, r);
} else {
lastAiMessage = messageParser.parseMessage(response);
lastAiMessage = messageParser.parse(response);
fullMessage.append(lastAiMessage.getContent());
lastAiMessage.setFullContent(fullMessage.toString());
MessageResponse<?> r = new AiMessageResponse(lastAiMessage);
@ -102,11 +104,11 @@ public class BaseLlmClientListener implements LlmClientListener {
}
public interface AiMessageParser {
AiMessage parseMessage(String response);
}
public interface FunctionMessageParser {
FunctionMessage parseMessage(String response);
}
// public interface AiMessageParser {
// AiMessage parseMessage(String response);
// }
//
// public interface FunctionMessageParser {
// FunctionMessage parseMessage(String response);
// }
}

View File

@ -67,12 +67,7 @@ public class HttpClient {
try {
Response response = okHttpClient.newCall(request).execute();
return response.body().string();
// if (response.isSuccessful()) {
// return response.message();
// } else {
// return response.body().string();
// }
} catch (IOException e) {
} catch (Exception e) {
LOG.error(e.toString(), e);
}
return null;

View File

@ -18,5 +18,4 @@ package com.agentsflex.parser;
import com.agentsflex.message.AiMessage;
public interface AiMessageParser extends TextParser<AiMessage> {
}

View File

@ -18,5 +18,4 @@ package com.agentsflex.parser;
import com.agentsflex.message.FunctionMessage;
public interface FunctionMessageParser extends TextParser<FunctionMessage> {
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2022-2023, Agents-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.agentsflex.parser.impl;
import com.agentsflex.message.AiMessage;
import com.agentsflex.message.MessageStatus;
import com.agentsflex.parser.AiMessageParser;
import com.agentsflex.parser.Parser;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
public class BaseAiMessageParser implements AiMessageParser {
private String contentPath;
private String indexPath;
private String statusPath;
private String totalTokensPath;
private Parser<Object, MessageStatus> statusParser;
public String getContentPath() {
return contentPath;
}
public void setContentPath(String contentPath) {
this.contentPath = contentPath;
}
public String getIndexPath() {
return indexPath;
}
public void setIndexPath(String indexPath) {
this.indexPath = indexPath;
}
public String getStatusPath() {
return statusPath;
}
public void setStatusPath(String statusPath) {
this.statusPath = statusPath;
}
public String getTotalTokensPath() {
return totalTokensPath;
}
public void setTotalTokensPath(String totalTokensPath) {
this.totalTokensPath = totalTokensPath;
}
public Parser<Object, MessageStatus> getStatusParser() {
return statusParser;
}
public void setStatusParser(Parser<Object, MessageStatus> statusParser) {
this.statusParser = statusParser;
}
@Override
public AiMessage parse(String content) {
AiMessage aiMessage = new AiMessage();
JSONObject rootJson = JSON.parseObject(content);
aiMessage.setContent((String) JSONPath.eval(rootJson, this.contentPath));
aiMessage.setIndex((Integer) JSONPath.eval(rootJson, this.indexPath));
aiMessage.setTotalTokens((Integer) JSONPath.eval(rootJson, this.totalTokensPath));
String statusString = (String) JSONPath.eval(rootJson, this.statusPath);
if (this.statusParser != null) {
aiMessage.setStatus(this.statusParser.parse(statusString));
}
return aiMessage;
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2022-2023, Agents-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.agentsflex.parser.impl;
import com.agentsflex.message.FunctionMessage;
import com.agentsflex.parser.FunctionMessageParser;
import com.agentsflex.parser.Parser;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import java.util.Map;
public class BaseFunctionMessageParser implements FunctionMessageParser {
private String functionNamePath;
private String functionArgsPath;
private Parser<String, Map<String, Object>> functionArgsParser;
public String getFunctionNamePath() {
return functionNamePath;
}
public void setFunctionNamePath(String functionNamePath) {
this.functionNamePath = functionNamePath;
}
public String getFunctionArgsPath() {
return functionArgsPath;
}
public void setFunctionArgsPath(String functionArgsPath) {
this.functionArgsPath = functionArgsPath;
}
public Parser<String, Map<String, Object>> getFunctionArgsParser() {
return functionArgsParser;
}
public void setFunctionArgsParser(Parser<String, Map<String, Object>> functionArgsParser) {
this.functionArgsParser = functionArgsParser;
}
@Override
public FunctionMessage parse(String content) {
FunctionMessage functionMessage = new FunctionMessage();
JSONObject jsonObject = JSON.parseObject(content);
String functionName = (String) JSONPath.eval(jsonObject, this.functionNamePath);
functionMessage.setFunctionName(functionName);
String functionArgsString = (String) JSONPath.eval(jsonObject, this.functionArgsPath);
if (functionArgsString != null) {
functionMessage.setArgs(this.functionArgsParser.parse(functionArgsString));
}
return functionMessage;
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2022-2023, Agents-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.agentsflex.prompt;
import com.agentsflex.functions.Function;
import com.agentsflex.functions.Parameter;
import com.agentsflex.message.AiMessage;
import com.agentsflex.message.HumanMessage;
import com.agentsflex.message.Message;
import com.agentsflex.message.SystemMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DefaultPromptFormat implements PromptFormat {
@Override
public Object toMessagesJsonKey(Prompt<?> prompt) {
if (prompt == null) {
return null;
}
List<Message> messages = prompt.toMessages();
if (messages == null || messages.isEmpty()) {
return null;
}
List<Map<String, String>> messageArray = new ArrayList<>(messages.size());
messages.forEach(message -> {
Map<String, String> map = new HashMap<>(2);
if (message instanceof HumanMessage) {
map.put("role", "user");
map.put("content", ((HumanMessage) message).getContent());
} else if (message instanceof AiMessage) {
map.put("role", "assistant");
map.put("content", ((AiMessage) message).getFullContent());
} else if (message instanceof SystemMessage) {
map.put("role", "system");
map.put("content", ((SystemMessage) message).getContent());
}
messageArray.add(map);
});
return messageArray;
}
@Override
public Object toFunctionsJsonKey(Prompt<?> prompt) {
if (!(prompt instanceof FunctionPrompt)) {
return null;
}
List<Function<?>> functions = ((FunctionPrompt)prompt).getFunctions();
if (functions == null || functions.isEmpty()) {
return null;
}
List<Map<String, Object>> functionsJsonArray = new ArrayList<>();
for (Function<?> function : functions) {
Map<String, Object> functionRoot = new HashMap<>();
functionRoot.put("type", "function");
Map<String, Object> functionObj = new HashMap<>();
functionRoot.put("function", functionObj);
functionObj.put("name", function.getName());
functionObj.put("description", function.getDescription());
Map<String, Object> parametersObj = new HashMap<>();
functionObj.put("parameters", parametersObj);
parametersObj.put("type", "object");
Map<String, Object> propertiesObj = new HashMap<>();
parametersObj.put("properties", propertiesObj);
for (Parameter parameter : function.getParameters()) {
Map<String, Object> parameterObj = new HashMap<>();
parameterObj.put("type", parameter.getType());
parameterObj.put("description", parameter.getDescription());
parameterObj.put("enum", parameter.getEnums());
propertiesObj.put(parameter.getName(), parameterObj);
}
functionsJsonArray.add(functionRoot);
}
return functionsJsonArray;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2022-2023, Agents-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.agentsflex.prompt;
public interface PromptFormat {
Object toMessagesJsonKey(Prompt<?> prompt);
Object toFunctionsJsonKey(Prompt<?> prompt);
}

View File

@ -26,7 +26,7 @@ public class VectorDocument extends VectorData{
public VectorDocument(VectorData vectorData) {
this.setVector(vectorData.getVector());
this.setMetadataMap(vectorData.getMetadataMap());
this.setMetadatas(vectorData.getMetadatas());
}
public String getId() {

View File

@ -1,35 +0,0 @@
package com.agentsflex.util;
import java.util.HashMap;
import java.util.Map;
public class MapUtil {
public static Map<String, Object> ofSingleKey(String key, Object value) {
Map<String, Object> map = new HashMap<>();
map.put(key, value);
return map;
}
public static MapBuilder of() {
return new MapBuilder();
}
public static MapBuilder of(String key, Object value) {
return new MapBuilder().put(key, value);
}
public static class MapBuilder {
private Map<String, Object> map = new HashMap<>();
public MapBuilder put(String key, Object value) {
map.put(key, value);
return this;
}
public Map<String, Object> build() {
return map;
}
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2022-2023, Agents-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.agentsflex.util;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class Maps {
public static Builder of() {
return new Builder();
}
public static Builder of(String key, Object value) {
return new Builder().put(key, value);
}
public static Builder ofNotNull(String key, Object value) {
return new Builder().putIfNotNull(key, value);
}
public static Builder ofNotEmpty(String key, Object value) {
return new Builder().putIfNotEmpty(key, value);
}
public static Builder ofNotEmpty(String key, Builder value) {
return new Builder().putIfNotEmpty(key, value);
}
public static class Builder {
private Map<String, Object> map = new HashMap<>();
public Builder put(String key, Object value) {
map.put(key, value);
return this;
}
public Builder put(String key, Builder value) {
map.put(key, value.build());
return this;
}
public Builder putIf(boolean condition, String key, Builder value) {
if (condition) put(key, value);
return this;
}
public Builder putIf(boolean condition, String key, Object value) {
if (condition) put(key, value);
return this;
}
public Builder putIfNotNull(String key, Object value) {
if (value != null) put(key, value);
return this;
}
public Builder putIfNotEmpty(String key, Builder value) {
Map<String, Object> map = value.build();
if (map != null && !map.isEmpty()) put(key, value);
return this;
}
public Builder putIfNotEmpty(String key, Object value) {
if (value == null) {
return this;
}
if (value instanceof Collection && ((Collection<?>) value).isEmpty()) {
return this;
}
if (value instanceof Map && ((Map<?, ?>) value).isEmpty()) {
return this;
}
if (value.getClass().isArray() && Array.getLength(value) == 0) {
return this;
}
if (value instanceof String && ((String) value).isEmpty()) {
return this;
}
put(key, value);
return this;
}
public Builder putIfContainsKey(String checkKey, String key, Object value) {
if (map.containsKey(checkKey)) {
this.put(key, value);
}
return this;
}
public Builder putIfContainsKey(String checkKey, String key, Builder value) {
if (map.containsKey(checkKey)) {
this.put(key, value);
}
return this;
}
public Builder putIfNotContainsKey(String checkKey, String key, Object value) {
if (!map.containsKey(checkKey)) {
this.put(key, value);
}
return this;
}
public Builder putIfNotContainsKey(String checkKey, String key, Builder value) {
if (!map.containsKey(checkKey)) {
this.put(key, value);
}
return this;
}
public Map<String, Object> build() {
return map;
}
public Object get(String key) {
return map.get(key);
}
public Map getAsMap(String key) {
return (Map) map.get(key);
}
}
}

View File

@ -21,34 +21,41 @@ import java.util.Map;
public class Metadata implements Serializable {
protected Map<String, Object> metadataMap;
protected Map<String, Object> metadatas;
public Object getMetadata(String key) {
return metadataMap != null ? metadataMap.get(key) : null;
return metadatas != null ? metadatas.get(key) : null;
}
public void addMetadata(String key, Object value) {
if (metadataMap == null) {
metadataMap = new HashMap<>();
if (metadatas == null) {
metadatas = new HashMap<>();
}
metadataMap.put(key, value);
metadatas.put(key, value);
}
public void addMetadata(Map<String, Object> metadata) {
if (metadata == null || metadata.isEmpty()) {
return;
}
if (metadataMap == null) {
metadataMap = new HashMap<>();
if (metadatas == null) {
metadatas = new HashMap<>();
}
metadataMap.putAll(metadata);
metadatas.putAll(metadata);
}
public Map<String, Object> getMetadataMap() {
return metadataMap;
public Object removeMetadata(String key) {
if (this.metadatas == null) {
return null;
}
return this.metadatas.remove(key);
}
public void setMetadataMap(Map<String, Object> metadataMap) {
this.metadataMap = metadataMap;
public Map<String, Object> getMetadatas() {
return metadatas;
}
public void setMetadatas(Map<String, Object> metadatas) {
this.metadatas = metadatas;
}
}

View File

@ -15,40 +15,47 @@
*/
package com.agentsflex.llm.openai;
import com.agentsflex.functions.Function;
import com.agentsflex.functions.Parameter;
import com.agentsflex.message.AiMessage;
import com.agentsflex.message.HumanMessage;
import com.agentsflex.message.Message;
import com.agentsflex.message.MessageStatus;
import com.agentsflex.prompt.Prompt;
import com.agentsflex.document.Document;
import com.agentsflex.message.MessageStatus;
import com.agentsflex.parser.AiMessageParser;
import com.agentsflex.parser.FunctionMessageParser;
import com.agentsflex.parser.impl.BaseAiMessageParser;
import com.agentsflex.parser.impl.BaseFunctionMessageParser;
import com.agentsflex.prompt.DefaultPromptFormat;
import com.agentsflex.prompt.Prompt;
import com.agentsflex.prompt.PromptFormat;
import com.agentsflex.util.Maps;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import java.util.*;
public class OpenAiLLmUtil {
public static AiMessage parseAiMessage(String json) {
AiMessage aiMessage = new AiMessage();
JSONObject jsonObject = JSON.parseObject(json);
Object status = JSONPath.eval(jsonObject, "$.choices[0].finish_reason");
MessageStatus messageStatus = parseMessageStatus((String) status);
aiMessage.setStatus(messageStatus);
aiMessage.setIndex((Integer) JSONPath.eval(jsonObject, "$.choices[0].index"));
aiMessage.setContent((String) JSONPath.eval(jsonObject, "$.choices[0].delta.content"));
return aiMessage;
private static final PromptFormat promptFormat = new DefaultPromptFormat();
public static AiMessageParser getAiMessageParser() {
BaseAiMessageParser aiMessageParser = new BaseAiMessageParser();
aiMessageParser.setContentPath("$.choices[0].delta.content");
aiMessageParser.setIndexPath("$.choices[0].index");
aiMessageParser.setStatusPath("$.choices[0].finish_reason");
aiMessageParser.setStatusParser(content -> parseMessageStatus((String) content));
return aiMessageParser;
}
public static FunctionMessageParser getFunctionMessageParser() {
BaseFunctionMessageParser functionMessageParser = new BaseFunctionMessageParser();
functionMessageParser.setFunctionNamePath("$.choices[0].message.tool_calls[0].function.name");
functionMessageParser.setFunctionArgsPath("$.choices[0].message.tool_calls[0].function.arguments");
functionMessageParser.setFunctionArgsParser(JSON::parseObject);
return functionMessageParser;
}
public static MessageStatus parseMessageStatus(String status) {
return "stop".equals(status) ? MessageStatus.END : MessageStatus.MIDDLE;
}
public static String promptToEmbeddingsPayload(Document text) {
// https://platform.openai.com/docs/api-reference/making-requests
String payload = "{\n" +
" \"input\": \"" + text.getContent() + "\",\n" +
@ -61,96 +68,13 @@ public class OpenAiLLmUtil {
public static String promptToPayload(Prompt prompt, OpenAiLlmConfig config) {
Maps.Builder builder = Maps.of("model", config.getModel())
.put("messages", promptFormat.toMessagesJsonKey(prompt))
.putIfNotEmpty("tools", promptFormat.toFunctionsJsonKey(prompt))
.putIfContainsKey("tools", "tool_choice", "auto")
.putIfNotContainsKey("tools", "temperature", 0.7);
List<Message> messages = prompt.toMessages();
List<Map<String, String>> messageArray = new ArrayList<>();
messages.forEach(message -> {
Map<String, String> map = new HashMap<>(2);
if (message instanceof HumanMessage) {
map.put("role", "user");
map.put("content", ((HumanMessage) message).getContent());
} else if (message instanceof AiMessage) {
map.put("role", "assistant");
map.put("content", ((AiMessage) message).getFullContent());
}
messageArray.add(map);
});
String messageText = JSON.toJSONString(messageArray);
// https://platform.openai.com/docs/api-reference/making-requests
return "{\n" +
// " \"model\": \"gpt-3.5-turbo\",\n" +
" \"model\": \"" + config.getModel() + "\",\n" +
" \"messages\": " + messageText + ",\n" +
" \"temperature\": 0.7\n" +
"}";
}
public static <R> String promptToFunctionCallingPayload(Prompt prompt, OpenAiLlmConfig config, List<Function<R>> functions) {
List<Message> messages = prompt.toMessages();
List<Map<String, String>> messageArray = new ArrayList<>();
messages.forEach(message -> {
Map<String, String> map = new HashMap<>(2);
if (message instanceof HumanMessage) {
map.put("role", "user");
map.put("content", ((HumanMessage) message).getContent());
} else if (message instanceof AiMessage) {
map.put("role", "assistant");
map.put("content", ((AiMessage) message).getFullContent());
}
messageArray.add(map);
});
String messageText = JSON.toJSONString(messageArray);
List<Map<String, Object>> toolsArray = new ArrayList<>();
for (Function<?> function : functions) {
Map<String, Object> functionRoot = new HashMap<>();
functionRoot.put("type", "function");
Map<String, Object> functionObj = new HashMap<>();
functionRoot.put("function", functionObj);
functionObj.put("name", function.getName());
functionObj.put("description", function.getDescription());
Map<String, Object> parametersObj = new HashMap<>();
functionObj.put("parameters", parametersObj);
parametersObj.put("type", "object");
Map<String, Object> propertiesObj = new HashMap<>();
parametersObj.put("properties", propertiesObj);
for (Parameter parameter : function.getParameters()) {
Map<String, Object> parameterObj = new HashMap<>();
parameterObj.put("type", parameter.getType());
parameterObj.put("description", parameter.getDescription());
parameterObj.put("enum", parameter.getEnums());
propertiesObj.put(parameter.getName(), parameterObj);
}
toolsArray.add(functionRoot);
}
String toolsText = JSON.toJSONString(toolsArray);
// https://platform.openai.com/docs/api-reference/making-requests
return "{\n" +
// " \"model\": \"gpt-3.5-turbo\",\n" +
" \"model\": \"" + config.getModel() + "\",\n" +
" \"messages\": " + messageText + ",\n" +
" \"tools\": " + toolsText + ",\n" +
" \"tool_choice\": \"auto\"\n" +
"}";
return JSON.toJSONString(builder.build());
}

View File

@ -16,7 +16,6 @@
package com.agentsflex.llm.openai;
import com.agentsflex.document.Document;
import com.agentsflex.functions.Function;
import com.agentsflex.llm.BaseLlm;
import com.agentsflex.llm.MessageListener;
import com.agentsflex.llm.MessageResponse;
@ -27,24 +26,24 @@ import com.agentsflex.llm.client.LlmClientListener;
import com.agentsflex.llm.client.impl.SseClient;
import com.agentsflex.llm.response.AiMessageResponse;
import com.agentsflex.llm.response.FunctionMessageResponse;
import com.agentsflex.message.AiMessage;
import com.agentsflex.message.FunctionMessage;
import com.agentsflex.message.Message;
import com.agentsflex.parser.AiMessageParser;
import com.agentsflex.parser.FunctionMessageParser;
import com.agentsflex.prompt.FunctionPrompt;
import com.agentsflex.prompt.Prompt;
import com.agentsflex.util.StringUtil;
import com.agentsflex.store.VectorData;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.agentsflex.util.StringUtil;
import com.alibaba.fastjson.JSONPath;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class OpenAiLlm extends BaseLlm<OpenAiLlmConfig> {
private final HttpClient httpClient = new HttpClient();
public AiMessageParser aiMessageParser = OpenAiLLmUtil.getAiMessageParser();
public FunctionMessageParser functionMessageParser = OpenAiLLmUtil.getFunctionMessageParser();
public OpenAiLlm(OpenAiLlmConfig config) {
super(config);
@ -64,20 +63,10 @@ public class OpenAiLlm extends BaseLlm<OpenAiLlmConfig> {
}
if (prompt instanceof FunctionPrompt) {
List<Function<?>> functions = ((FunctionPrompt) prompt).getFunctions();
JSONObject jsonObject = JSON.parseObject(responseString);
String callFunctionName = (String) JSONPath.eval(jsonObject, "$.choices[0].tool_calls[0].function.name");
String callFunctionArgsString = (String) JSONPath.eval(jsonObject, "$.choices[0].tool_calls[0].function.arguments");
JSONObject callFunctionArgs = JSON.parseObject(callFunctionArgsString);
FunctionMessage functionMessage = new FunctionMessage();
functionMessage.setFunctionName(callFunctionName);
functionMessage.setArgs(callFunctionArgs);
return (R) new FunctionMessageResponse(functions, functionMessage);
return (R) new FunctionMessageResponse(((FunctionPrompt) prompt).getFunctions()
, functionMessageParser.parse(responseString));
} else {
AiMessage aiMessage = OpenAiLLmUtil.parseAiMessage(responseString);
return (R) new AiMessageResponse(aiMessage);
return (R) new AiMessageResponse(aiMessageParser.parse(responseString));
}
}
@ -90,7 +79,7 @@ public class OpenAiLlm extends BaseLlm<OpenAiLlmConfig> {
String payload = OpenAiLLmUtil.promptToPayload(prompt, config);
LlmClientListener clientListener = new BaseLlmClientListener(this, llmClient, listener, prompt, OpenAiLLmUtil::parseAiMessage, null);
LlmClientListener clientListener = new BaseLlmClientListener(this, llmClient, listener, prompt, aiMessageParser, functionMessageParser);
llmClient.start("https://api.openai.com/v1/chat/completions", headers, payload, clientListener);
}

View File

@ -25,8 +25,12 @@ import com.agentsflex.llm.client.LlmClient;
import com.agentsflex.llm.client.LlmClientListener;
import com.agentsflex.llm.client.impl.SseClient;
import com.agentsflex.llm.response.AiMessageResponse;
import com.agentsflex.llm.response.FunctionMessageResponse;
import com.agentsflex.message.AiMessage;
import com.agentsflex.message.Message;
import com.agentsflex.parser.AiMessageParser;
import com.agentsflex.parser.FunctionMessageParser;
import com.agentsflex.parser.impl.BaseAiMessageParser;
import com.agentsflex.prompt.FunctionPrompt;
import com.agentsflex.prompt.Prompt;
import com.agentsflex.store.VectorData;
@ -37,12 +41,16 @@ import java.util.Map;
public class QwenLlm extends BaseLlm<QwenLlmConfig> {
HttpClient httpClient = new HttpClient();
public AiMessageParser aiMessageParser = QwenLlmUtil.getAiMessageParser();
public FunctionMessageParser functionMessageParser = QwenLlmUtil.getFunctionMessageParser();
public QwenLlm(QwenLlmConfig config) {
super(config);
}
HttpClient httpClient = new HttpClient();
@Override
public <R extends MessageResponse<M>, M extends Message> R chat(Prompt<M> prompt) {
@ -58,13 +66,10 @@ public class QwenLlm extends BaseLlm<QwenLlmConfig> {
}
if (prompt instanceof FunctionPrompt) {
return (R) new FunctionMessageResponse(((FunctionPrompt) prompt).getFunctions(), functionMessageParser.parse(responseString));
} else {
AiMessage aiMessage = QwenLlmUtil.parseAiMessage(responseString, 0);
return (R) new AiMessageResponse(aiMessage);
return (R) new AiMessageResponse(aiMessageParser.parse(responseString));
}
return null;
}
@ -77,16 +82,18 @@ public class QwenLlm extends BaseLlm<QwenLlmConfig> {
String payload = QwenLlmUtil.promptToPayload(prompt, config);
LlmClientListener clientListener = new BaseLlmClientListener(this, llmClient, listener, prompt, new BaseLlmClientListener.AiMessageParser() {
LlmClientListener clientListener = new BaseLlmClientListener(this, llmClient, listener, prompt, new BaseAiMessageParser() {
int prevMessageLength = 0;
@Override
public AiMessage parseMessage(String response) {
AiMessage aiMessage = QwenLlmUtil.parseAiMessage(response, prevMessageLength);
prevMessageLength += aiMessage.getContent().length();
public AiMessage parse(String content) {
AiMessage aiMessage = aiMessageParser.parse(content);
String messageContent = aiMessage.getContent();
aiMessage.setContent(messageContent.substring(prevMessageLength));
prevMessageLength = messageContent.length();
return aiMessage;
}
}, null);
}, functionMessageParser);
llmClient.start("https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation", headers, payload, clientListener);
}

View File

@ -15,33 +15,37 @@
*/
package com.agentsflex.llm.qwen;
import com.agentsflex.message.AiMessage;
import com.agentsflex.message.HumanMessage;
import com.agentsflex.message.Message;
import com.agentsflex.message.MessageStatus;
import com.agentsflex.parser.AiMessageParser;
import com.agentsflex.parser.FunctionMessageParser;
import com.agentsflex.parser.impl.BaseAiMessageParser;
import com.agentsflex.parser.impl.BaseFunctionMessageParser;
import com.agentsflex.prompt.DefaultPromptFormat;
import com.agentsflex.prompt.Prompt;
import com.agentsflex.prompt.PromptFormat;
import com.agentsflex.util.Maps;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class QwenLlmUtil {
private static final PromptFormat promptFormat = new DefaultPromptFormat();
public static AiMessage parseAiMessage(String json, int length) {
AiMessage aiMessage = new AiMessage();
JSONObject jsonObject = JSON.parseObject(json);
MessageStatus messageStatus = parseMessageStatus((String) JSONPath.eval(json, "$.output.finish_reason"));
aiMessage.setStatus(messageStatus);
public static AiMessageParser getAiMessageParser() {
BaseAiMessageParser aiMessageParser = new BaseAiMessageParser();
aiMessageParser.setContentPath("$.output.text");
aiMessageParser.setStatusPath("$.output.finish_reason");
aiMessageParser.setTotalTokensPath("$.usage.total_tokens");
aiMessageParser.setStatusParser(content -> parseMessageStatus((String) content));
return aiMessageParser;
}
String text = (String) JSONPath.eval(jsonObject, "$.output.text");
aiMessage.setContent(text.substring(length));
aiMessage.setTotalTokens((Integer) JSONPath.eval(jsonObject, "$.usage.total_tokens"));
return aiMessage;
public static FunctionMessageParser getFunctionMessageParser() {
BaseFunctionMessageParser functionMessageParser = new BaseFunctionMessageParser();
functionMessageParser.setFunctionNamePath("$.choices[0].message.tool_calls[0].function.name");
functionMessageParser.setFunctionArgsPath("$.choices[0].message.tool_calls[0].function.arguments");
functionMessageParser.setFunctionArgsParser(JSON::parseObject);
return functionMessageParser;
}
@ -50,34 +54,9 @@ public class QwenLlmUtil {
}
public static String promptToPayload(Prompt prompt, QwenLlmConfig config) {
List<Message> messages = prompt.toMessages();
public static String promptToPayload(Prompt<?> prompt, QwenLlmConfig config) {
// https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.0.1ff6fa70jCgGRc#b8ebf6b25eul6
String payload = "{\n" +
" \"model\": \"" + config.getModel() + "\",\n" +
" \"input\": {\n" +
" \"messages\": messageJsonString\n" +
" }\n" +
"}";
List<Map<String, String>> messageArray = new ArrayList<>();
messages.forEach(message -> {
Map<String, String> map = new HashMap<>(2);
if (message instanceof HumanMessage) {
map.put("role", "user");
map.put("content", ((HumanMessage) message).getContent());
} else if (message instanceof AiMessage) {
map.put("role", "assistant");
map.put("content", ((AiMessage) message).getFullContent());
}
messageArray.add(map);
});
String messageText = JSON.toJSONString(messageArray);
return payload.replace("messageJsonString", messageText);
Maps.Builder root = Maps.of("model", config.getModel()).put("input", Maps.of("messages", promptFormat.toMessagesJsonKey(prompt)));
return JSON.toJSONString(root.build());
}
}

View File

@ -16,7 +16,6 @@
package com.agentsflex.llm.spark;
import com.agentsflex.document.Document;
import com.agentsflex.functions.Function;
import com.agentsflex.llm.BaseLlm;
import com.agentsflex.llm.ChatContext;
import com.agentsflex.llm.MessageListener;
@ -30,18 +29,21 @@ import com.agentsflex.llm.response.FunctionMessageResponse;
import com.agentsflex.message.AiMessage;
import com.agentsflex.message.FunctionMessage;
import com.agentsflex.message.Message;
import com.agentsflex.parser.AiMessageParser;
import com.agentsflex.parser.FunctionMessageParser;
import com.agentsflex.prompt.FunctionPrompt;
import com.agentsflex.prompt.Prompt;
import com.agentsflex.store.VectorData;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class SparkLlm extends BaseLlm<SparkLlmConfig> {
public AiMessageParser aiMessageParser = SparkLlmUtil.getAiMessageParser();
public FunctionMessageParser functionMessageParser = SparkLlmUtil.getFunctionMessageParser();
public SparkLlm(SparkLlmConfig config) {
super(config);
}
@ -51,6 +53,8 @@ public class SparkLlm extends BaseLlm<SparkLlmConfig> {
return null;
}
@SuppressWarnings("unchecked")
@Override
public <R extends MessageResponse<M>, M extends Message> R chat(Prompt<M> prompt) {
@ -91,6 +95,7 @@ public class SparkLlm extends BaseLlm<SparkLlmConfig> {
}
@Override
public <R extends MessageResponse<M>, M extends Message> void chatAsync(Prompt<M> prompt, MessageListener<R, M> listener) {
LlmClient llmClient = new WebSocketClient();
@ -98,20 +103,7 @@ public class SparkLlm extends BaseLlm<SparkLlmConfig> {
String payload = SparkLlmUtil.promptToPayload(prompt, config);
LlmClientListener clientListener = new BaseLlmClientListener(this, llmClient, listener, prompt, SparkLlmUtil::parseAiMessage, new BaseLlmClientListener.FunctionMessageParser() {
@Override
public FunctionMessage parseMessage(String response) {
JSONObject jsonObject = JSON.parseObject(response);
String callFunctionName = (String) JSONPath.eval(jsonObject, "$.payload.choices.text[0].function_call.name");
String callFunctionArgsString = (String) JSONPath.eval(jsonObject, "$.payload.choices.text[0].function_call.arguments");
JSONObject callFunctionArgs = JSON.parseObject(callFunctionArgsString);
FunctionMessage functionMessage = new FunctionMessage();
functionMessage.setFunctionName(callFunctionName);
functionMessage.setArgs(callFunctionArgs);
return functionMessage;
}
});
LlmClientListener clientListener = new BaseLlmClientListener(this, llmClient, listener, prompt, aiMessageParser, functionMessageParser);
llmClient.start(url, null, payload, clientListener);
}

View File

@ -15,119 +15,58 @@
*/
package com.agentsflex.llm.spark;
import com.agentsflex.functions.Function;
import com.agentsflex.functions.Parameter;
import com.agentsflex.message.*;
import com.agentsflex.message.MessageStatus;
import com.agentsflex.parser.AiMessageParser;
import com.agentsflex.parser.FunctionMessageParser;
import com.agentsflex.parser.impl.BaseAiMessageParser;
import com.agentsflex.parser.impl.BaseFunctionMessageParser;
import com.agentsflex.prompt.DefaultPromptFormat;
import com.agentsflex.prompt.FunctionPrompt;
import com.agentsflex.prompt.Prompt;
import com.agentsflex.prompt.PromptFormat;
import com.agentsflex.util.HashUtil;
import com.agentsflex.util.Maps;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Base64;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
public class SparkLlmUtil {
public static AiMessage parseAiMessage(String json) {
AiMessage aiMessage = new AiMessage();
JSONObject jsonObject = JSON.parseObject(json);
Object status = JSONPath.eval(jsonObject, "$.payload.choices.status");
MessageStatus messageStatus = SparkLlmUtil.parseMessageStatus((Integer) status);
aiMessage.setStatus(messageStatus);
aiMessage.setIndex((Integer) JSONPath.eval(jsonObject, "$.payload.choices.text[0].index"));
aiMessage.setContent((String) JSONPath.eval(jsonObject, "$.payload.choices.text[0].content"));
return aiMessage;
private static final PromptFormat promptFormat = new DefaultPromptFormat();
public static AiMessageParser getAiMessageParser() {
BaseAiMessageParser aiMessageParser = new BaseAiMessageParser();
aiMessageParser.setContentPath("$.payload.choices.text[0].content");
aiMessageParser.setIndexPath("$.payload.choices.text[0].index");
aiMessageParser.setStatusPath("$.payload.choices.status");
aiMessageParser.setStatusParser(content -> parseMessageStatus((Integer) content));
return aiMessageParser;
}
public static FunctionMessageParser getFunctionMessageParser() {
BaseFunctionMessageParser functionMessageParser = new BaseFunctionMessageParser();
functionMessageParser.setFunctionNamePath("$.payload.choices.text[0].function_call.name");
functionMessageParser.setFunctionArgsPath("$.payload.choices.text[0].function_call.arguments");
functionMessageParser.setFunctionArgsParser(JSON::parseObject);
return functionMessageParser;
}
public static String promptToPayload(Prompt prompt, SparkLlmConfig config) {
// https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
String payload = "{\n" +
" \"header\": {\n" +
" \"app_id\": \"" + config.getAppId() + "\",\n" +
" \"uid\": \"" + UUID.randomUUID() + "\"\n" +
" },\n" +
" \"parameter\": {\n" +
" \"chat\": {\n" +
" \"domain\": \"generalv3\",\n" +
" \"temperature\": 0.5,\n" +
" \"max_tokens\": 1024 \n" +
" }\n" +
" },\n" +
" \"payload\": {\n" +
" \"message\": {\n" +
" \"text\": messageJsonString" +
" },\n" +
" \"functions\":functionsJsonString" +
" }\n" +
"}";
List<Message> messages = prompt.toMessages();
List<Map<String, String>> messageArray = new ArrayList<>();
messages.forEach(message -> {
Map<String, String> map = new HashMap<>(2);
if (message instanceof HumanMessage) {
map.put("role", "user");
map.put("content", ((HumanMessage) message).getContent());
} else if (message instanceof AiMessage) {
map.put("role", "assistant");
map.put("content", ((AiMessage) message).getFullContent());
} else if (message instanceof SystemMessage) {
map.put("role", "system");
map.put("content", ((SystemMessage) message).getContent());
}
messageArray.add(map);
});
String functionsJsonString = "\"\"";
if (prompt instanceof FunctionPrompt) {
List<Function<?>> functions = ((FunctionPrompt) prompt).getFunctions();
List<Map<String, Object>> functionsArray = new ArrayList<>();
for (Function<?> function : functions) {
Map<String, Object> functionRoot = new HashMap<>();
functionRoot.put("type", "function");
Map<String, Object> functionObj = new HashMap<>();
functionRoot.put("function", functionObj);
functionObj.put("name", function.getName());
functionObj.put("description", function.getDescription());
Map<String, Object> parametersObj = new HashMap<>();
functionObj.put("parameters", parametersObj);
parametersObj.put("type", "object");
Map<String, Object> propertiesObj = new HashMap<>();
parametersObj.put("properties", propertiesObj);
for (Parameter parameter : function.getParameters()) {
Map<String, Object> parameterObj = new HashMap<>();
parameterObj.put("type", parameter.getType());
parameterObj.put("description", parameter.getDescription());
parameterObj.put("enum", parameter.getEnums());
propertiesObj.put(parameter.getName(), parameterObj);
}
functionsArray.add(functionRoot);
}
Map<String, Object> functionsJsonMap = new HashMap<>();
functionsJsonMap.put("text", functionsArray);
functionsJsonString = JSON.toJSONString(functionsJsonMap);
}
String messageText = JSON.toJSONString(messageArray);
return payload.replace("messageJsonString", messageText).replace("functionsJsonString", functionsJsonString);
Maps.Builder root = Maps.of("header", Maps.of("app_id", config.getAppId()).put("uid", UUID.randomUUID()));
root.put("parameter", Maps.of("chat", Maps.of("domain", "generalv3").put("temperature", 0.5).put("max_tokens", 1024)));
root.put("payload", Maps.of("message", Maps.of("text", promptFormat.toMessagesJsonKey(prompt)))
.putIfNotEmpty("functions", Maps.ofNotNull("text", promptFormat.toFunctionsJsonKey((FunctionPrompt) prompt)))
);
return JSON.toJSONString(root.build());
}
public static MessageStatus parseMessageStatus(Integer status) {
@ -139,7 +78,6 @@ public class SparkLlmUtil {
case 2:
return MessageStatus.END;
}
return MessageStatus.UNKNOW;
}

View File

@ -31,11 +31,11 @@ import java.util.*;
*/
public class AliyunVectorStore extends VectorStore<VectorDocument> {
private AliyunVectorStorageConfig config;
private AliyunVectorStoreConfig config;
private final HttpClient httpUtil = new HttpClient();
public AliyunVectorStore(AliyunVectorStorageConfig config) {
public AliyunVectorStore(AliyunVectorStoreConfig config) {
this.config = config;
}
@ -50,8 +50,8 @@ public class AliyunVectorStore extends VectorStore<VectorDocument> {
List<Map<String,Object>> payloadDocs = new ArrayList<>();
for (VectorDocument vectorDocument : documents) {
Map<String, Object> document = new HashMap<>();
if (vectorDocument.getMetadataMap() != null) {
document.put("fields", vectorDocument.getMetadataMap());
if (vectorDocument.getMetadatas() != null) {
document.put("fields", vectorDocument.getMetadatas());
}
document.put("vector", vectorDocument.getVector());
document.put("id", vectorDocument.getId());
@ -79,6 +79,7 @@ public class AliyunVectorStore extends VectorStore<VectorDocument> {
httpUtil.delete("https://" + config.getEndpoint() + "/v1/collections/" + config.getCollection() + "/docs", headers, payload);
}
@Override
public void update(List<VectorDocument> documents) {
Map<String, String> headers = new HashMap<>();
@ -90,8 +91,8 @@ public class AliyunVectorStore extends VectorStore<VectorDocument> {
List<Map<String,Object>> payloadDocs = new ArrayList<>();
for (VectorDocument vectorDocument : documents) {
Map<String, Object> document = new HashMap<>();
if (vectorDocument.getMetadataMap() != null) {
document.put("fields", vectorDocument.getMetadataMap());
if (vectorDocument.getMetadatas() != null) {
document.put("fields", vectorDocument.getMetadatas());
}
document.put("vector", vectorDocument.getVector());
document.put("id", vectorDocument.getId());

View File

@ -20,7 +20,7 @@ import java.io.Serializable;
/**
* https://help.aliyun.com/document_detail/2510317.html
*/
public class AliyunVectorStorageConfig implements Serializable {
public class AliyunVectorStoreConfig implements Serializable {
private String endpoint;
private String apiKey;
private String database;

View File

@ -31,12 +31,12 @@ import java.util.*;
*/
public class QCloudVectorStore extends VectorStore<VectorDocument> {
private QCloudVectorStorageConfig config;
private QCloudVectorStoreConfig config;
private final HttpClient httpUtil = new HttpClient();
public QCloudVectorStore(QCloudVectorStorageConfig config) {
public QCloudVectorStore(QCloudVectorStoreConfig config) {
this.config = config;
}
@ -55,8 +55,8 @@ public class QCloudVectorStore extends VectorStore<VectorDocument> {
List<Map<String, Object>> payloadDocs = new ArrayList<>();
for (VectorDocument vectorDocument : documents) {
Map<String, Object> document = new HashMap<>();
if (vectorDocument.getMetadataMap() != null) {
document.putAll(vectorDocument.getMetadataMap());
if (vectorDocument.getMetadatas() != null) {
document.putAll(vectorDocument.getMetadatas());
}
document.put("vector", vectorDocument.getVector());
document.put("id", vectorDocument.getId());
@ -103,7 +103,7 @@ public class QCloudVectorStore extends VectorStore<VectorDocument> {
Map<String, Object> documentIdsObj = new HashMap<>();
documentIdsObj.put("documentIds", Collections.singletonList(document.getId()));
payloadMap.put("query", documentIdsObj);
payloadMap.put("update", document.getMetadataMap());
payloadMap.put("update", document.getMetadatas());
String payload = JSON.toJSONString(payloadMap);
httpUtil.post(config.getHost() + "/document/update", headers, payload);
}

View File

@ -17,7 +17,7 @@ package com.agentsflex.store.qcloud;
import java.io.Serializable;
public class QCloudVectorStorageConfig implements Serializable {
public class QCloudVectorStoreConfig implements Serializable {
private String host;
private String apiKey;

View File

@ -16,5 +16,11 @@
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,7 @@
package com.agentsflex;
public class JavaMain {
public static void main(String[] args) {
}
}

19
pom.xml
View File

@ -56,11 +56,30 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>1.7.29</slf4j.version>
<junit.version>4.13.2</junit.version>
<okhttp.version>4.9.3</okhttp.version>
<fastjson.version>2.0.45</fastjson.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-sse</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>