Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions chat2db-client/src/blocks/Setting/AiSetting/aiTypeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const AITypeName = {
[AIType.TONGYIQIANWENAI]: i18n('setting.tab.aiType.tongyiqianwen'),
[AIType.OPENAI]: 'Open AI',
[AIType.AZUREAI]: 'Azure AI',
[AIType.MINIMAXAI]: 'MiniMax AI',
[AIType.RESTAI]: i18n('setting.tab.custom'),
};

Expand Down Expand Up @@ -53,6 +54,11 @@ const AIFormConfig: Record<AIType, IAiConfigBooleans> = {
apiHost: true,
model: true,
},
[AIType.MINIMAXAI]: {
apiKey: true,
apiHost: 'https://api.minimax.io/v1/chat/completions',
model: 'MiniMax-M2.7',
},
[AIType.RESTAI]: {
apiKey: true,
apiHost: true,
Expand Down
1 change: 1 addition & 0 deletions chat2db-client/src/typings/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export enum AIType {
TONGYIQIANWENAI='TONGYIQIANWENAI',
OPENAI = 'OPENAI',
AZUREAI = 'AZUREAI',
MINIMAXAI = 'MINIMAXAI',
RESTAI = 'RESTAI',
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ public enum AiSqlSourceEnum implements BaseEnum<String> {
*/
FASTCHATAI("FAST CHAT AI"),

/**
* MINIMAX AI
*/
MINIMAXAI("MINIMAX AI"),

;

final String description;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import ai.chat2db.server.web.api.controller.ai.fastchat.listener.FastChatAIEventSourceListener;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatRole;
import ai.chat2db.server.web.api.controller.ai.minimax.client.MiniMaxAIClient;
import ai.chat2db.server.web.api.controller.ai.minimax.listener.MiniMaxAIEventSourceListener;
import ai.chat2db.server.web.api.controller.ai.openai.client.OpenAIClient;
import ai.chat2db.server.web.api.controller.ai.openai.listener.OpenAIEventSourceListener;
import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
Expand Down Expand Up @@ -250,6 +252,8 @@ public SseEmitter distributeAISql(ChatQueryRequest queryRequest, SseEmitter sseE
return chatWithTongyiChatAi(queryRequest, sseEmitter, uid);
case ZHIPUAI:
return chatWithZhipuChatAi(queryRequest, sseEmitter, uid);
case MINIMAXAI:
return chatWithMiniMaxAi(queryRequest, sseEmitter, uid);
}
return chatWithOpenAi(queryRequest, sseEmitter, uid);
}
Expand Down Expand Up @@ -454,6 +458,27 @@ private SseEmitter chatWithBaichuanAi(ChatQueryRequest queryRequest, SseEmitter
return sseEmitter;
}

/**
* chat with MiniMax AI
*
* @param queryRequest
* @param sseEmitter
* @param uid
* @return
* @throws IOException
*/
private SseEmitter chatWithMiniMaxAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
String prompt = buildPrompt(queryRequest);
List<FastChatMessage> messages = getFastChatMessage(uid, prompt);

buildSseEmitter(sseEmitter, uid);

MiniMaxAIEventSourceListener sourceListener = new MiniMaxAIEventSourceListener(sseEmitter);
MiniMaxAIClient.getInstance().streamCompletions(messages, sourceListener);
LocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);
return sseEmitter;
}

/**
* get fast chat message
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package ai.chat2db.server.web.api.controller.ai.minimax.client;

import ai.chat2db.server.domain.api.model.Config;
import ai.chat2db.server.domain.api.service.ConfigService;
import ai.chat2db.server.web.api.util.ApplicationContextUtil;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
* MiniMax AI client
*
* @author octo-patch
*/
@Slf4j
public class MiniMaxAIClient {

/**
* MiniMax AI API Key
*/
public static final String MINIMAX_API_KEY = "minimax.ai.apiKey";

/**
* MiniMax AI API Host
*/
public static final String MINIMAX_HOST = "minimax.ai.host";

/**
* MiniMax AI Model
*/
public static final String MINIMAX_MODEL = "minimax.ai.model";

private static MiniMaxAIStreamClient MINIMAX_AI_STREAM_CLIENT;

public static MiniMaxAIStreamClient getInstance() {
if (MINIMAX_AI_STREAM_CLIENT != null) {
return MINIMAX_AI_STREAM_CLIENT;
} else {
return singleton();
}
}

private static MiniMaxAIStreamClient singleton() {
if (MINIMAX_AI_STREAM_CLIENT == null) {
synchronized (MiniMaxAIClient.class) {
if (MINIMAX_AI_STREAM_CLIENT == null) {
refresh();
}
}
}
return MINIMAX_AI_STREAM_CLIENT;
}

/**
* Refresh client
*/
public static void refresh() {
String apiUrl = "";
String apiKey = "";
String model = "";
ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
Config apiHostConfig = configService.find(MINIMAX_HOST).getData();
if (apiHostConfig != null && StringUtils.isNotBlank(apiHostConfig.getContent())) {
apiUrl = apiHostConfig.getContent();
}
Config config = configService.find(MINIMAX_API_KEY).getData();
if (config != null) {
apiKey = config.getContent();
}
Config modelConfig = configService.find(MINIMAX_MODEL).getData();
if (modelConfig != null && StringUtils.isNotBlank(modelConfig.getContent())) {
model = modelConfig.getContent();
}
MINIMAX_AI_STREAM_CLIENT = MiniMaxAIStreamClient.builder().apiKey(apiKey).apiHost(apiUrl).model(model)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package ai.chat2db.server.web.api.controller.ai.minimax.client;

import ai.chat2db.server.tools.common.exception.ParamBusinessException;
import ai.chat2db.server.web.api.controller.ai.fastchat.interceptor.FastChatHeaderAuthorizationInterceptor;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsOptions;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
import cn.hutool.http.ContentType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import okhttp3.sse.EventSources;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
* MiniMax AI stream client
*
* @author octo-patch
*/
@Slf4j
public class MiniMaxAIStreamClient {

private static final String DEFAULT_HOST = "https://api.minimax.io/v1/chat/completions";

private static final String DEFAULT_MODEL = "MiniMax-M2.7";

/**
* apikey
*/
@Getter
@NotNull
private String apiKey;

/**
* apiHost
*/
@Getter
@NotNull
private String apiHost;

/**
* model
*/
@Getter
private String model;

/**
* okHttpClient
*/
@Getter
private OkHttpClient okHttpClient;

/**
* Construct instance object
*
* @param builder
*/
public MiniMaxAIStreamClient(Builder builder) {
this.apiKey = builder.apiKey;
this.apiHost = StringUtils.isNotBlank(builder.apiHost) ? builder.apiHost : DEFAULT_HOST;
this.model = StringUtils.isNotBlank(builder.model) ? builder.model : DEFAULT_MODEL;
this.okHttpClient = new OkHttpClient
.Builder()
.addInterceptor(new FastChatHeaderAuthorizationInterceptor(this.apiKey))
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(50, TimeUnit.SECONDS)
.readTimeout(50, TimeUnit.SECONDS)
.build();
}

/**
* builder
*
* @return
*/
public static MiniMaxAIStreamClient.Builder builder() {
return new MiniMaxAIStreamClient.Builder();
}

/**
* builder
*/
public static final class Builder {
private String apiKey;

private String apiHost;

private String model;

private OkHttpClient okHttpClient;

public Builder() {
}

public MiniMaxAIStreamClient.Builder apiKey(String apiKeyValue) {
this.apiKey = apiKeyValue;
return this;
}

public MiniMaxAIStreamClient.Builder apiHost(String apiHostValue) {
this.apiHost = apiHostValue;
return this;
}

public MiniMaxAIStreamClient.Builder model(String modelValue) {
this.model = modelValue;
return this;
}

public MiniMaxAIStreamClient.Builder okHttpClient(OkHttpClient val) {
this.okHttpClient = val;
return this;
}

public MiniMaxAIStreamClient build() {
return new MiniMaxAIStreamClient(this);
}
}

/**
* Stream completions
*
* @param chatMessages
* @param eventSourceListener
*/
public void streamCompletions(List<FastChatMessage> chatMessages, EventSourceListener eventSourceListener) {
if (CollectionUtils.isEmpty(chatMessages)) {
log.error("param error: MiniMax AI Prompt cannot be empty");
throw new ParamBusinessException("prompt");
}
if (Objects.isNull(eventSourceListener)) {
log.error("param error: MiniMaxAIEventSourceListener cannot be empty");
throw new ParamBusinessException();
}
log.info("MiniMax AI, prompt:{}", chatMessages.get(chatMessages.size() - 1).getContent());
try {
FastChatCompletionsOptions chatCompletionsOptions = new FastChatCompletionsOptions(chatMessages);
chatCompletionsOptions.setStream(true);
chatCompletionsOptions.setModel(this.model);

EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String requestBody = mapper.writeValueAsString(chatCompletionsOptions);
Request request = new Request.Builder()
.url(apiHost)
.post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))
.build();
EventSource eventSource = factory.newEventSource(request, eventSourceListener);
log.info("finish invoking MiniMax AI");
} catch (Exception e) {
log.error("MiniMax AI error", e);
eventSourceListener.onFailure(null, e, null);
throw new ParamBusinessException();
}
}
}
Loading