chat - 聊天模型接口方言及定制
框架已内置 OllamaChatDialect、OpenaiChatDialect 两种方言(基本够用),自动支持 Ollama 提供的模型接口与 Openai 规范的模型接口。还可以通过定制,实现更多的模型兼容。
1、方言接口
public interface ChatDialect extends AiModelDialect {
//匹配检测
boolean matched(ChatConfig config);
//构建请求数据
String buildRequestJson(ChatConfig config, ChatOptions options, List<ChatMessage> messages, boolean stream);
//分析响应数据
boolean parseResponseJson(ChatConfig config, boolean isStream, ChatResponseDefault resp, String respJson);
}
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 boolean parseResponseJson(ChatConfig config, boolean isStream, 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(isStream, 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;
}
}