chat - 聊天模型接口方言及定制
框架已内置 OllamaChatDialect、DashscopeChatDialect、OpenaiChatDialect(默认) 三种方言(基本够用),自动支持 Ollama 提供的模型接口、Dashscope 提供的模型接口及 Openai 规范的模型接口。
也可以通过定制,实现更多的模型兼容。
1、方言接口
public interface ChatDialect extends AiModelDialect {
/**
* 匹配检测
*
* @param config 聊天配置
*/
boolean matched(ChatConfig config);
/**
* 构建请求数据
*
* @param config 聊天配置
* @param options 聊天选项
* @param messages 消息
* @param isStream 是否流式获取
*/
String buildRequestJson(ChatConfig config, ChatOptions options, List<ChatMessage> messages, boolean isStream);
/**
* 构建助理消息节点
*
* @param toolCallBuilders 工具调用构建器集合
*/
ONode buildAssistantMessageNode(Map<Integer, ToolCallBuilder> toolCallBuilders);
/**
* 分析响应数据
*
* @param config 聊天配置
* @param resp 响应体
* @param respJson 响应数据
*/
boolean parseResponseJson(ChatConfig config, ChatResponseDefault resp, String respJson);
/**
* 分析工具调用
*
* @param resp 响应体
* @param oMessage 消息节点
*/
List<AssistantMessage> parseAssistantMessage(ChatResponseDefault resp, ONode oMessage);
}
2、内置的方言适配
言方 | 配置要求 | 描述 |
---|---|---|
openai | 兼容 openai 的接口规范(默认) | |
ollama | provider=ollama | 兼容 ollama 的接口规范 |
dashscope | provider=dashscope | 兼容 dashscope (阿里云的平台百炼)的接口规范 |
配置示例:
solon.ai.chat:
demo:
apiUrl: "http://127.0.0.1:11434/api/chat" # 使用完整地址(而不是 api_base)
provider: "ollama" # 使用 ollama 服务时,需要配置 provider
model: "llama3.2"
3、OllamaChatDialect 定制参考
如果方言有组件注解,会自动注册。否则,需要手动注册:
ChatDialectManager.register(new OllamaChatDialect());
方言定制:
//@Component
public class OllamaChatDialect extends AbstractChatDialect {
private static OllamaChatDialect instance = new OllamaChatDialect();
public static OllamaChatDialect getInstance() {
return instance;
}
/**
* 匹配检测
*
* @param config 聊天配置
*/
@Override
public boolean matched(ChatConfig config) {
return "ollama".equals(config.getProvider());
}
@Override
protected void buildChatMessageNodeDo(ONode oNode, UserMessage msg) {
oNode.set("role", msg.getRole().name().toLowerCase());
if (Utils.isEmpty(msg.getMedias())) {
oNode.set("content", msg.getContent());
} else {
oNode.set("content", msg.getContent());
AiMedia demo = msg.getMedias().get(0);
if (demo instanceof Image) {
oNode.set("images", msg.getMedias().stream().map(i -> i.toDataString(false)).collect(Collectors.toList()));
} else if (demo instanceof Audio) {
oNode.set("audios", msg.getMedias().stream().map(i -> i.toDataString(false)).collect(Collectors.toList()));
} else if (demo instanceof Video) {
oNode.set("videos", msg.getMedias().stream().map(i -> i.toDataString(false)).collect(Collectors.toList()));
}
}
}
@Override
public ONode buildAssistantMessageNode(Map<Integer, ToolCallBuilder> toolCallBuilders) {
ONode oNode = new ONode();
oNode.set("role", "assistant");
oNode.set("content", "");
oNode.getOrNew("tool_calls").asArray().build(n1 -> {
for (Map.Entry<Integer, ToolCallBuilder> kv : toolCallBuilders.entrySet()) {
//有可能没有
n1.addNew().set("id", kv.getValue().idBuilder.toString())
.set("type", "function")
.getOrNew("function").build(n2 -> {
n2.set("name", kv.getValue().nameBuilder.toString());
n2.set("arguments", ONode.loadStr(kv.getValue().argumentsBuilder.toString()));
});
}
});
return oNode;
}
@Override
public boolean parseResponseJson(ChatConfig config, ChatResponseDefault resp, String json) {
//解析
ONode oResp = ONode.load(json);
if (oResp.isObject() == false) {
return false;
}
if (oResp.contains("error")) {
resp.setError(new ChatException(oResp.get("error").getString()));
} else {
resp.setModel(oResp.get("model").getString());
resp.setFinished(oResp.get("done").getBoolean());
String done_reason = oResp.get("done_reason").getString();
String createdStr = oResp.get("created_at").getString();
if (createdStr != null) {
createdStr = createdStr.substring(0, createdStr.indexOf(".") + 4);
}
Date created = DateUtil.parseTry(createdStr);
List<AssistantMessage> messageList = parseAssistantMessage(resp, oResp.get("message"));
for (AssistantMessage msg1 : messageList) {
resp.addChoice(new ChatChoice(0, created, done_reason, msg1));
}
if (resp.isFinished()) {
long promptTokens = oResp.get("prompt_eval_count").getLong();
long completionTokens = oResp.get("eval_count").getLong();
long totalTokens = promptTokens + completionTokens;
resp.setUsage(new AiUsage(promptTokens, completionTokens, totalTokens));
}
}
return true;
}
}