mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-17 22:03:39 +00:00
feat: 添加MiniMax作为LLM提供商,合并PR#280并补充监听
合并PR#280的MiniMax provider实现,解决与main分支的冲突, 并在MinimaxServiceImpl中补充MyChatModelListener监听, 与其他provider保持一致。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
| 模块 | 现有能力
|
| 模块 | 现有能力
|
||||||
|:----------:|---
|
|:----------:|---
|
||||||
| **模型管理** | 多模型接入(OpenAI/DeepSeek/通义/智谱)、多模态理解、Coze/DIFY/FastGPT平台集成
|
| **模型管理** | 多模型接入(OpenAI/DeepSeek/通义/智谱/MiniMax)、多模态理解、Coze/DIFY/FastGPT平台集成
|
||||||
| **知识管理** | 本地RAG + 向量库(Milvus/Weaviate/Qdrant) + 文档解析
|
| **知识管理** | 本地RAG + 向量库(Milvus/Weaviate/Qdrant) + 文档解析
|
||||||
| **工具管理** | Mcp协议集成、Skills能力 + 可扩展工具生态
|
| **工具管理** | Mcp协议集成、Skills能力 + 可扩展工具生态
|
||||||
| **流程编排** | 可视化工作流设计器、节点拖拽编排、SSE流式执行,目前已经支持模型调用,邮件发送,人工审核等节点
|
| **流程编排** | 可视化工作流设计器、节点拖拽编排、SSE流式执行,目前已经支持模型调用,邮件发送,人工审核等节点
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
| Module | Current Capabilities |
|
| Module | Current Capabilities |
|
||||||
|:---:|---|
|
|:---:|---|
|
||||||
| **Model Management** | Multi-model integration (OpenAI/DeepSeek/Tongyi/Zhipu), multi-modal understanding, Coze/DIFY/FastGPT platform integration |
|
| **Model Management** | Multi-model integration (OpenAI/DeepSeek/Tongyi/Zhipu/MiniMax), multi-modal understanding, Coze/DIFY/FastGPT platform integration |
|
||||||
| **Knowledge Base** | Local RAG + Vector DB (Milvus/Weaviate/Qdrant) + Document parsing |
|
| **Knowledge Base** | Local RAG + Vector DB (Milvus/Weaviate/Qdrant) + Document parsing |
|
||||||
| **Tool Management** | MCP protocol integration, Skills capability + Extensible tool ecosystem |
|
| **Tool Management** | MCP protocol integration, Skills capability + Extensible tool ecosystem |
|
||||||
| **Workflow Orchestration** | Visual workflow designer, drag-and-drop node orchestration, SSE streaming execution, currently supports model calls, email sending, manual review nodes |
|
| **Workflow Orchestration** | Visual workflow designer, drag-and-drop node orchestration, SSE streaming execution, currently supports model calls, email sending, manual review nodes |
|
||||||
|
|||||||
23
docs/script/sql/minimax_provider.sql
Normal file
23
docs/script/sql/minimax_provider.sql
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-- ----------------------------
|
||||||
|
-- Add MiniMax provider
|
||||||
|
-- ----------------------------
|
||||||
|
INSERT INTO `chat_provider` (`id`, `provider_name`, `provider_code`, `provider_icon`, `provider_desc`, `api_host`, `status`, `sort_order`, `create_dept`, `create_time`, `create_by`, `update_by`, `update_time`, `remark`, `version`, `del_flag`, `update_ip`, `tenant_id`)
|
||||||
|
VALUES (2010000000000000001, 'MiniMax', 'minimax', NULL, 'MiniMax大模型服务,支持M2.7、M2.5等模型', 'https://api.minimax.io/v1', '0', 6, NULL, NOW(), '1', '1', NOW(), 'MiniMax厂商', NULL, '0', NULL, 0);
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Add MiniMax chat models
|
||||||
|
-- ----------------------------
|
||||||
|
INSERT INTO `chat_model` (`id`, `category`, `model_name`, `provider_code`, `model_describe`, `model_dimension`, `model_show`, `api_host`, `api_key`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `tenant_id`)
|
||||||
|
VALUES (2010000000000000002, 'chat', 'MiniMax-M2.7', 'minimax', 'MiniMax-M2.7', NULL, 'Y', 'https://api.minimax.io/v1', '', NULL, 1, NOW(), 1, NOW(), 'MiniMax最新旗舰模型M2.7,支持1M上下文窗口', 0);
|
||||||
|
|
||||||
|
INSERT INTO `chat_model` (`id`, `category`, `model_name`, `provider_code`, `model_describe`, `model_dimension`, `model_show`, `api_host`, `api_key`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `tenant_id`)
|
||||||
|
VALUES (2010000000000000003, 'chat', 'MiniMax-M2.5', 'minimax', 'MiniMax-M2.5', NULL, 'Y', 'https://api.minimax.io/v1', '', NULL, 1, NOW(), 1, NOW(), 'MiniMax M2.5模型,204K上下文窗口', 0);
|
||||||
|
|
||||||
|
INSERT INTO `chat_model` (`id`, `category`, `model_name`, `provider_code`, `model_describe`, `model_dimension`, `model_show`, `api_host`, `api_key`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `tenant_id`)
|
||||||
|
VALUES (2010000000000000004, 'chat', 'MiniMax-M2.5-highspeed', 'minimax', 'MiniMax-M2.5-highspeed', NULL, 'Y', 'https://api.minimax.io/v1', '', NULL, 1, NOW(), 1, NOW(), 'MiniMax M2.5高速版,204K上下文窗口,更低延迟', 0);
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Add MiniMax embedding model
|
||||||
|
-- ----------------------------
|
||||||
|
INSERT INTO `chat_model` (`id`, `category`, `model_name`, `provider_code`, `model_describe`, `model_dimension`, `model_show`, `api_host`, `api_key`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `tenant_id`)
|
||||||
|
VALUES (2010000000000000005, 'vector', 'embo-01', 'minimax', 'embo-01', 1536, 'N', 'https://api.minimax.io/v1', '', NULL, 1, NOW(), 1, NOW(), 'MiniMax embo-01嵌入模型,1536维度', 0);
|
||||||
@@ -173,6 +173,21 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
package org.ruoyi.controller.knowledge;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import jakarta.validation.constraints.*;
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
|
||||||
import org.ruoyi.domain.bo.knowledge.KnowledgeGraphInstanceBo;
|
|
||||||
import org.ruoyi.domain.vo.knowledge.KnowledgeGraphInstanceVo;
|
|
||||||
import org.ruoyi.service.knowledge.IKnowledgeGraphInstanceService;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
|
||||||
import org.ruoyi.common.log.annotation.Log;
|
|
||||||
import org.ruoyi.common.web.core.BaseController;
|
|
||||||
import org.ruoyi.common.mybatis.core.page.PageQuery;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.core.validate.AddGroup;
|
|
||||||
import org.ruoyi.common.core.validate.EditGroup;
|
|
||||||
import org.ruoyi.common.log.enums.BusinessType;
|
|
||||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
|
||||||
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 知识图谱实例
|
|
||||||
*
|
|
||||||
* @author ageerle
|
|
||||||
* @date 2025-12-17
|
|
||||||
*/
|
|
||||||
@Validated
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/system/graphInstance")
|
|
||||||
public class KnowledgeGraphInstanceController extends BaseController {
|
|
||||||
|
|
||||||
private final IKnowledgeGraphInstanceService knowledgeGraphInstanceService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询知识图谱实例列表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphInstance:list")
|
|
||||||
@GetMapping("/list")
|
|
||||||
public TableDataInfo<KnowledgeGraphInstanceVo> list(KnowledgeGraphInstanceBo bo, PageQuery pageQuery) {
|
|
||||||
return knowledgeGraphInstanceService.queryPageList(bo, pageQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出知识图谱实例列表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphInstance:export")
|
|
||||||
@Log(title = "知识图谱实例", businessType = BusinessType.EXPORT)
|
|
||||||
@PostMapping("/export")
|
|
||||||
public void export(KnowledgeGraphInstanceBo bo, HttpServletResponse response) {
|
|
||||||
List<KnowledgeGraphInstanceVo> list = knowledgeGraphInstanceService.queryList(bo);
|
|
||||||
ExcelUtil.exportExcel(list, "知识图谱实例", KnowledgeGraphInstanceVo.class, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取知识图谱实例详细信息
|
|
||||||
*
|
|
||||||
* @param id 主键
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphInstance:query")
|
|
||||||
@GetMapping("/{id}")
|
|
||||||
public R<KnowledgeGraphInstanceVo> getInfo(@NotNull(message = "主键不能为空")
|
|
||||||
@PathVariable Long id) {
|
|
||||||
return R.ok(knowledgeGraphInstanceService.queryById(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增知识图谱实例
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphInstance:add")
|
|
||||||
@Log(title = "知识图谱实例", businessType = BusinessType.INSERT)
|
|
||||||
@RepeatSubmit()
|
|
||||||
@PostMapping()
|
|
||||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody KnowledgeGraphInstanceBo bo) {
|
|
||||||
return toAjax(knowledgeGraphInstanceService.insertByBo(bo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改知识图谱实例
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphInstance:edit")
|
|
||||||
@Log(title = "知识图谱实例", businessType = BusinessType.UPDATE)
|
|
||||||
@RepeatSubmit()
|
|
||||||
@PutMapping()
|
|
||||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody KnowledgeGraphInstanceBo bo) {
|
|
||||||
return toAjax(knowledgeGraphInstanceService.updateByBo(bo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除知识图谱实例
|
|
||||||
*
|
|
||||||
* @param ids 主键串
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphInstance:remove")
|
|
||||||
@Log(title = "知识图谱实例", businessType = BusinessType.DELETE)
|
|
||||||
@DeleteMapping("/{ids}")
|
|
||||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
|
||||||
@PathVariable Long[] ids) {
|
|
||||||
return toAjax(knowledgeGraphInstanceService.deleteWithValidByIds(List.of(ids), true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
package org.ruoyi.controller.knowledge;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import jakarta.validation.constraints.*;
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
|
||||||
import org.ruoyi.domain.bo.knowledge.KnowledgeGraphSegmentBo;
|
|
||||||
import org.ruoyi.domain.vo.knowledge.KnowledgeGraphSegmentVo;
|
|
||||||
import org.ruoyi.service.knowledge.IKnowledgeGraphSegmentService;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
|
||||||
import org.ruoyi.common.log.annotation.Log;
|
|
||||||
import org.ruoyi.common.web.core.BaseController;
|
|
||||||
import org.ruoyi.common.mybatis.core.page.PageQuery;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.core.validate.AddGroup;
|
|
||||||
import org.ruoyi.common.core.validate.EditGroup;
|
|
||||||
import org.ruoyi.common.log.enums.BusinessType;
|
|
||||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
|
||||||
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 知识图谱片段
|
|
||||||
*
|
|
||||||
* @author ageerle
|
|
||||||
* @date 2025-12-17
|
|
||||||
*/
|
|
||||||
@Validated
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/system/graphSegment")
|
|
||||||
public class KnowledgeGraphSegmentController extends BaseController {
|
|
||||||
|
|
||||||
private final IKnowledgeGraphSegmentService knowledgeGraphSegmentService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询知识图谱片段列表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphSegment:list")
|
|
||||||
@GetMapping("/list")
|
|
||||||
public TableDataInfo<KnowledgeGraphSegmentVo> list(KnowledgeGraphSegmentBo bo, PageQuery pageQuery) {
|
|
||||||
return knowledgeGraphSegmentService.queryPageList(bo, pageQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出知识图谱片段列表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphSegment:export")
|
|
||||||
@Log(title = "知识图谱片段", businessType = BusinessType.EXPORT)
|
|
||||||
@PostMapping("/export")
|
|
||||||
public void export(KnowledgeGraphSegmentBo bo, HttpServletResponse response) {
|
|
||||||
List<KnowledgeGraphSegmentVo> list = knowledgeGraphSegmentService.queryList(bo);
|
|
||||||
ExcelUtil.exportExcel(list, "知识图谱片段", KnowledgeGraphSegmentVo.class, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取知识图谱片段详细信息
|
|
||||||
*
|
|
||||||
* @param id 主键
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphSegment:query")
|
|
||||||
@GetMapping("/{id}")
|
|
||||||
public R<KnowledgeGraphSegmentVo> getInfo(@NotNull(message = "主键不能为空")
|
|
||||||
@PathVariable Long id) {
|
|
||||||
return R.ok(knowledgeGraphSegmentService.queryById(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增知识图谱片段
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphSegment:add")
|
|
||||||
@Log(title = "知识图谱片段", businessType = BusinessType.INSERT)
|
|
||||||
@RepeatSubmit()
|
|
||||||
@PostMapping()
|
|
||||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody KnowledgeGraphSegmentBo bo) {
|
|
||||||
return toAjax(knowledgeGraphSegmentService.insertByBo(bo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改知识图谱片段
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphSegment:edit")
|
|
||||||
@Log(title = "知识图谱片段", businessType = BusinessType.UPDATE)
|
|
||||||
@RepeatSubmit()
|
|
||||||
@PutMapping()
|
|
||||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody KnowledgeGraphSegmentBo bo) {
|
|
||||||
return toAjax(knowledgeGraphSegmentService.updateByBo(bo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除知识图谱片段
|
|
||||||
*
|
|
||||||
* @param ids 主键串
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("system:graphSegment:remove")
|
|
||||||
@Log(title = "知识图谱片段", businessType = BusinessType.DELETE)
|
|
||||||
@DeleteMapping("/{ids}")
|
|
||||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
|
||||||
@PathVariable Long[] ids) {
|
|
||||||
return toAjax(knowledgeGraphSegmentService.deleteWithValidByIds(List.of(ids), true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,8 @@ public enum ChatModeType {
|
|||||||
QIAN_WEN("qianwen", "通义千问"),
|
QIAN_WEN("qianwen", "通义千问"),
|
||||||
OPEN_AI("openai", "openai"),
|
OPEN_AI("openai", "openai"),
|
||||||
PPIO("ppio", "ppio"),
|
PPIO("ppio", "ppio"),
|
||||||
CUSTOM_API("custom_api", "自定义API");
|
CUSTOM_API("custom_api", "自定义API"),
|
||||||
|
MINIMAX("minimax", "MiniMax");
|
||||||
private final String code;
|
private final String code;
|
||||||
private final String description;
|
private final String description;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.ruoyi.service.chat.impl.provider;
|
||||||
|
|
||||||
|
import dev.langchain4j.model.chat.StreamingChatModel;
|
||||||
|
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
|
import org.ruoyi.enums.ChatModeType;
|
||||||
|
import org.ruoyi.observability.MyChatModelListener;
|
||||||
|
import org.ruoyi.service.chat.AbstractChatService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MiniMax服务调用
|
||||||
|
* <p>
|
||||||
|
* MiniMax提供OpenAI兼容的API接口,支持MiniMax-M2.7、MiniMax-M2.5等模型。
|
||||||
|
* API地址:https://api.minimax.io/v1
|
||||||
|
*
|
||||||
|
* @author octopus
|
||||||
|
* @date 2026/3/21
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class MinimaxServiceImpl implements AbstractChatService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamingChatModel buildStreamingChatModel(ChatModelVo chatModelVo, ChatRequest chatRequest) {
|
||||||
|
return OpenAiStreamingChatModel.builder()
|
||||||
|
.baseUrl(chatModelVo.getApiHost())
|
||||||
|
.apiKey(chatModelVo.getApiKey())
|
||||||
|
.modelName(chatModelVo.getModelName())
|
||||||
|
.listeners(List.of(new MyChatModelListener()))
|
||||||
|
.returnThinking(chatRequest.getEnableThinking())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProviderName() {
|
||||||
|
return ChatModeType.MINIMAX.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.ruoyi.service.embed.impl;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MiniMax嵌入模型(兼容OpenAI接口)
|
||||||
|
* <p>
|
||||||
|
* 支持embo-01模型,1536维度向量。
|
||||||
|
* API地址:https://api.minimax.io/v1
|
||||||
|
*
|
||||||
|
* @author octopus
|
||||||
|
* @date 2026/3/21
|
||||||
|
*/
|
||||||
|
@Component("minimax")
|
||||||
|
public class MinimaxEmbeddingProvider extends OpenAiEmbeddingProvider {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.ruoyi.enums;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for ChatModeType enum
|
||||||
|
*/
|
||||||
|
class ChatModeTypeTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void minimaxEnumExists() {
|
||||||
|
ChatModeType minimax = ChatModeType.MINIMAX;
|
||||||
|
assertNotNull(minimax);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void minimaxCode_isMinimax() {
|
||||||
|
assertEquals("minimax", ChatModeType.MINIMAX.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void minimaxDescription_isMiniMax() {
|
||||||
|
assertEquals("MiniMax", ChatModeType.MINIMAX.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void allProviders_haveUniqueCode() {
|
||||||
|
ChatModeType[] values = ChatModeType.values();
|
||||||
|
long uniqueCodes = java.util.Arrays.stream(values)
|
||||||
|
.map(ChatModeType::getCode)
|
||||||
|
.distinct()
|
||||||
|
.count();
|
||||||
|
assertEquals(values.length, uniqueCodes, "All providers must have unique codes");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void valueOf_minimax() {
|
||||||
|
assertEquals(ChatModeType.MINIMAX, ChatModeType.valueOf("MINIMAX"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package org.ruoyi.integration;
|
||||||
|
|
||||||
|
import dev.langchain4j.model.chat.StreamingChatModel;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||||
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
|
import org.ruoyi.service.chat.impl.provider.MinimaxServiceImpl;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for MiniMax provider.
|
||||||
|
* These tests require a valid MINIMAX_API_KEY environment variable.
|
||||||
|
*/
|
||||||
|
@EnabledIfEnvironmentVariable(named = "MINIMAX_API_KEY", matches = ".+")
|
||||||
|
class MinimaxIntegrationTest {
|
||||||
|
|
||||||
|
private MinimaxServiceImpl minimaxService;
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
minimaxService = new MinimaxServiceImpl();
|
||||||
|
apiKey = System.getenv("MINIMAX_API_KEY");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildStreamingChatModel_withRealApiKey_M27() {
|
||||||
|
ChatModelVo modelVo = new ChatModelVo();
|
||||||
|
modelVo.setApiHost("https://api.minimax.io/v1");
|
||||||
|
modelVo.setApiKey(apiKey);
|
||||||
|
modelVo.setModelName("MiniMax-M2.7");
|
||||||
|
|
||||||
|
ChatRequest request = new ChatRequest();
|
||||||
|
request.setEnableThinking(false);
|
||||||
|
|
||||||
|
StreamingChatModel model = minimaxService.buildStreamingChatModel(modelVo, request);
|
||||||
|
assertNotNull(model, "Should create streaming model with real API key");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildStreamingChatModel_withRealApiKey_M25() {
|
||||||
|
ChatModelVo modelVo = new ChatModelVo();
|
||||||
|
modelVo.setApiHost("https://api.minimax.io/v1");
|
||||||
|
modelVo.setApiKey(apiKey);
|
||||||
|
modelVo.setModelName("MiniMax-M2.5");
|
||||||
|
|
||||||
|
ChatRequest request = new ChatRequest();
|
||||||
|
request.setEnableThinking(false);
|
||||||
|
|
||||||
|
StreamingChatModel model = minimaxService.buildStreamingChatModel(modelVo, request);
|
||||||
|
assertNotNull(model, "Should create streaming model with M2.5");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildStreamingChatModel_withRealApiKey_M25Highspeed() {
|
||||||
|
ChatModelVo modelVo = new ChatModelVo();
|
||||||
|
modelVo.setApiHost("https://api.minimax.io/v1");
|
||||||
|
modelVo.setApiKey(apiKey);
|
||||||
|
modelVo.setModelName("MiniMax-M2.5-highspeed");
|
||||||
|
|
||||||
|
ChatRequest request = new ChatRequest();
|
||||||
|
request.setEnableThinking(false);
|
||||||
|
|
||||||
|
StreamingChatModel model = minimaxService.buildStreamingChatModel(modelVo, request);
|
||||||
|
assertNotNull(model, "Should create streaming model with M2.5-highspeed");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package org.ruoyi.service.chat.impl.provider;
|
||||||
|
|
||||||
|
import dev.langchain4j.model.chat.StreamingChatModel;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
|
import org.ruoyi.enums.ChatModeType;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for MinimaxServiceImpl
|
||||||
|
*/
|
||||||
|
class MinimaxServiceImplTest {
|
||||||
|
|
||||||
|
private MinimaxServiceImpl minimaxService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
minimaxService = new MinimaxServiceImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getProviderName_returnsMinimaxCode() {
|
||||||
|
assertEquals("minimax", minimaxService.getProviderName());
|
||||||
|
assertEquals(ChatModeType.MINIMAX.getCode(), minimaxService.getProviderName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildStreamingChatModel_returnsNonNull() {
|
||||||
|
ChatModelVo modelVo = new ChatModelVo();
|
||||||
|
modelVo.setApiHost("https://api.minimax.io/v1");
|
||||||
|
modelVo.setApiKey("test-api-key");
|
||||||
|
modelVo.setModelName("MiniMax-M2.7");
|
||||||
|
|
||||||
|
ChatRequest request = new ChatRequest();
|
||||||
|
request.setEnableThinking(false);
|
||||||
|
|
||||||
|
StreamingChatModel model = minimaxService.buildStreamingChatModel(modelVo, request);
|
||||||
|
assertNotNull(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildStreamingChatModel_withThinkingEnabled() {
|
||||||
|
ChatModelVo modelVo = new ChatModelVo();
|
||||||
|
modelVo.setApiHost("https://api.minimax.io/v1");
|
||||||
|
modelVo.setApiKey("test-api-key");
|
||||||
|
modelVo.setModelName("MiniMax-M2.5");
|
||||||
|
|
||||||
|
ChatRequest request = new ChatRequest();
|
||||||
|
request.setEnableThinking(true);
|
||||||
|
|
||||||
|
StreamingChatModel model = minimaxService.buildStreamingChatModel(modelVo, request);
|
||||||
|
assertNotNull(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildStreamingChatModel_withHighspeedModel() {
|
||||||
|
ChatModelVo modelVo = new ChatModelVo();
|
||||||
|
modelVo.setApiHost("https://api.minimax.io/v1");
|
||||||
|
modelVo.setApiKey("test-api-key");
|
||||||
|
modelVo.setModelName("MiniMax-M2.5-highspeed");
|
||||||
|
|
||||||
|
ChatRequest request = new ChatRequest();
|
||||||
|
request.setEnableThinking(false);
|
||||||
|
|
||||||
|
StreamingChatModel model = minimaxService.buildStreamingChatModel(modelVo, request);
|
||||||
|
assertNotNull(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void implementsAbstractChatService() {
|
||||||
|
assertInstanceOf(org.ruoyi.service.chat.AbstractChatService.class, minimaxService);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package org.ruoyi.service.embed.impl;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
|
import org.ruoyi.enums.ModalityType;
|
||||||
|
import org.ruoyi.service.embed.BaseEmbedModelService;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for MinimaxEmbeddingProvider
|
||||||
|
*/
|
||||||
|
class MinimaxEmbeddingProviderTest {
|
||||||
|
|
||||||
|
private MinimaxEmbeddingProvider provider;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
provider = new MinimaxEmbeddingProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void implementsBaseEmbedModelService() {
|
||||||
|
assertInstanceOf(BaseEmbedModelService.class, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void extendsOpenAiEmbeddingProvider() {
|
||||||
|
assertInstanceOf(OpenAiEmbeddingProvider.class, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSupportedModalities_returnsText() {
|
||||||
|
Set<ModalityType> modalities = provider.getSupportedModalities();
|
||||||
|
assertNotNull(modalities);
|
||||||
|
assertTrue(modalities.contains(ModalityType.TEXT));
|
||||||
|
assertEquals(1, modalities.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void configure_setsModelConfig() {
|
||||||
|
ChatModelVo config = new ChatModelVo();
|
||||||
|
config.setApiHost("https://api.minimax.io/v1");
|
||||||
|
config.setApiKey("test-api-key");
|
||||||
|
config.setModelName("embo-01");
|
||||||
|
config.setModelDimension(1536);
|
||||||
|
|
||||||
|
provider.configure(config);
|
||||||
|
// configure sets internal state; verify no exception thrown
|
||||||
|
assertNotNull(provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user