Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

minimax-chat-part #13

Merged
merged 15 commits into from
Oct 16, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
MoonshotConfigProperties.class,
HunyuanConfigProperties.class,
LingyiConfigProperties.class,
OllamaConfigProperties.class})
OllamaConfigProperties.class,
MinimaxConfigProperties.class,})

public class AiConfigAutoConfiguration {

// okhttp配置
Expand All @@ -51,10 +53,11 @@ public class AiConfigAutoConfiguration {
private final HunyuanConfigProperties hunyuanConfigProperties;
private final LingyiConfigProperties lingyiConfigProperties;
private final OllamaConfigProperties ollamaConfigProperties;
private final MinimaxConfigProperties minimaxConfigProperties;

private io.github.lnyocly.ai4j.service.Configuration configuration = new io.github.lnyocly.ai4j.service.Configuration();

public AiConfigAutoConfiguration(OkHttpConfigProperties okHttpConfigProperties, OpenAiConfigProperties openAiConfigProperties, PineconeConfigProperties pineconeConfigProperties, ZhipuConfigProperties zhipuConfigProperties, DeepSeekConfigProperties deepSeekConfigProperties, MoonshotConfigProperties moonshotConfigProperties, HunyuanConfigProperties hunyuanConfigProperties, LingyiConfigProperties lingyiConfigProperties, OllamaConfigProperties ollamaConfigProperties) {
public AiConfigAutoConfiguration(OkHttpConfigProperties okHttpConfigProperties, OpenAiConfigProperties openAiConfigProperties, PineconeConfigProperties pineconeConfigProperties, ZhipuConfigProperties zhipuConfigProperties, DeepSeekConfigProperties deepSeekConfigProperties, MoonshotConfigProperties moonshotConfigProperties, HunyuanConfigProperties hunyuanConfigProperties, LingyiConfigProperties lingyiConfigProperties, OllamaConfigProperties ollamaConfigProperties, MinimaxConfigProperties minimaxConfigProperties) {
this.okHttpConfigProperties = okHttpConfigProperties;
this.openAiConfigProperties = openAiConfigProperties;
this.pineconeConfigProperties = pineconeConfigProperties;
Expand All @@ -64,6 +67,7 @@ public AiConfigAutoConfiguration(OkHttpConfigProperties okHttpConfigProperties,
this.hunyuanConfigProperties = hunyuanConfigProperties;
this.lingyiConfigProperties = lingyiConfigProperties;
this.ollamaConfigProperties = ollamaConfigProperties;
this.minimaxConfigProperties = minimaxConfigProperties;
}

@Bean
Expand All @@ -89,8 +93,11 @@ private void init() {
initHunyuanConfig();
initLingyiConfig();
initOllamaConfig();
initMinimaxConfig();
}



private void initOkHttp() {
//configuration.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1",10809)));

Expand Down Expand Up @@ -235,5 +242,15 @@ private void initOllamaConfig() {

configuration.setOllamaConfig(ollamaConfig);
}
/**
* 初始化Minimax 配置信息
*/
private void initMinimaxConfig() {
MinimaxConfig minimaxConfig = new MinimaxConfig();
minimaxConfig.setApiHost(minimaxConfigProperties.getApiHost());
minimaxConfig.setApiKey(minimaxConfigProperties.getApiKey());
minimaxConfig.setChatCompletionUrl(minimaxConfigProperties.getChatCompletionUrl());

configuration.setMinimaxConfig(minimaxConfig);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.lnyocly.ai4j;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* @Author : isxuwl
* @Date: 2024/10/15 16:27
* @Model Description:
* @Description:
*/

@Data
@ConfigurationProperties(prefix = "ai.minimax")
public class MinimaxConfigProperties {
private String apiHost = "https://api.minimax.chat/";
private String apiKey = "";
private String chatCompletionUrl = "v1/text/chatcompletion_v2";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.github.lnyocly.ai4j.config;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* @Author : isxuwl
* @Date: 2024/10/15 16:08
* @Model Description:
* @Description:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MinimaxConfig {
private String apiHost = "https://api.minimax.chat/";
private String apiKey = "";
private String chatCompletionUrl = "v1/text/chatcompletion_v2";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
package io.github.lnyocly.ai4j.platform.minimax.chat;

import com.alibaba.fastjson2.JSON;
import io.github.lnyocly.ai4j.config.MinimaxConfig;
import io.github.lnyocly.ai4j.constant.Constants;
import io.github.lnyocly.ai4j.convert.chat.ParameterConvert;
import io.github.lnyocly.ai4j.convert.chat.ResultConvert;
import io.github.lnyocly.ai4j.listener.SseListener;
import io.github.lnyocly.ai4j.platform.minimax.chat.entity.MinimaxChatCompletion;
import io.github.lnyocly.ai4j.platform.minimax.chat.entity.MinimaxChatCompletionResponse;
import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletion;
import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletionResponse;
import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage;
import io.github.lnyocly.ai4j.platform.openai.chat.entity.Choice;
import io.github.lnyocly.ai4j.platform.openai.tool.Tool;
import io.github.lnyocly.ai4j.platform.openai.tool.ToolCall;
import io.github.lnyocly.ai4j.platform.openai.usage.Usage;
import io.github.lnyocly.ai4j.service.Configuration;
import io.github.lnyocly.ai4j.service.IChatService;
import io.github.lnyocly.ai4j.utils.ToolUtil;
import io.github.lnyocly.ai4j.utils.ValidateUtil;
import okhttp3.*;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
* @Author : isxuwl
* @Date: 2024/10/15 16:24
* @Model Description:
* @Description: Minimax
*/
public class MinimaxChatService implements IChatService, ParameterConvert<MinimaxChatCompletion>, ResultConvert<MinimaxChatCompletionResponse> {
private final MinimaxConfig minimaxConfig;
private final OkHttpClient okHttpClient;
private final EventSource.Factory factory;

public MinimaxChatService(Configuration configuration) {
this.minimaxConfig = configuration.getMinimaxConfig();
this.okHttpClient = configuration.getOkHttpClient();
this.factory = configuration.createRequestFactory();
}



@Override
public MinimaxChatCompletion convertChatCompletionObject(ChatCompletion chatCompletion) {
MinimaxChatCompletion minimaxChatCompletion = new MinimaxChatCompletion();
minimaxChatCompletion.setModel(chatCompletion.getModel());
minimaxChatCompletion.setMessages(chatCompletion.getMessages());
minimaxChatCompletion.setTools(chatCompletion.getTools());
minimaxChatCompletion.setFunctions(chatCompletion.getFunctions());
minimaxChatCompletion.setToolChoice(chatCompletion.getToolChoice());
minimaxChatCompletion.setTemperature(chatCompletion.getTemperature());
minimaxChatCompletion.setTopP(chatCompletion.getTopP());
minimaxChatCompletion.setStream(chatCompletion.getStream());
minimaxChatCompletion.setMaxTokens(chatCompletion.getMaxTokens());
return minimaxChatCompletion;
}

@Override
public EventSourceListener convertEventSource(SseListener eventSourceListener) {
return new EventSourceListener() {
@Override
public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
eventSourceListener.onOpen(eventSource, response);
}

@Override
public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
eventSourceListener.onFailure(eventSource, t, response);
}

@Override
public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type, @NotNull String data) {
if ("[DONE]".equalsIgnoreCase(data)) {
eventSourceListener.onEvent(eventSource, id, type, data);
return;
}

MinimaxChatCompletionResponse chatCompletionResponse = JSON.parseObject(data, MinimaxChatCompletionResponse.class);
ChatCompletionResponse response = convertChatCompletionResponse(chatCompletionResponse);

eventSourceListener.onEvent(eventSource, id, type, JSON.toJSONString(response));
}

@Override
public void onClosed(@NotNull EventSource eventSource) {
eventSourceListener.onClosed(eventSource);
}
};
}


@Override
public ChatCompletionResponse convertChatCompletionResponse(MinimaxChatCompletionResponse minimaxChatCompletionResponse) {
ChatCompletionResponse chatCompletionResponse = new ChatCompletionResponse();
chatCompletionResponse.setId(minimaxChatCompletionResponse.getId());
chatCompletionResponse.setObject(minimaxChatCompletionResponse.getObject());
chatCompletionResponse.setCreated(minimaxChatCompletionResponse.getCreated());
chatCompletionResponse.setModel(minimaxChatCompletionResponse.getModel());
chatCompletionResponse.setChoices(minimaxChatCompletionResponse.getChoices());
chatCompletionResponse.setUsage(minimaxChatCompletionResponse.getUsage());

return chatCompletionResponse;
}

@Override
public ChatCompletionResponse chatCompletion(String baseUrl, String apiKey, ChatCompletion chatCompletion) throws Exception {
if(baseUrl == null || "".equals(baseUrl)) baseUrl = minimaxConfig.getApiHost();
if(apiKey == null || "".equals(apiKey)) apiKey = minimaxConfig.getApiKey();
chatCompletion.setStream(false);
chatCompletion.setStreamOptions(null);

// 转换 请求参数
MinimaxChatCompletion minimaxChatCompletion = this.convertChatCompletionObject(chatCompletion);

// 如含有function,则添加tool
if(minimaxChatCompletion.getFunctions()!=null && !minimaxChatCompletion.getFunctions().isEmpty()){
List<Tool> tools = ToolUtil.getAllFunctionTools(minimaxChatCompletion.getFunctions());
minimaxChatCompletion.setTools(tools);
}

// 总token消耗
Usage allUsage = new Usage();

String finishReason = "first";

while("first".equals(finishReason) || "tool_calls".equals(finishReason)){

finishReason = null;

// 构造请求
String requestString = JSON.toJSONString(minimaxChatCompletion);

Request request = new Request.Builder()
.header("Authorization", "Bearer " + apiKey)
.url(ValidateUtil.concatUrl(baseUrl, minimaxConfig.getChatCompletionUrl()))
.post(RequestBody.create(MediaType.parse(Constants.JSON_CONTENT_TYPE), requestString))
.build();

Response execute = okHttpClient.newCall(request).execute();
if (execute.isSuccessful() && execute.body() != null){
MinimaxChatCompletionResponse minimaxChatCompletionResponse = JSON.parseObject(execute.body().string(), MinimaxChatCompletionResponse.class);

Choice choice = minimaxChatCompletionResponse.getChoices().get(0);
finishReason = choice.getFinishReason();

Usage usage = minimaxChatCompletionResponse.getUsage();
allUsage.setCompletionTokens(allUsage.getCompletionTokens() + usage.getCompletionTokens());
allUsage.setTotalTokens(allUsage.getTotalTokens() + usage.getTotalTokens());
allUsage.setPromptTokens(allUsage.getPromptTokens() + usage.getPromptTokens());

// 判断是否为函数调用返回
if("tool_calls".equals(finishReason)){
ChatMessage message = choice.getMessage();
List<ToolCall> toolCalls = message.getToolCalls();

List<ChatMessage> messages = new ArrayList<>(minimaxChatCompletion.getMessages());
messages.add(message);

// 添加 tool 消息
for (ToolCall toolCall : toolCalls) {
String functionName = toolCall.getFunction().getName();
String arguments = toolCall.getFunction().getArguments();
String functionResponse = ToolUtil.invoke(functionName, arguments);

messages.add(ChatMessage.withTool(functionResponse, toolCall.getId()));
}
minimaxChatCompletion.setMessages(messages);

}else{// 其他情况直接返回

// 设置包含tool的总token数
minimaxChatCompletionResponse.setUsage(allUsage);
//deepSeekChatCompletionResponse.setObject("chat.completion");

// 恢复原始请求数据
chatCompletion.setMessages(minimaxChatCompletion.getMessages());
chatCompletion.setTools(minimaxChatCompletion.getTools());

return this.convertChatCompletionResponse(minimaxChatCompletionResponse);

}

}

}


return null;
}

@Override
public ChatCompletionResponse chatCompletion(ChatCompletion chatCompletion) throws Exception {
return this.chatCompletion(null, null, chatCompletion);
}

@Override
public void chatCompletionStream(String baseUrl, String apiKey, ChatCompletion chatCompletion, SseListener eventSourceListener) throws Exception {
if(baseUrl == null || "".equals(baseUrl)) baseUrl = minimaxConfig.getApiHost();
if(apiKey == null || "".equals(apiKey)) apiKey = minimaxConfig.getApiKey();
chatCompletion.setStream(true);

// 转换 请求参数
MinimaxChatCompletion minimaxChatCompletion = this.convertChatCompletionObject(chatCompletion);

// 如含有function,则添加tool
if(minimaxChatCompletion.getFunctions()!=null && !minimaxChatCompletion.getFunctions().isEmpty()){
List<Tool> tools = ToolUtil.getAllFunctionTools(minimaxChatCompletion.getFunctions());
minimaxChatCompletion.setTools(tools);
}

String finishReason = "first";

while("first".equals(finishReason) || "tool_calls".equals(finishReason)){

finishReason = null;
String jsonString = JSON.toJSONString(minimaxChatCompletion);

Request request = new Request.Builder()
.header("Authorization", "Bearer " + apiKey)
.url(ValidateUtil.concatUrl(baseUrl, minimaxConfig.getChatCompletionUrl()))
.post(RequestBody.create(MediaType.parse(Constants.APPLICATION_JSON), jsonString))
.build();


factory.newEventSource(request, convertEventSource(eventSourceListener));
eventSourceListener.getCountDownLatch().await();

finishReason = eventSourceListener.getFinishReason();
List<ToolCall> toolCalls = eventSourceListener.getToolCalls();

// 需要调用函数
if("tool_calls".equals(finishReason) && !toolCalls.isEmpty()){
// 创建tool响应消息
ChatMessage responseMessage = ChatMessage.withAssistant(eventSourceListener.getToolCalls());

List<ChatMessage> messages = new ArrayList<>(minimaxChatCompletion.getMessages());
messages.add(responseMessage);

// 封装tool结果消息
for (ToolCall toolCall : toolCalls) {
String functionName = toolCall.getFunction().getName();
String arguments = toolCall.getFunction().getArguments();
String functionResponse = ToolUtil.invoke(functionName, arguments);

messages.add(ChatMessage.withTool(functionResponse, toolCall.getId()));
}
eventSourceListener.setToolCalls(new ArrayList<>());
eventSourceListener.setToolCall(null);
minimaxChatCompletion.setMessages(messages);
}

}

// 补全原始请求
chatCompletion.setMessages(minimaxChatCompletion.getMessages());
chatCompletion.setTools(minimaxChatCompletion.getTools());
}

@Override
public void chatCompletionStream(ChatCompletion chatCompletion, SseListener eventSourceListener) throws Exception {
this.chatCompletionStream(null, null, chatCompletion, eventSourceListener);
}
}
Loading