From 081da6d18da4df098736bb21fca44a2f1c8760b6 Mon Sep 17 00:00:00 2001 From: wangle Date: Fri, 17 Apr 2026 18:31:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0MiniMax=E4=BD=9C?= =?UTF-8?q?=E4=B8=BALLM=E6=8F=90=E4=BE=9B=E5=95=86=EF=BC=8C=E5=90=88?= =?UTF-8?q?=E5=B9=B6PR#280=E5=B9=B6=E8=A1=A5=E5=85=85=E7=9B=91=E5=90=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 合并PR#280的MiniMax provider实现,解决与main分支的冲突, 并在MinimaxServiceImpl中补充MyChatModelListener监听, 与其他provider保持一致。 Co-Authored-By: Claude Opus 4.7 --- README.md | 4 +- README_EN.md | 2 +- docs/script/sql/minimax_provider.sql | 23 ++++ ruoyi-modules/ruoyi-chat/pom.xml | 15 +++ .../KnowledgeGraphInstanceController.java | 105 ------------------ .../KnowledgeGraphSegmentController.java | 105 ------------------ .../java/org/ruoyi/enums/ChatModeType.java | 3 +- .../impl/provider/MinimaxServiceImpl.java | 44 ++++++++ .../embed/impl/MinimaxEmbeddingProvider.java | 17 +++ .../org/ruoyi/enums/ChatModeTypeTest.java | 42 +++++++ .../integration/MinimaxIntegrationTest.java | 70 ++++++++++++ .../impl/provider/MinimaxServiceImplTest.java | 76 +++++++++++++ .../impl/MinimaxEmbeddingProviderTest.java | 55 +++++++++ 13 files changed, 347 insertions(+), 214 deletions(-) create mode 100644 docs/script/sql/minimax_provider.sql delete mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/knowledge/KnowledgeGraphInstanceController.java delete mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/knowledge/KnowledgeGraphSegmentController.java create mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/MinimaxServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/embed/impl/MinimaxEmbeddingProvider.java create mode 100644 ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/enums/ChatModeTypeTest.java create mode 100644 ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/integration/MinimaxIntegrationTest.java create mode 100644 ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/service/chat/impl/provider/MinimaxServiceImplTest.java create mode 100644 ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/service/embed/impl/MinimaxEmbeddingProviderTest.java diff --git a/README.md b/README.md index e9c7c082..3b679895 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ | 模块 | 现有能力 |:----------:|--- -| **模型管理** | 多模型接入(OpenAI/DeepSeek/通义/智谱)、多模态理解、Coze/DIFY/FastGPT平台集成 -| **知识管理** | 本地RAG + 向量库(Milvus/Weaviate/Qdrant) + 文档解析 +| **模型管理** | 多模型接入(OpenAI/DeepSeek/通义/智谱/MiniMax)、多模态理解、Coze/DIFY/FastGPT平台集成 +| **知识管理** | 本地RAG + 向量库(Milvus/Weaviate/Qdrant) + 文档解析 | **工具管理** | Mcp协议集成、Skills能力 + 可扩展工具生态 | **流程编排** | 可视化工作流设计器、节点拖拽编排、SSE流式执行,目前已经支持模型调用,邮件发送,人工审核等节点 | **多智能体** | 基于Langchain4j的Agent框架、Supervisor模式编排,支持多种决策模型 diff --git a/README_EN.md b/README_EN.md index 00564414..b369491e 100644 --- a/README_EN.md +++ b/README_EN.md @@ -34,7 +34,7 @@ | 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 | | **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 | diff --git a/docs/script/sql/minimax_provider.sql b/docs/script/sql/minimax_provider.sql new file mode 100644 index 00000000..e7331aa2 --- /dev/null +++ b/docs/script/sql/minimax_provider.sql @@ -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); diff --git a/ruoyi-modules/ruoyi-chat/pom.xml b/ruoyi-modules/ruoyi-chat/pom.xml index 43ea2b1e..b551fda0 100644 --- a/ruoyi-modules/ruoyi-chat/pom.xml +++ b/ruoyi-modules/ruoyi-chat/pom.xml @@ -173,6 +173,21 @@ spring-boot-starter-test test + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/knowledge/KnowledgeGraphInstanceController.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/knowledge/KnowledgeGraphInstanceController.java deleted file mode 100644 index 8fbc547a..00000000 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/knowledge/KnowledgeGraphInstanceController.java +++ /dev/null @@ -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 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 list = knowledgeGraphInstanceService.queryList(bo); - ExcelUtil.exportExcel(list, "知识图谱实例", KnowledgeGraphInstanceVo.class, response); - } - - /** - * 获取知识图谱实例详细信息 - * - * @param id 主键 - */ - @SaCheckPermission("system:graphInstance:query") - @GetMapping("/{id}") - public R 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 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 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 remove(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] ids) { - return toAjax(knowledgeGraphInstanceService.deleteWithValidByIds(List.of(ids), true)); - } -} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/knowledge/KnowledgeGraphSegmentController.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/knowledge/KnowledgeGraphSegmentController.java deleted file mode 100644 index 7a0c7963..00000000 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/knowledge/KnowledgeGraphSegmentController.java +++ /dev/null @@ -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 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 list = knowledgeGraphSegmentService.queryList(bo); - ExcelUtil.exportExcel(list, "知识图谱片段", KnowledgeGraphSegmentVo.class, response); - } - - /** - * 获取知识图谱片段详细信息 - * - * @param id 主键 - */ - @SaCheckPermission("system:graphSegment:query") - @GetMapping("/{id}") - public R 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 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 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 remove(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] ids) { - return toAjax(knowledgeGraphSegmentService.deleteWithValidByIds(List.of(ids), true)); - } -} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/enums/ChatModeType.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/enums/ChatModeType.java index 1b91b8a2..62729ce2 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/enums/ChatModeType.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/enums/ChatModeType.java @@ -16,7 +16,8 @@ public enum ChatModeType { QIAN_WEN("qianwen", "通义千问"), OPEN_AI("openai", "openai"), PPIO("ppio", "ppio"), - CUSTOM_API("custom_api", "自定义API"); + CUSTOM_API("custom_api", "自定义API"), + MINIMAX("minimax", "MiniMax"); private final String code; private final String description; diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/MinimaxServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/MinimaxServiceImpl.java new file mode 100644 index 00000000..d83d9900 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/MinimaxServiceImpl.java @@ -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服务调用 + *

+ * 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(); + } + +} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/embed/impl/MinimaxEmbeddingProvider.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/embed/impl/MinimaxEmbeddingProvider.java new file mode 100644 index 00000000..4d2dbec5 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/embed/impl/MinimaxEmbeddingProvider.java @@ -0,0 +1,17 @@ +package org.ruoyi.service.embed.impl; + +import org.springframework.stereotype.Component; + +/** + * MiniMax嵌入模型(兼容OpenAI接口) + *

+ * 支持embo-01模型,1536维度向量。 + * API地址:https://api.minimax.io/v1 + * + * @author octopus + * @date 2026/3/21 + */ +@Component("minimax") +public class MinimaxEmbeddingProvider extends OpenAiEmbeddingProvider { + +} diff --git a/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/enums/ChatModeTypeTest.java b/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/enums/ChatModeTypeTest.java new file mode 100644 index 00000000..9733af8b --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/enums/ChatModeTypeTest.java @@ -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")); + } +} diff --git a/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/integration/MinimaxIntegrationTest.java b/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/integration/MinimaxIntegrationTest.java new file mode 100644 index 00000000..7e052e6d --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/integration/MinimaxIntegrationTest.java @@ -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"); + } +} diff --git a/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/service/chat/impl/provider/MinimaxServiceImplTest.java b/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/service/chat/impl/provider/MinimaxServiceImplTest.java new file mode 100644 index 00000000..697f52e2 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/service/chat/impl/provider/MinimaxServiceImplTest.java @@ -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); + } +} diff --git a/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/service/embed/impl/MinimaxEmbeddingProviderTest.java b/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/service/embed/impl/MinimaxEmbeddingProviderTest.java new file mode 100644 index 00000000..e089aae1 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/test/java/org/ruoyi/service/embed/impl/MinimaxEmbeddingProviderTest.java @@ -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 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); + } +}