From ea9d122c885e0116299618c000464937a9a2ac0a Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Mon, 6 Jul 2026 16:36:01 +0800 Subject: [PATCH 1/5] feat: add MiniMax M3 prompt driver --- griptape/drivers/__init__.py | 2 + griptape/drivers/prompt/minimax/__init__.py | 5 ++ .../drivers/prompt/minimax_prompt_driver.py | 16 ++++++ griptape/tokenizers/__init__.py | 2 + griptape/tokenizers/minimax_tokenizer.py | 13 +++++ .../prompt/test_minimax_prompt_driver.py | 56 +++++++++++++++++++ .../unit/tokenizers/test_minimax_tokenizer.py | 16 ++++++ 7 files changed, 110 insertions(+) create mode 100644 griptape/drivers/prompt/minimax/__init__.py create mode 100644 griptape/drivers/prompt/minimax_prompt_driver.py create mode 100644 griptape/tokenizers/minimax_tokenizer.py create mode 100644 tests/unit/drivers/prompt/test_minimax_prompt_driver.py create mode 100644 tests/unit/tokenizers/test_minimax_tokenizer.py diff --git a/griptape/drivers/__init__.py b/griptape/drivers/__init__.py index 4fba24367c..21960cbf64 100644 --- a/griptape/drivers/__init__.py +++ b/griptape/drivers/__init__.py @@ -14,6 +14,7 @@ from .prompt.dummy import DummyPromptDriver from .prompt.ollama import OllamaPromptDriver from .prompt.grok import GrokPromptDriver +from .prompt.minimax import MinimaxPromptDriver from .prompt.griptape_cloud import GriptapeCloudPromptDriver from .prompt.perplexity import PerplexityPromptDriver @@ -215,6 +216,7 @@ "LocalVectorStoreDriver", "MarkdownifyWebScraperDriver", "MarqoVectorStoreDriver", + "MinimaxPromptDriver", "MongoDbAtlasVectorStoreDriver", "NoOpObservabilityDriver", "OllamaEmbeddingDriver", diff --git a/griptape/drivers/prompt/minimax/__init__.py b/griptape/drivers/prompt/minimax/__init__.py new file mode 100644 index 0000000000..e7e8dd5542 --- /dev/null +++ b/griptape/drivers/prompt/minimax/__init__.py @@ -0,0 +1,5 @@ +from griptape.drivers.prompt.minimax_prompt_driver import MinimaxPromptDriver + +__all__ = [ + "MinimaxPromptDriver", +] diff --git a/griptape/drivers/prompt/minimax_prompt_driver.py b/griptape/drivers/prompt/minimax_prompt_driver.py new file mode 100644 index 0000000000..36afedb056 --- /dev/null +++ b/griptape/drivers/prompt/minimax_prompt_driver.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from attrs import Factory, define, field + +from griptape.drivers.prompt.openai import OpenAiChatPromptDriver +from griptape.tokenizers.minimax_tokenizer import MinimaxTokenizer + + +@define +class MinimaxPromptDriver(OpenAiChatPromptDriver): + base_url: str = field(default="https://api.minimax.io/v1", kw_only=True, metadata={"serializable": True}) + tokenizer: MinimaxTokenizer = field( + default=Factory(lambda self: MinimaxTokenizer(model=self.model), takes_self=True), + kw_only=True, + metadata={"serializable": True}, + ) diff --git a/griptape/tokenizers/__init__.py b/griptape/tokenizers/__init__.py index 6fdc7f71c9..94b383ca02 100644 --- a/griptape/tokenizers/__init__.py +++ b/griptape/tokenizers/__init__.py @@ -9,6 +9,7 @@ from griptape.tokenizers.dummy_tokenizer import DummyTokenizer from griptape.tokenizers.amazon_bedrock_tokenizer import AmazonBedrockTokenizer from griptape.tokenizers.grok_tokenizer import GrokTokenizer +from griptape.tokenizers.minimax_tokenizer import MinimaxTokenizer __all__ = [ @@ -20,6 +21,7 @@ "GoogleTokenizer", "GrokTokenizer", "HuggingFaceTokenizer", + "MinimaxTokenizer", "OpenAiTokenizer", "SimpleTokenizer", "VoyageAiTokenizer", diff --git a/griptape/tokenizers/minimax_tokenizer.py b/griptape/tokenizers/minimax_tokenizer.py new file mode 100644 index 0000000000..5b9f522d54 --- /dev/null +++ b/griptape/tokenizers/minimax_tokenizer.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from attrs import define + +from griptape.tokenizers import OpenAiTokenizer + + +@define() +class MinimaxTokenizer(OpenAiTokenizer): + # MiniMax exposes an OpenAI-compatible API without a dedicated tokenization + # endpoint, so token counting reuses the OpenAI tiktoken logic. + # https://www.minimax.io/platform/document/text_api + MODEL_PREFIXES_TO_MAX_INPUT_TOKENS = {"MiniMax": 1000000} diff --git a/tests/unit/drivers/prompt/test_minimax_prompt_driver.py b/tests/unit/drivers/prompt/test_minimax_prompt_driver.py new file mode 100644 index 0000000000..fefe590da7 --- /dev/null +++ b/tests/unit/drivers/prompt/test_minimax_prompt_driver.py @@ -0,0 +1,56 @@ +from griptape.drivers.prompt.minimax import MinimaxPromptDriver +from tests.unit.drivers.prompt.test_openai_chat_prompt_driver import TestOpenAiChatPromptDriverFixtureMixin + + +class TestMinimaxPromptDriver(TestOpenAiChatPromptDriverFixtureMixin): + def test_init(self): + assert MinimaxPromptDriver(api_key="foo", model="MiniMax-M3") + + def test_default_base_url(self): + driver = MinimaxPromptDriver(api_key="foo", model="MiniMax-M3") + + assert driver.base_url == "https://api.minimax.io/v1" + assert driver.tokenizer.model == "MiniMax-M3" + + def test_to_dict(self): + # Given + driver = MinimaxPromptDriver(model="MiniMax-M3") + + # When + result = driver.to_dict() + + # Then + assert result == { + "type": "MinimaxPromptDriver", + "audio": {"format": "pcm16", "voice": "alloy"}, + "base_url": "https://api.minimax.io/v1", + "extra_params": {}, + "max_tokens": None, + "model": "MiniMax-M3", + "modalities": [], + "organization": None, + "parallel_tool_calls": True, + "reasoning_effort": "medium", + "response_format": None, + "seed": None, + "stream": False, + "structured_output_strategy": "native", + "temperature": 0.1, + "tokenizer": { + "type": "MinimaxTokenizer", + "model": "MiniMax-M3", + "stop_sequences": [], + }, + "use_native_tools": True, + "user": "", + } + + def test_from_dict(self): + # Given + driver = MinimaxPromptDriver(model="MiniMax-M3") + + # When + result = MinimaxPromptDriver.from_dict(driver.to_dict()) + + # Then + assert result.to_dict() == driver.to_dict() diff --git a/tests/unit/tokenizers/test_minimax_tokenizer.py b/tests/unit/tokenizers/test_minimax_tokenizer.py new file mode 100644 index 0000000000..02dc52a345 --- /dev/null +++ b/tests/unit/tokenizers/test_minimax_tokenizer.py @@ -0,0 +1,16 @@ +from griptape.tokenizers import MinimaxTokenizer + + +class TestMinimaxTokenizer: + def test_init(self): + assert MinimaxTokenizer(model="MiniMax-M3") + + def test_count_tokens(self): + tokenizer = MinimaxTokenizer(model="MiniMax-M3") + + assert tokenizer.count_tokens("foo bar huzzah") == 5 + + def test_max_input_tokens(self): + tokenizer = MinimaxTokenizer(model="MiniMax-M3") + + assert tokenizer.max_input_tokens == 1000000 - MinimaxTokenizer.TOKEN_OFFSET From de59ec763d23df2eac3b4face366550e4139372d Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Sun, 12 Jul 2026 14:32:52 +0800 Subject: [PATCH 2/5] fix: use model-specific MiniMax context windows --- griptape/tokenizers/minimax_tokenizer.py | 11 +++++++---- .../drivers/prompt/test_minimax_prompt_driver.py | 16 ++++++++++++++-- tests/unit/tokenizers/test_minimax_tokenizer.py | 15 ++++++++++++--- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/griptape/tokenizers/minimax_tokenizer.py b/griptape/tokenizers/minimax_tokenizer.py index 5b9f522d54..bc6955f0de 100644 --- a/griptape/tokenizers/minimax_tokenizer.py +++ b/griptape/tokenizers/minimax_tokenizer.py @@ -7,7 +7,10 @@ @define() class MinimaxTokenizer(OpenAiTokenizer): - # MiniMax exposes an OpenAI-compatible API without a dedicated tokenization - # endpoint, so token counting reuses the OpenAI tiktoken logic. - # https://www.minimax.io/platform/document/text_api - MODEL_PREFIXES_TO_MAX_INPUT_TOKENS = {"MiniMax": 1000000} + # MiniMax does not expose a public token-counting endpoint, so token counting + # uses local tiktoken logic. + # https://platform.minimax.io/docs/api-reference/api-overview + MODEL_PREFIXES_TO_MAX_INPUT_TOKENS = { + "MiniMax-M3": 1_000_000, + "MiniMax-M2.7": 204_800, + } diff --git a/tests/unit/drivers/prompt/test_minimax_prompt_driver.py b/tests/unit/drivers/prompt/test_minimax_prompt_driver.py index fefe590da7..c7c1a31c98 100644 --- a/tests/unit/drivers/prompt/test_minimax_prompt_driver.py +++ b/tests/unit/drivers/prompt/test_minimax_prompt_driver.py @@ -1,10 +1,13 @@ +import pytest + from griptape.drivers.prompt.minimax import MinimaxPromptDriver from tests.unit.drivers.prompt.test_openai_chat_prompt_driver import TestOpenAiChatPromptDriverFixtureMixin class TestMinimaxPromptDriver(TestOpenAiChatPromptDriverFixtureMixin): - def test_init(self): - assert MinimaxPromptDriver(api_key="foo", model="MiniMax-M3") + @pytest.mark.parametrize("model", ["MiniMax-M3", "MiniMax-M2.7"]) + def test_init(self, model): + assert MinimaxPromptDriver(api_key="foo", model=model) def test_default_base_url(self): driver = MinimaxPromptDriver(api_key="foo", model="MiniMax-M3") @@ -12,6 +15,15 @@ def test_default_base_url(self): assert driver.base_url == "https://api.minimax.io/v1" assert driver.tokenizer.model == "MiniMax-M3" + def test_custom_base_url(self): + driver = MinimaxPromptDriver( + api_key="foo", + model="MiniMax-M3", + base_url="https://api.minimaxi.com/v1", + ) + + assert driver.base_url == "https://api.minimaxi.com/v1" + def test_to_dict(self): # Given driver = MinimaxPromptDriver(model="MiniMax-M3") diff --git a/tests/unit/tokenizers/test_minimax_tokenizer.py b/tests/unit/tokenizers/test_minimax_tokenizer.py index 02dc52a345..04b842d160 100644 --- a/tests/unit/tokenizers/test_minimax_tokenizer.py +++ b/tests/unit/tokenizers/test_minimax_tokenizer.py @@ -1,3 +1,5 @@ +import pytest + from griptape.tokenizers import MinimaxTokenizer @@ -10,7 +12,14 @@ def test_count_tokens(self): assert tokenizer.count_tokens("foo bar huzzah") == 5 - def test_max_input_tokens(self): - tokenizer = MinimaxTokenizer(model="MiniMax-M3") + @pytest.mark.parametrize( + ("model", "context_window"), + [ + ("MiniMax-M3", 1_000_000), + ("MiniMax-M2.7", 204_800), + ], + ) + def test_max_input_tokens(self, model, context_window): + tokenizer = MinimaxTokenizer(model=model) - assert tokenizer.max_input_tokens == 1000000 - MinimaxTokenizer.TOKEN_OFFSET + assert tokenizer.max_input_tokens == context_window - MinimaxTokenizer.TOKEN_OFFSET From 5a047a6a1ea4f39da776bdf1c0b54244330f45b2 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:21:39 +0800 Subject: [PATCH 3/5] docs: cover MiniMax API endpoint options --- .../drivers/prompt-drivers.md | 34 +++++++++++++++++++ griptape/tokenizers/minimax_tokenizer.py | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/docs/griptape-framework/drivers/prompt-drivers.md b/docs/griptape-framework/drivers/prompt-drivers.md index dfe3844fcc..6e0504faac 100644 --- a/docs/griptape-framework/drivers/prompt-drivers.md +++ b/docs/griptape-framework/drivers/prompt-drivers.md @@ -370,6 +370,40 @@ The [GrokPromptDriver](../../reference/griptape/drivers/prompt/grok_prompt_drive --8<-- "docs/griptape-framework/drivers/logs/prompt_drivers_grok.txt" ``` +### MiniMax + +The [MinimaxPromptDriver](../../reference/griptape/drivers/prompt/minimax_prompt_driver.md) connects to the [MiniMax OpenAI-compatible API](https://platform.minimax.io/docs/api-reference/text-openai-api). It supports `MiniMax-M3` and `MiniMax-M2.7` and uses the global endpoint by default: + +```python +import os + +from griptape.drivers.prompt.minimax import MinimaxPromptDriver + +driver = MinimaxPromptDriver(api_key=os.environ["MINIMAX_API_KEY"], model="MiniMax-M3") +``` + +For the China endpoint, set `base_url="https://api.minimaxi.com/v1"`. + +MiniMax also provides an [Anthropic-compatible API](https://platform.minimax.io/docs/api-reference/text-anthropic-api). Configure an `AnthropicPromptDriver` with an Anthropic client and the MiniMax tokenizer: + +```python +import os + +from anthropic import Anthropic + +from griptape.drivers.prompt.anthropic import AnthropicPromptDriver +from griptape.tokenizers import MinimaxTokenizer + +model = "MiniMax-M3" +driver = AnthropicPromptDriver( + client=Anthropic(api_key=os.environ["MINIMAX_API_KEY"], base_url="https://api.minimax.io/anthropic"), + model=model, + tokenizer=MinimaxTokenizer(model=model), +) +``` + +This configuration requires the `drivers-prompt-anthropic` extra. For the China endpoint, set the Anthropic client's `base_url` to `https://api.minimaxi.com/anthropic`. + ### Perplexity The [PerplexityPromptDriver](../../reference/griptape/drivers/prompt/perplexity_prompt_driver.md) uses [Perplexity Sonar's chat completion](https://docs.perplexity.ai/api-reference/chat-completions) endpoint. diff --git a/griptape/tokenizers/minimax_tokenizer.py b/griptape/tokenizers/minimax_tokenizer.py index bc6955f0de..173616d882 100644 --- a/griptape/tokenizers/minimax_tokenizer.py +++ b/griptape/tokenizers/minimax_tokenizer.py @@ -7,7 +7,7 @@ @define() class MinimaxTokenizer(OpenAiTokenizer): - # MiniMax does not expose a public token-counting endpoint, so token counting + # The OpenAI-compatible API does not expose token counting, so this tokenizer # uses local tiktoken logic. # https://platform.minimax.io/docs/api-reference/api-overview MODEL_PREFIXES_TO_MAX_INPUT_TOKENS = { From 336f322ef44746f633f0a623630428f389acc844 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Tue, 14 Jul 2026 17:25:55 +0800 Subject: [PATCH 4/5] fix: add MiniMax output token limits --- griptape/tokenizers/minimax_tokenizer.py | 4 ++++ tests/unit/tokenizers/test_minimax_tokenizer.py | 9 +++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/griptape/tokenizers/minimax_tokenizer.py b/griptape/tokenizers/minimax_tokenizer.py index 173616d882..5eaba266a9 100644 --- a/griptape/tokenizers/minimax_tokenizer.py +++ b/griptape/tokenizers/minimax_tokenizer.py @@ -14,3 +14,7 @@ class MinimaxTokenizer(OpenAiTokenizer): "MiniMax-M3": 1_000_000, "MiniMax-M2.7": 204_800, } + MODEL_PREFIXES_TO_MAX_OUTPUT_TOKENS = { + "MiniMax-M3": 524_288, + "MiniMax-M2.7": 204_800, + } diff --git a/tests/unit/tokenizers/test_minimax_tokenizer.py b/tests/unit/tokenizers/test_minimax_tokenizer.py index 04b842d160..07f039344a 100644 --- a/tests/unit/tokenizers/test_minimax_tokenizer.py +++ b/tests/unit/tokenizers/test_minimax_tokenizer.py @@ -13,13 +13,14 @@ def test_count_tokens(self): assert tokenizer.count_tokens("foo bar huzzah") == 5 @pytest.mark.parametrize( - ("model", "context_window"), + ("model", "context_window", "max_output_tokens"), [ - ("MiniMax-M3", 1_000_000), - ("MiniMax-M2.7", 204_800), + ("MiniMax-M3", 1_000_000, 524_288), + ("MiniMax-M2.7", 204_800, 204_800), ], ) - def test_max_input_tokens(self, model, context_window): + def test_token_limits(self, model, context_window, max_output_tokens): tokenizer = MinimaxTokenizer(model=model) assert tokenizer.max_input_tokens == context_window - MinimaxTokenizer.TOKEN_OFFSET + assert tokenizer.max_output_tokens == max_output_tokens From 0d2e2ff81773c82090632fba842016eeb9024d36 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Thu, 16 Jul 2026 17:37:19 +0800 Subject: [PATCH 5/5] docs: document MiniMax thinking controls --- docs/griptape-framework/drivers/prompt-drivers.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/griptape-framework/drivers/prompt-drivers.md b/docs/griptape-framework/drivers/prompt-drivers.md index 6e0504faac..5b6868732d 100644 --- a/docs/griptape-framework/drivers/prompt-drivers.md +++ b/docs/griptape-framework/drivers/prompt-drivers.md @@ -382,6 +382,20 @@ from griptape.drivers.prompt.minimax import MinimaxPromptDriver driver = MinimaxPromptDriver(api_key=os.environ["MINIMAX_API_KEY"], model="MiniMax-M3") ``` +`MiniMax-M3` enables thinking by default. Pass MiniMax-specific request fields through the OpenAI SDK's `extra_body` parameter to control it: + +```python +driver = MinimaxPromptDriver( + api_key=os.environ["MINIMAX_API_KEY"], + model="MiniMax-M3", + extra_params={"extra_body": {"thinking": {"type": "disabled"}}}, +) +``` + +Use `"adaptive"` instead of `"disabled"` to explicitly enable `MiniMax-M3` thinking. `MiniMax-M2.7` always uses thinking, even when `"disabled"` is requested. + +This driver supports text and image prompt content for `MiniMax-M3`; `MiniMax-M2.7` supports text only. The MiniMax API also accepts video input for `MiniMax-M3`, but Griptape prompt content does not currently expose video input. + For the China endpoint, set `base_url="https://api.minimaxi.com/v1"`. MiniMax also provides an [Anthropic-compatible API](https://platform.minimax.io/docs/api-reference/text-anthropic-api). Configure an `AnthropicPromptDriver` with an Anthropic client and the MiniMax tokenizer: