feat: add MiniMax as embedding and LLM (genie) provider with M3 as default - #530
feat: add MiniMax as embedding and LLM (genie) provider with M3 as default#530octo-patch wants to merge 5 commits into
Conversation
Add MiniMax support with two new provider classes: - MiniMaxEmbeddings: native MiniMax embedding API (embo-01 model, 1536 dimensions) with db/query type distinction, batching, and async support - MiniMaxGenie: OpenAI-compatible chat completions for MiniMax M2.7/M2.5 models with temperature clamping, think-tag stripping, and JSON structured output Both providers are registered in the embeddings registry and exported from the main package. Includes 61 unit tests and 6 integration tests.
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request adds MiniMax as a first-class provider for both embeddings and LLM inference within the Chonkie library. It includes the necessary classes, registration, and testing to fully integrate MiniMax's capabilities, providing users with a new option for their AI applications. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
Add MiniMaxEmbeddings using MiniMax's native embedding API (embo-01, 1536 dims) with db/query type distinction for asymmetric retrieval. Also add MiniMaxGenie for LLM inference via OpenAI-compatible API (M2.5, M2.5-highspeed) with temperature clamping and think-tag stripping. - MiniMaxEmbeddings: direct httpx REST client, batch support, async support - MiniMaxGenie: OpenAI-compat SDK, JSON mode, structured output - Registry: minimax:// provider URI, embo-* pattern matching - 58 unit tests (31 embedding + 27 genie), 6 integration tests - Documentation and overview page updates
There was a problem hiding this comment.
Code Review
This pull request introduces support for MiniMax as a provider for both embeddings (MiniMaxEmbeddings) and LLM inference (MiniMaxGenie). The changes include the implementation of the two new classes, their registration, and comprehensive unit and integration tests. The code is well-structured and follows existing patterns in the repository. I have one suggestion for MiniMaxEmbeddings to improve the performance of asynchronous operations by reusing the httpx.AsyncClient instance. Otherwise, the implementation looks great.
| List of embedding vectors (each a list of floats). | ||
|
|
||
| Raises: | ||
| ValueError: If the API returns an error. | ||
|
|
||
| """ | ||
| async with httpx.AsyncClient(timeout=self._timeout) as client: | ||
| response = await client.post( | ||
| _MINIMAX_EMBEDDINGS_URL, | ||
| headers={ | ||
| "Authorization": f"Bearer {self.api_key}", | ||
| "Content-Type": "application/json", | ||
| }, | ||
| json={ | ||
| "model": self.model, | ||
| "texts": texts, | ||
| "type": self.embedding_type, | ||
| }, | ||
| ) | ||
| response.raise_for_status() | ||
| data = response.json() | ||
|
|
||
| base_resp = data.get("base_resp", {}) | ||
| if base_resp.get("status_code", 0) != 0: | ||
| raise ValueError( | ||
| f"MiniMax API error: {base_resp.get('status_msg', 'unknown error')}" |
There was a problem hiding this comment.
For better performance, the httpx.AsyncClient should be instantiated once in the __init__ method and reused across all asynchronous calls, rather than being created for each call within _acall_api. This avoids the overhead of setting up a new client and connection pool for every API request, which is especially important for batch operations.
This pattern is already used for the synchronous httpx.Client and is also consistent with how the MiniMaxGenie class handles its AsyncOpenAI client.
You should add self._async_client = httpx.AsyncClient(timeout=self._timeout) to __init__ and then use self._async_client here.
response = await self._async_client.post(
_MINIMAX_EMBEDDINGS_URL,
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
},
json={
"model": self.model,
"texts": texts,
"type": self.embedding_type,
},
)
response.raise_for_status()
data = response.json()
base_resp = data.get("base_resp", {})
if base_resp.get("status_code", 0) != 0:
raise ValueError(
f"MiniMax API error: {base_resp.get('status_msg', 'unknown error')}"
)
vectors = data.get("vectors")
if vectors is None:
raise ValueError("MiniMax API response missing 'vectors' field")
return vectorsMiniMax does not have a public embedding API. Remove the minimax-embeddings doc page and the MiniMax entry from the embeddings overview.
- Add MiniMax-M3 to model list and set as default - Keep MiniMax-M2.7 and MiniMax-M2.7-highspeed - Remove older models (M2.5/M2.5-highspeed) - Update related tests
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR introduces MiniMax as a new embeddings and LLM provider: adds MiniMaxEmbeddings (sync/async, batching, retries, tokenizer/dimension, resource management) and MiniMaxGenie (sync/async OpenAI-compatible generation, JSON schema injection, think-tag stripping, retries), wires them into exports/registry, and adds comprehensive tests and a pyproject optional extra. ChangesMiniMax Provider Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/embeddings/test_minimax_embeddings.py (1)
1-371:⚠️ Potential issue | 🟠 MajorAdd test coverage for MiniMaxEmbeddings async methods and tenacity retry behavior
MiniMaxEmbeddingsimplementsaembed/aembed_batchand appliestenacityretry to both_call_apiand_acall_api(retrying onhttpx.HTTPStatusError,httpx.ConnectError,httpx.TimeoutException), buttests/embeddings/test_minimax_embeddings.pyonly covers synchronousembed/embed_batchpaths and doesn’t assert any retry behavior. Addpytest.mark.asynciounit tests foraembed/aembed_batch, plus tests that force retryable exceptions and verify the number of attempts / eventual success.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/embeddings/test_minimax_embeddings.py` around lines 1 - 371, Add async unit tests that cover MiniMaxEmbeddings.aembed and aembed_batch and verify tenacity retry behavior: create pytest.mark.asyncio tests that use a mocked async httpx client (patch MiniMaxEmbeddings to use a mock _client) and call aembed/aembed_batch; assert they return proper numpy arrays like embed/embed_batch; also add tests that make the mock client's async post raise httpx.HTTPStatusError, httpx.ConnectError, or httpx.TimeoutException for the first N-1 calls and succeed on the Nth call to exercise retries on _acall_api/_call_api, asserting the number of attempts (mock post call_count or side_effect sequence) and final success. Ensure tests reference MiniMaxEmbeddings.aembed, MiniMaxEmbeddings.aembed_batch, and the retry-wrapped internal methods (_acall_api/_call_api) so reviewers can locate the code.
🧹 Nitpick comments (6)
tests/embeddings/test_minimax_embeddings.py (1)
38-46: 💤 Low valueOptional: Simplify the fixture by removing redundant
_clientassignment.Line 45 replaces
_clientafter construction, but thepatchcontext on line 42 already ensuresmock_clientis returned whenMiniMaxEmbeddings.__init__callshttpx.Client(timeout=...). The manual replacement is redundant.♻️ Simplify by removing line 45
`@pytest.fixture` def embedding_model(mock_api_response): """Create a MiniMaxEmbeddings instance with a mocked httpx client.""" mock_client = MagicMock() mock_client.post.return_value = mock_api_response with patch("httpx.Client", return_value=mock_client): model = MiniMaxEmbeddings(api_key="test-minimax-key") - # Replace the real client with our mock - model._client = mock_client return model🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/embeddings/test_minimax_embeddings.py` around lines 38 - 46, The fixture embedding_model currently patches httpx.Client to return mock_client, so when MiniMaxEmbeddings(api_key="test-minimax-key") is constructed it already receives the mocked client; remove the redundant manual assignment model._client = mock_client (referencing the embedding_model function, mock_client variable, and MiniMaxEmbeddings class) to simplify the fixture and rely solely on the patch context.tests/genie/test_minimax_genie.py (1)
19-26: ⚡ Quick winConsider adding functional tests for async methods.
The test suite verifies that
agenerate,agenerate_json, andgenerate_json_batchmethods exist but does not functionally test them. Adding tests for these methods would improve coverage and catch potential issues in async code paths.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/genie/test_minimax_genie.py` around lines 19 - 26, Add functional async tests that exercise MiniMaxGenie's asynchronous code paths: create async test functions (using pytest.mark.asyncio or asyncio.run) that instantiate MiniMaxGenie and call agenerate with a simple prompt and assert the returned result shape/type, call agenerate_json and assert the JSON-parsed structure and expected keys, and call generate_json_batch with a small batch input and assert each item in the returned batch has the expected structure; reference the methods agenerate, agenerate_json, and generate_json_batch on the MiniMaxGenie class and include minimal assertions (types/keys/status) to verify correct async behavior.src/chonkie/embeddings/minimax.py (1)
168-168: ⚖️ Poor tradeoffAsync method creates new client per call, unlike sync pattern.
The async
_acall_apicreates a newAsyncClientfor each call (line 168), while the sync version reusesself._client. This is safer for resource management but incurs connection overhead per call. For batch operations viaaembed_batch, this means N/batch_size new connections.Consider storing
self._async_clientand implementing async cleanup, similar to the sync pattern, for better performance on repeated async calls.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/chonkie/embeddings/minimax.py` at line 168, The async method _acall_api currently creates a new httpx.AsyncClient per call which causes connection overhead (especially in aembed_batch); change the implementation to reuse a single AsyncClient by adding self._async_client (parallel to the sync self._client), initialize it lazily, use it inside _acall_api instead of creating a new client each call, and add an async cleanup method (e.g., async close_async_client or implement __aenter__/__aexit__ on the class) to close self._async_client when the service is shut down or disposed to match the sync resource-management pattern.src/chonkie/genie/minimax.py (3)
100-102: 💤 Low valueBoth sync and async clients created but async client may not be cleaned up.
Lines 101-102 create both
self.clientandself.async_client. Unlike the embeddings class, these clients are never explicitly closed. OpenAI clients typically handle cleanup internally, but for consistency and to avoid potential resource leaks with long-lived instances, consider adding a close method.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/chonkie/genie/minimax.py` around lines 100 - 102, The code creates both OpenAI and AsyncOpenAI clients (self.client and self.async_client) but never closes them; add cleanup by implementing a close method on the class in minimax.py that calls self.client.close() if present and asynchronously closes the async client (await self.async_client.aclose() or await self.async_client.close() depending on the library) inside a try/except to handle missing attributes, and also provide an async_close/async def aclose method (or __aenter__/__aexit__ handlers) so callers can properly await cleanup of AsyncOpenAI; reference the OpenAI, AsyncOpenAI, self.client, and self.async_client symbols when adding these methods.
95-96: 💤 Low valueTemperature silently clamped without user warning.
The temperature is clamped to
[0.01, 1.0](line 96), but if a user passestemperature=0, they silently get0.01. This could surprise users expecting deterministic output. Consider logging a warning when clamping occurs.Also, the docstring says
(0, 1]but the code enforces[0.01, 1.0]— a minor documentation inconsistency.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/chonkie/genie/minimax.py` around lines 95 - 96, The constructor currently silently clamps the temperature via self.temperature = max(0.01, min(temperature, 1.0)); update this to detect when the incoming temperature is outside the allowed range and emit a warning (e.g., using the module logger or warnings.warn) that reports the original value and the clamped value, then assign the clamped value to self.temperature; also make the docstring for the class or __init__ match the enforced range (change "(0, 1]" to "[0.01, 1.0]" or adjust the clamp to match the documented "(0, 1]" behavior) so code and docs are consistent.
35-36: 💤 Low valueRegex
re.DOTALLflag is redundant.The pattern
[\s\S]*?already matches any character including newlines, making there.DOTALLflag redundant. This is harmless but could be simplified.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/chonkie/genie/minimax.py` around lines 35 - 36, The compiled regex _THINK_TAG_RE uses [\s\S]*? which already matches newlines, so remove the redundant re.DOTALL flag from the re.compile call; update the call that defines _THINK_TAG_RE to just re.compile(r"<think>[\s\S]*?</think>\s*") (leaving the pattern as-is) so behavior is unchanged but the flag is not used.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/chonkie/embeddings/minimax.py`:
- Around line 99-107: The retry decorator currently retries all
httpx.HTTPStatusError instances; change it to retry only on transient HTTP
errors by replacing retry_if_exception_type((httpx.HTTPStatusError,
httpx.ConnectError, httpx.TimeoutException)) with a custom predicate (use
tenacity.retry_if_exception with a lambda or function) that: for
httpx.HTTPStatusError inspects exc.response.status_code and returns True only
for 429 or 5xx (>=500), and for httpx.ConnectError or httpx.TimeoutException
returns True; keep stop_after_attempt and wait_exponential as-is so the
backoff/retry targets only transient (429/5xx) HTTP failures while still
retrying connection/timeout errors.
- Line 95: The MiniMaxEmbeddings class creates an httpx.Client instance stored
as self._client in the __init__ method but never closes it, causing resource
leaks. Add a __del__ method to the MiniMaxEmbeddings class that closes the
self._client when the instance is garbage collected, or alternatively implement
context manager methods (__enter__ and __exit__) to properly manage the client's
lifecycle. Ensure the client is explicitly closed by calling
self._client.close() to release any underlying network connections and
resources.
In `@tests/embeddings/test_minimax_embeddings.py`:
- Around line 269-280: The tests currently use pytest.raises(Exception) which is
too broad; update the assertions to expect the specific ValueError raised by
MiniMaxEmbeddings._call_api. Replace pytest.raises(Exception) with
pytest.raises(ValueError) in test_api_error_response (the block calling
embedding_model.embed("test") with the mocked API error response) and also in
the analogous test covering the missing "vectors" field (the test that calls
embedding_model.embed with a response lacking "vectors"). This ensures the tests
assert the correct exception type from MiniMaxEmbeddings._call_api.
---
Outside diff comments:
In `@tests/embeddings/test_minimax_embeddings.py`:
- Around line 1-371: Add async unit tests that cover MiniMaxEmbeddings.aembed
and aembed_batch and verify tenacity retry behavior: create pytest.mark.asyncio
tests that use a mocked async httpx client (patch MiniMaxEmbeddings to use a
mock _client) and call aembed/aembed_batch; assert they return proper numpy
arrays like embed/embed_batch; also add tests that make the mock client's async
post raise httpx.HTTPStatusError, httpx.ConnectError, or httpx.TimeoutException
for the first N-1 calls and succeed on the Nth call to exercise retries on
_acall_api/_call_api, asserting the number of attempts (mock post call_count or
side_effect sequence) and final success. Ensure tests reference
MiniMaxEmbeddings.aembed, MiniMaxEmbeddings.aembed_batch, and the retry-wrapped
internal methods (_acall_api/_call_api) so reviewers can locate the code.
---
Nitpick comments:
In `@src/chonkie/embeddings/minimax.py`:
- Line 168: The async method _acall_api currently creates a new
httpx.AsyncClient per call which causes connection overhead (especially in
aembed_batch); change the implementation to reuse a single AsyncClient by adding
self._async_client (parallel to the sync self._client), initialize it lazily,
use it inside _acall_api instead of creating a new client each call, and add an
async cleanup method (e.g., async close_async_client or implement
__aenter__/__aexit__ on the class) to close self._async_client when the service
is shut down or disposed to match the sync resource-management pattern.
In `@src/chonkie/genie/minimax.py`:
- Around line 100-102: The code creates both OpenAI and AsyncOpenAI clients
(self.client and self.async_client) but never closes them; add cleanup by
implementing a close method on the class in minimax.py that calls
self.client.close() if present and asynchronously closes the async client (await
self.async_client.aclose() or await self.async_client.close() depending on the
library) inside a try/except to handle missing attributes, and also provide an
async_close/async def aclose method (or __aenter__/__aexit__ handlers) so
callers can properly await cleanup of AsyncOpenAI; reference the OpenAI,
AsyncOpenAI, self.client, and self.async_client symbols when adding these
methods.
- Around line 95-96: The constructor currently silently clamps the temperature
via self.temperature = max(0.01, min(temperature, 1.0)); update this to detect
when the incoming temperature is outside the allowed range and emit a warning
(e.g., using the module logger or warnings.warn) that reports the original value
and the clamped value, then assign the clamped value to self.temperature; also
make the docstring for the class or __init__ match the enforced range (change
"(0, 1]" to "[0.01, 1.0]" or adjust the clamp to match the documented "(0, 1]"
behavior) so code and docs are consistent.
- Around line 35-36: The compiled regex _THINK_TAG_RE uses [\s\S]*? which
already matches newlines, so remove the redundant re.DOTALL flag from the
re.compile call; update the call that defines _THINK_TAG_RE to just
re.compile(r"<think>[\s\S]*?</think>\s*") (leaving the pattern as-is) so
behavior is unchanged but the flag is not used.
In `@tests/embeddings/test_minimax_embeddings.py`:
- Around line 38-46: The fixture embedding_model currently patches httpx.Client
to return mock_client, so when MiniMaxEmbeddings(api_key="test-minimax-key") is
constructed it already receives the mocked client; remove the redundant manual
assignment model._client = mock_client (referencing the embedding_model
function, mock_client variable, and MiniMaxEmbeddings class) to simplify the
fixture and rely solely on the patch context.
In `@tests/genie/test_minimax_genie.py`:
- Around line 19-26: Add functional async tests that exercise MiniMaxGenie's
asynchronous code paths: create async test functions (using pytest.mark.asyncio
or asyncio.run) that instantiate MiniMaxGenie and call agenerate with a simple
prompt and assert the returned result shape/type, call agenerate_json and assert
the JSON-parsed structure and expected keys, and call generate_json_batch with a
small batch input and assert each item in the returned batch has the expected
structure; reference the methods agenerate, agenerate_json, and
generate_json_batch on the MiniMaxGenie class and include minimal assertions
(types/keys/status) to verify correct async behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5539d8fe-9e25-4673-aa26-1480e224c023
📒 Files selected for processing (9)
pyproject.tomlsrc/chonkie/__init__.pysrc/chonkie/embeddings/__init__.pysrc/chonkie/embeddings/minimax.pysrc/chonkie/embeddings/registry.pysrc/chonkie/genie/__init__.pysrc/chonkie/genie/minimax.pytests/embeddings/test_minimax_embeddings.pytests/genie/test_minimax_genie.py
- Add close()/__enter__/__exit__/__del__ to release httpx.Client pool - Restrict retry to transient errors only (ConnectError, TimeoutException, 5xx HTTPStatusError) — no more retrying 4xx like 401/422 - Tighten test_api_error_response and test_missing_vectors_field to pytest.raises(ValueError, match=...) instead of generic Exception Co-Authored-By: Octopus <liyuan851277048@icloud.com>
Summary
Add MiniMax as a first-class embedding and LLM provider in Chonkie, with the latest MiniMax-M3 as the default LLM model.
MiniMaxEmbeddings
httpxREST calls"db"(storage) and"query"(search) embedding types for asymmetric retrievalEmbeddingsRegistrywithminimax://provider URI andembo-*pattern matchingMiniMaxGenie
https://api.minimax.io/v1)MiniMax-M3features a 512K context window, up to 128K output, and image input support<think>tag stripping for reasoning modelsFiles Changed
src/chonkie/embeddings/minimax.py— MiniMaxEmbeddings with native API clientsrc/chonkie/genie/minimax.py— MiniMaxGenie with OpenAI-compat SDK (M3 default, keeps M2.7)src/chonkie/embeddings/registry.py— Registry entries for minimax providersrc/chonkie/embeddings/__init__.py,src/chonkie/genie/__init__.py,src/chonkie/__init__.py— Exportspyproject.toml—minimaxoptional dependency grouptests/embeddings/test_minimax_embeddings.py— Unit + integration teststests/genie/test_minimax_genie.py— Unit + integration tests (updated for M3)Test plan
Summary by CodeRabbit
New Features
Tests
Chores