diff --git a/README.md b/README.md index 172210af7f..a7624cc2ae 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Choose **one** of: - Add your OPENAI_API_KEY in .env - Custom model: - See [docs](https://gpt-engineer.readthedocs.io/en/latest/open_models.html), supports local model, azure, etc. +- For MiniMax models: `export MINIMAX_API_KEY=[your api key]` Check the [Windows README](./WINDOWS_README.md) for Windows usage. @@ -102,7 +103,26 @@ E.g. `gpte projects/example-vision gpt-4-vision-preview --prompt_file prompt/tex ### Open source, local and alternative models -By default, gpt-engineer supports OpenAI Models via the OpenAI API or Azure OpenAI API, as well as Anthropic models. +By default, gpt-engineer supports OpenAI Models via the OpenAI API or Azure OpenAI API, as well as Anthropic models and [MiniMax](https://platform.minimax.io) models. + +#### MiniMax Models + +To use MiniMax models, set the `MINIMAX_API_KEY` environment variable and pass a MiniMax model name: + +```bash +export MINIMAX_API_KEY=your-minimax-api-key +gpte my-project -m MiniMax-M2.7 +``` + +Supported MiniMax models: +| Model | Description | +|-------|-------------| +| `MiniMax-M2.7` | Latest flagship model with enhanced reasoning and coding. | +| `MiniMax-M2.7-highspeed` | High-speed version of M2.7 for low-latency scenarios. | +| `MiniMax-M2.5` | Peak Performance. Ultimate Value. 204K context window. | +| `MiniMax-M2.5-highspeed` | Same performance, faster and more agile. 204K context window. | + +You can also set a custom API base URL via `MINIMAX_API_BASE` (defaults to `https://api.minimax.io/v1`). For users in China, set it to `https://api.minimaxi.com/v1`. With a little extra setup, you can also run with open source models like WizardCoder. See the [documentation](https://gpt-engineer.readthedocs.io/en/latest/open_models.html) for example instructions. diff --git a/gpt_engineer/applications/cli/main.py b/gpt_engineer/applications/cli/main.py index 5a0c4135b7..2df5d9b2af 100644 --- a/gpt_engineer/applications/cli/main.py +++ b/gpt_engineer/applications/cli/main.py @@ -89,6 +89,11 @@ def load_env_if_needed(): if os.getenv("ANTHROPIC_API_KEY") is None: load_dotenv(dotenv_path=os.path.join(os.getcwd(), ".env")) + if os.getenv("MINIMAX_API_KEY") is None: + load_dotenv() + if os.getenv("MINIMAX_API_KEY") is None: + load_dotenv(dotenv_path=os.path.join(os.getcwd(), ".env")) + def concatenate_paths(base_path, sub_path): # Compute the relative path from base_path to sub_path @@ -551,6 +556,8 @@ def main( if ai.token_usage_log.is_openai_model(): print("Total api cost: $ ", ai.token_usage_log.usage_cost()) + elif ai.token_usage_log.is_minimax_model(): + print("Total tokens used: ", ai.token_usage_log.total_tokens()) elif os.getenv("LOCAL_MODEL"): print("Total api cost: $ 0.0 since we are using local LLM.") else: diff --git a/gpt_engineer/core/ai.py b/gpt_engineer/core/ai.py index ae86f63364..c3e1a8fef6 100644 --- a/gpt_engineer/core/ai.py +++ b/gpt_engineer/core/ai.py @@ -362,6 +362,26 @@ def _create_chat_model(self) -> BaseChatModel: streaming=self.streaming, max_tokens_to_sample=4096, ) + elif "minimax" in self.model_name.lower(): + # MiniMax requires temperature in (0.0, 1.0], zero is not accepted + minimax_temperature = max(self.temperature, 0.01) + minimax_api_key = os.getenv("MINIMAX_API_KEY") + if not minimax_api_key: + raise ValueError( + "MINIMAX_API_KEY environment variable is required for MiniMax models. " + "Get your API key at https://platform.minimax.io" + ) + minimax_base_url = os.getenv( + "MINIMAX_API_BASE", "https://api.minimax.io/v1" + ) + return ChatOpenAI( + model=self.model_name, + temperature=minimax_temperature, + streaming=self.streaming, + callbacks=[StreamingStdOutCallbackHandler()], + openai_api_key=minimax_api_key, + openai_api_base=minimax_base_url, + ) elif self.vision: return ChatOpenAI( model=self.model_name, diff --git a/gpt_engineer/core/token_usage.py b/gpt_engineer/core/token_usage.py index b10fec9033..5c38dd58ed 100644 --- a/gpt_engineer/core/token_usage.py +++ b/gpt_engineer/core/token_usage.py @@ -259,6 +259,17 @@ def is_openai_model(self) -> bool: """ return "gpt" in self.model_name.lower() + def is_minimax_model(self) -> bool: + """ + Check if the model is a MiniMax model. + + Returns + ------- + bool + True if the model is a MiniMax model, False otherwise. + """ + return "minimax" in self.model_name.lower() + def total_tokens(self) -> int: """ Return the total number of tokens used in the conversation. diff --git a/tests/core/test_ai.py b/tests/core/test_ai.py index 44eaeb6321..65a03da32a 100644 --- a/tests/core/test_ai.py +++ b/tests/core/test_ai.py @@ -1,3 +1,7 @@ +import os + +from unittest.mock import patch + from langchain.chat_models.base import BaseChatModel from langchain_community.chat_models.fake import FakeListChatModel @@ -51,3 +55,53 @@ def test_token_logging(monkeypatch): # assert assert usageCostAfterStart > 0 assert usageCostAfterNext > usageCostAfterStart + + +def test_minimax_model_creation(monkeypatch): + monkeypatch.setattr(AI, "_create_chat_model", mock_create_chat_model) + + ai = AI("MiniMax-M2.7") + + # act + response_messages = ai.start("system prompt", "user prompt", step_name="step name") + + # assert + assert response_messages[-1].content == "response1" + assert ai.token_usage_log.is_minimax_model() + assert not ai.token_usage_log.is_openai_model() + + +def test_minimax_temperature_constraint(): + """Test that MiniMax temperature is clamped to avoid zero.""" + ai_instance = AI.__new__(AI) + ai_instance.temperature = 0.0 + ai_instance.model_name = "MiniMax-M2.7" + ai_instance.azure_endpoint = None + ai_instance.streaming = True + ai_instance.vision = False + + with patch.dict(os.environ, {"MINIMAX_API_KEY": "test-key"}): + with patch("gpt_engineer.core.ai.ChatOpenAI") as mock_chat: + mock_chat.return_value = FakeListChatModel(responses=["response"]) + ai_instance._create_chat_model() + # Verify temperature was clamped above 0 + call_kwargs = mock_chat.call_args[1] + assert call_kwargs["temperature"] >= 0.01 + + +def test_minimax_missing_api_key(): + """Test that MiniMax raises error when API key is missing.""" + ai_instance = AI.__new__(AI) + ai_instance.temperature = 0.1 + ai_instance.model_name = "MiniMax-M2.7" + ai_instance.azure_endpoint = None + ai_instance.streaming = True + ai_instance.vision = False + + with patch.dict(os.environ, {}, clear=True): + os.environ.pop("MINIMAX_API_KEY", None) + try: + ai_instance._create_chat_model() + assert False, "Should have raised ValueError" + except ValueError as e: + assert "MINIMAX_API_KEY" in str(e)