返回 ViMax
test_minimax_integration.py
根目录 / tests / test_minimax_integration.py
1 """Integration tests for MiniMax provider support.
2
3 These tests verify provider preset resolution and default pipeline config
4 loading. They mock the LangChain factory so no real API calls are made.
5
6 Heavy multimedia dependencies (moviepy, scenedetect, cv2, google-genai,
7 etc.) are stubbed in setUpModule and restored in tearDownModule. Stubbing
8 at import time leaked the MagicMocks into sys.modules for every test module
9 collected after this one, making suite results import-order dependent.
10 """
11
12 import importlib
13 import os
14 import sys
15 import types
16 import unittest
17 from unittest.mock import patch, MagicMock
18
19 _STUB_MODULES = [
20 "moviepy", "cv2", "scenedetect", "scenedetect.detectors",
21 "PIL", "PIL.Image",
22 "faiss",
23 "google", "google.genai", "google.genai.types", "google.genai.errors",
24 "langchain_community", "langchain_community.vectorstores",
25 "langchain_community.vectorstores.FAISS",
26 ]
27 _saved = {}
28 _modules_before_stubs = set()
29
30
31 def setUpModule():
32 _modules_before_stubs.update(sys.modules)
33 for _mod in _STUB_MODULES:
34 _saved[_mod] = sys.modules.get(_mod)
35 mock = MagicMock()
36 # Give stub a __spec__ so importlib.util.find_spec() works
37 mock.__spec__ = importlib.machinery.ModuleSpec(_mod, None)
38 mock.__path__ = []
39 sys.modules[_mod] = mock
40
41
42 def tearDownModule():
43 # Drop project modules that were first imported while the stubs were
44 # active, so later test modules import them fresh against real libraries.
45 for name in list(sys.modules):
46 if name in _modules_before_stubs:
47 continue
48 if name.split(".")[0] in {"pipelines", "agents", "tools", "interfaces"}:
49 del sys.modules[name]
50 for _mod, original in _saved.items():
51 if original is None:
52 sys.modules.pop(_mod, None)
53 else:
54 sys.modules[_mod] = original
55 _saved.clear()
56 _modules_before_stubs.clear()
57
58
59 from utils.provider_presets import resolve_chat_model_config
60
61
62 class TestPipelineConfigResolution(unittest.TestCase):
63 """Integration: config dict -> resolve -> init_chat_model kwargs."""
64
65 def _make_minimax_config(self, **overrides):
66 base = {
67 "model": "MiniMax-M3",
68 "model_provider": "minimax",
69 "api_key": "test-key",
70 }
71 base.update(overrides)
72 return base
73
74 def test_full_minimax_config_resolution(self):
75 config = self._make_minimax_config()
76 resolved = resolve_chat_model_config(config)
77 self.assertEqual(resolved["model_provider"], "openai")
78 self.assertEqual(resolved["base_url"], "https://api.minimax.io/v1")
79 self.assertEqual(resolved["model"], "MiniMax-M3")
80 self.assertEqual(resolved["api_key"], "test-key")
81
82 def test_minimax_highspeed_model(self):
83 config = self._make_minimax_config(model="MiniMax-M2.7-highspeed")
84 resolved = resolve_chat_model_config(config)
85 self.assertEqual(resolved["model"], "MiniMax-M2.7-highspeed")
86 self.assertEqual(resolved["model_provider"], "openai")
87
88 def test_minimax_m27_model(self):
89 config = self._make_minimax_config(model="MiniMax-M2.7")
90 resolved = resolve_chat_model_config(config)
91 self.assertEqual(resolved["model"], "MiniMax-M2.7")
92
93 @patch.dict(os.environ, {"MINIMAX_API_KEY": "env-api-key"})
94 def test_env_key_fallback_in_config(self):
95 config = {
96 "model": "MiniMax-M3",
97 "model_provider": "minimax",
98 }
99 resolved = resolve_chat_model_config(config)
100 self.assertEqual(resolved["api_key"], "env-api-key")
101
102 def test_openrouter_config_unchanged(self):
103 """Existing OpenRouter configs must not be affected."""
104 config = {
105 "model": "google/gemini-2.5-flash-lite-preview-09-2025",
106 "model_provider": "openai",
107 "api_key": "or-key",
108 "base_url": "https://openrouter.ai/api/v1",
109 }
110 resolved = resolve_chat_model_config(config)
111 self.assertEqual(resolved["model_provider"], "openai")
112 self.assertEqual(resolved["base_url"], "https://openrouter.ai/api/v1")
113 self.assertEqual(resolved["model"], "google/gemini-2.5-flash-lite-preview-09-2025")
114
115 def test_init_chat_model_receives_openai_provider(self):
116 """Verify that resolved kwargs have model_provider='openai'."""
117 config = self._make_minimax_config()
118 resolved = resolve_chat_model_config(config)
119 self.assertEqual(resolved["model_provider"], "openai")
120 self.assertEqual(resolved["base_url"], "https://api.minimax.io/v1")
121 self.assertEqual(resolved["model"], "MiniMax-M3")
122
123 def test_temperature_clamping_in_pipeline_flow(self):
124 config = self._make_minimax_config(temperature=2.0)
125 resolved = resolve_chat_model_config(config)
126 self.assertEqual(resolved["temperature"], 1.0)
127
128 def test_extra_kwargs_preserved(self):
129 config = self._make_minimax_config(max_tokens=4096, top_p=0.9)
130 resolved = resolve_chat_model_config(config)
131 self.assertEqual(resolved["max_tokens"], 4096)
132 self.assertEqual(resolved["top_p"], 0.9)
133
134
135 class TestPipelineInitFromConfig(unittest.TestCase):
136 """Integration: full pipeline init_from_config with provider configs."""
137
138 @patch("pipelines.idea2video_pipeline.init_chat_model")
139 @patch("pipelines.idea2video_pipeline.RenderBackend.from_config")
140 def test_idea2video_pipeline_minimax_config(self, mock_backend, mock_init):
141 mock_model = MagicMock()
142 mock_init.return_value = mock_model
143 mock_backend.return_value = MagicMock(image_generator=MagicMock(), video_generator=MagicMock())
144
145 from pipelines.idea2video_pipeline import Idea2VideoPipeline
146 pipeline = Idea2VideoPipeline.init_from_config("configs/idea2video_minimax.yaml")
147
148 mock_init.assert_called_once()
149 call_kwargs = mock_init.call_args[1]
150 self.assertEqual(call_kwargs["model_provider"], "openai")
151 self.assertEqual(call_kwargs["base_url"], "https://api.minimax.io/v1")
152 self.assertEqual(call_kwargs["model"], "MiniMax-M3")
153
154 @patch("pipelines.script2video_pipeline.init_chat_model")
155 @patch("pipelines.script2video_pipeline.RenderBackend.from_config")
156 def test_script2video_pipeline_minimax_config(self, mock_backend, mock_init):
157 mock_model = MagicMock()
158 mock_init.return_value = mock_model
159 mock_backend.return_value = MagicMock(image_generator=MagicMock(), video_generator=MagicMock())
160
161 from pipelines.script2video_pipeline import Script2VideoPipeline
162 pipeline = Script2VideoPipeline.init_from_config("configs/script2video_minimax.yaml")
163
164 mock_init.assert_called_once()
165 call_kwargs = mock_init.call_args[1]
166 self.assertEqual(call_kwargs["model_provider"], "openai")
167 self.assertEqual(call_kwargs["base_url"], "https://api.minimax.io/v1")
168 self.assertEqual(call_kwargs["model"], "MiniMax-M3")
169 """Integration: full pipeline init_from_config with default configs."""
170
171 @patch("pipelines.idea2video_pipeline.init_chat_model")
172 @patch("pipelines.idea2video_pipeline.RenderBackend.from_config")
173 def test_existing_openrouter_config_still_works(self, mock_backend, mock_init):
174 mock_model = MagicMock()
175 mock_init.return_value = mock_model
176 mock_backend.return_value = MagicMock(image_generator=MagicMock(), video_generator=MagicMock())
177
178 from pipelines.idea2video_pipeline import Idea2VideoPipeline
179 pipeline = Idea2VideoPipeline.init_from_config("configs/idea2video.yaml")
180
181 mock_init.assert_called_once()
182 call_kwargs = mock_init.call_args[1]
183 self.assertEqual(call_kwargs["model_provider"], "openai")
184 self.assertEqual(call_kwargs["base_url"], "https://openrouter.ai/api/v1")
185
186
187 if __name__ == "__main__":
188 unittest.main()
189
189 lines PYTHON