Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions docs/griptape-framework/drivers/prompt-drivers.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,54 @@ 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")
```

`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:

```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.
Expand Down
2 changes: 2 additions & 0 deletions griptape/drivers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -215,6 +216,7 @@
"LocalVectorStoreDriver",
"MarkdownifyWebScraperDriver",
"MarqoVectorStoreDriver",
"MinimaxPromptDriver",
"MongoDbAtlasVectorStoreDriver",
"NoOpObservabilityDriver",
"OllamaEmbeddingDriver",
Expand Down
5 changes: 5 additions & 0 deletions griptape/drivers/prompt/minimax/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from griptape.drivers.prompt.minimax_prompt_driver import MinimaxPromptDriver

__all__ = [
"MinimaxPromptDriver",
]
16 changes: 16 additions & 0 deletions griptape/drivers/prompt/minimax_prompt_driver.py
Original file line number Diff line number Diff line change
@@ -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},
)
2 changes: 2 additions & 0 deletions griptape/tokenizers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__ = [
Expand All @@ -20,6 +21,7 @@
"GoogleTokenizer",
"GrokTokenizer",
"HuggingFaceTokenizer",
"MinimaxTokenizer",
"OpenAiTokenizer",
"SimpleTokenizer",
"VoyageAiTokenizer",
Expand Down
20 changes: 20 additions & 0 deletions griptape/tokenizers/minimax_tokenizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from __future__ import annotations

from attrs import define

from griptape.tokenizers import OpenAiTokenizer


@define()
class MinimaxTokenizer(OpenAiTokenizer):
# 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 = {
"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,
}
68 changes: 68 additions & 0 deletions tests/unit/drivers/prompt/test_minimax_prompt_driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import pytest

from griptape.drivers.prompt.minimax import MinimaxPromptDriver
from tests.unit.drivers.prompt.test_openai_chat_prompt_driver import TestOpenAiChatPromptDriverFixtureMixin


class TestMinimaxPromptDriver(TestOpenAiChatPromptDriverFixtureMixin):
@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")

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")

# 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()
26 changes: 26 additions & 0 deletions tests/unit/tokenizers/test_minimax_tokenizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import pytest

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

@pytest.mark.parametrize(
("model", "context_window", "max_output_tokens"),
[
("MiniMax-M3", 1_000_000, 524_288),
("MiniMax-M2.7", 204_800, 204_800),
],
)
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
Loading