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
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,33 @@ python -u app.py \

Notice that `-e` flag can save a lot of memory.

### Using MiniMax as LLM Provider

InternGPT supports [MiniMax](https://www.minimax.io/) as an alternative LLM backend. MiniMax provides powerful language models (M2.7, M2.5) via an OpenAI-compatible API.

**Option 1: Environment variables**

Set the following environment variables before starting the app:
```shell
export LLM_PROVIDER=minimax
export MINIMAX_API_KEY=your_minimax_api_key
```

If only `MINIMAX_API_KEY` is set (without `OPENAI_API_KEY`), MiniMax is auto-detected.

**Option 2: UI selector**

Select "minimax" from the provider dropdown on the login page and enter your MiniMax API key.

**Available MiniMax models:**

| Model | Description |
|---|---|
| `MiniMax-M2.7` | Latest flagship model (default) |
| `MiniMax-M2.7-highspeed` | Fast variant of M2.7 |
| `MiniMax-M2.5` | Previous generation, 204K context |
| `MiniMax-M2.5-highspeed` | Fast variant of M2.5 |

### Selectively Loading Features
When you only want to try DragGAN, you just need to load StyleGAN and open the tab "DragGAN":
```shell
Expand Down
35 changes: 23 additions & 12 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
# from iGPT.models import *

from iGPT.controllers import ConversationBot
from iGPT.controllers.llm_provider import create_llm, detect_provider, MiniMaxLLM

import openai
from langchain.llms.openai import OpenAI
Expand Down Expand Up @@ -146,7 +147,7 @@ def cut_dialogue_history(history_memory, keep_last_n_words=500):
return '\n' + '\n'.join(paragraphs)


def login_with_key(bot, debug, api_key):
def login_with_key(bot, debug, api_key, provider="openai"):
# Just for debug
print('===>logging in')
user_state = [{}]
Expand All @@ -157,14 +158,18 @@ def login_with_key(bot, debug, api_key):
else:
if api_key and len(api_key) > 30:
print(api_key)
os.environ["OPENAI_API_KEY"] = api_key
openai.api_key = api_key
try:
llm = OpenAI(temperature=0)
if provider == "minimax":
os.environ["MINIMAX_API_KEY"] = api_key
llm = create_llm(provider="minimax", api_key=api_key)
else:
os.environ["OPENAI_API_KEY"] = api_key
openai.api_key = api_key
llm = create_llm(provider="openai", api_key=api_key)
llm('Hi!')
response = 'Success!'
is_error = False
user_state = bot.init_agent()
user_state = bot.init_agent(provider=provider, api_key=api_key)
except Exception as err:
# gr.update(visible=True)
print(err)
Expand All @@ -173,7 +178,7 @@ def login_with_key(bot, debug, api_key):
else:
is_error = True
response = 'Incorrect key, please input again'

return gr.update(visible=not is_error), gr.update(visible=is_error), gr.update(visible=is_error, value=response), user_state


Expand Down Expand Up @@ -240,15 +245,21 @@ def change_max_iter(max_iters):
)

with gr.Row(visible=True, elem_id='login') as login:
with gr.Column(scale=0.6, min_width=0) :
with gr.Column(scale=0.4, min_width=0) :
openai_api_key_text = gr.Textbox(
placeholder="Input openAI API key",
placeholder="Input API key (OpenAI or MiniMax)",
show_label=False,
label="OpenAI API Key",
label="API Key",
lines=1,
type="password").style(container=False)
with gr.Column(scale=0.2, min_width=0):
provider_selector = gr.Dropdown(
choices=["openai", "minimax"],
value="openai",
label="LLM Provider",
show_label=False).style(container=False)
with gr.Column(scale=0.4, min_width=0):
key_submit_button = gr.Button(value="Please log in with your OpenAI API Key", interactive=True, variant='primary').style(container=False)
key_submit_button = gr.Button(value="Log in with your API Key", interactive=True, variant='primary').style(container=False)

with gr.Row(visible=False) as user_interface:
with gr.Column(scale=0.5, elem_id="text_input") as chat_part:
Expand Down Expand Up @@ -375,8 +386,8 @@ def change_max_iter(max_iters):


login_func = partial(login_with_key, bot, args.debug)
openai_api_key_text.submit(login_func, [openai_api_key_text], [user_interface, openai_api_key_text, key_submit_button, user_state])
key_submit_button.click(login_func, [openai_api_key_text, ], [user_interface, openai_api_key_text, key_submit_button, user_state])
openai_api_key_text.submit(login_func, [openai_api_key_text, provider_selector], [user_interface, openai_api_key_text, key_submit_button, user_state])
key_submit_button.click(login_func, [openai_api_key_text, provider_selector], [user_interface, openai_api_key_text, key_submit_button, user_state])

txt.submit(
lambda: gr.update(interactive=False), [], [send_btn]).then(
Expand Down
7 changes: 4 additions & 3 deletions iGPT/controllers/ConversationBot.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from langchain.agents.initialize import initialize_agent
from langchain.agents.tools import Tool
from langchain.chains.conversation.memory import ConversationBufferMemory
from langchain.llms.openai import OpenAI

from .llm_provider import create_llm

from ..models import *
from iGPT.models.utils import (gen_new_name, to_image,
Expand Down Expand Up @@ -159,9 +160,9 @@ def __init__(self, load_dict, e_mode=False, chat_disabled=False):
self.tools.append(Tool(name=func.name, description=func.description, func=func))


def init_agent(self):
def init_agent(self, provider=None, api_key=None):
memory = ConversationBufferMemory(memory_key="chat_history", output_key='output')
llm = OpenAI(temperature=0)
llm = create_llm(provider=provider, api_key=api_key, temperature=0)
agent = initialize_agent(
self.tools,
llm,
Expand Down
4 changes: 4 additions & 0 deletions iGPT/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from .ConversationBot import ConversationBot
from .llm_provider import MiniMaxLLM, create_llm, detect_provider


__all__ = [
'ConversationBot',
'MiniMaxLLM',
'create_llm',
'detect_provider',
]
203 changes: 203 additions & 0 deletions iGPT/controllers/llm_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
"""
LLM provider factory for InternGPT.

Supports OpenAI (default) and MiniMax as LLM backends.
MiniMax models use the OpenAI-compatible chat completion API
(https://api.minimax.io/v1).

Usage:
# Auto-detect from environment variables
llm = create_llm()

# Explicit provider selection
llm = create_llm(provider="minimax")
"""

import os
import re
from typing import Any, List, Mapping, Optional

import openai

# Handle both old (0.x) and new (1.x+) openai SDK
_OPENAI_V1 = hasattr(openai, "__version__") and int(openai.__version__.split(".")[0]) >= 1

try:
from langchain.llms.base import LLM
except ImportError:
from langchain_core.language_models.llms import LLM

try:
from langchain.llms.openai import OpenAI
except ImportError:
from langchain_openai import OpenAI


# ---------------------------------------------------------------------------
# MiniMax-specific constants
# ---------------------------------------------------------------------------
MINIMAX_API_BASE = "https://api.minimax.io/v1"
MINIMAX_DEFAULT_MODEL = "MiniMax-M2.7"
MINIMAX_MODELS = [
"MiniMax-M2.7",
"MiniMax-M2.7-highspeed",
"MiniMax-M2.5",
"MiniMax-M2.5-highspeed",
]


def _chat_completion(model, messages, temperature, stop, api_key, api_base):
"""Call chat completion API, compatible with both old and new openai SDK."""
if _OPENAI_V1:
client = openai.OpenAI(api_key=api_key, base_url=api_base)
kwargs = dict(model=model, messages=messages, temperature=temperature)
if stop:
kwargs["stop"] = stop
response = client.chat.completions.create(**kwargs)
return response.choices[0].message.content or ""
else:
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=temperature,
stop=stop,
api_key=api_key,
api_base=api_base,
)
return response.choices[0].message.content or ""


class MiniMaxLLM(LLM):
"""LangChain LLM wrapper for MiniMax via OpenAI-compatible API.

MiniMax exposes an OpenAI-compatible ``/v1/chat/completions`` endpoint.
This class translates a plain-text prompt into a single-user-message chat
request so that it can be used with LangChain agents that expect a
``BaseLLM`` interface.

Parameters
----------
model_name : str
MiniMax model identifier (default ``MiniMax-M2.7``).
temperature : float
Sampling temperature. MiniMax requires ``temperature`` in (0, 1],
so values <= 0 are clamped to 0.01.
api_key : str
MiniMax API key. Falls back to the ``MINIMAX_API_KEY`` env var.
api_base : str
Base URL for the MiniMax API (default ``https://api.minimax.io/v1``).
"""

model_name: str = MINIMAX_DEFAULT_MODEL
temperature: float = 0.01
api_key: str = ""
api_base: str = MINIMAX_API_BASE

def __init__(self, **kwargs: Any):
super().__init__(**kwargs)
if not self.api_key:
self.api_key = os.environ.get("MINIMAX_API_KEY", "")
# Clamp temperature to MiniMax's valid range (0, 1]
if self.temperature <= 0:
self.temperature = 0.01

@property
def _llm_type(self) -> str:
return "minimax"

@property
def _identifying_params(self) -> Mapping[str, Any]:
return {
"model_name": self.model_name,
"temperature": self.temperature,
"api_base": self.api_base,
}

def _strip_think_tags(self, text: str) -> str:
"""Remove <think>...</think> blocks from model output."""
return re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL).strip()

def _call(self, prompt: str, stop: Optional[List[str]] = None, **kwargs) -> str:
"""Call MiniMax chat completion API with a single user message."""
content = _chat_completion(
model=self.model_name,
messages=[{"role": "user", "content": prompt}],
temperature=self.temperature,
stop=stop,
api_key=self.api_key,
api_base=self.api_base,
)
return self._strip_think_tags(content)


# ---------------------------------------------------------------------------
# Provider detection helpers
# ---------------------------------------------------------------------------
SUPPORTED_PROVIDERS = ("openai", "minimax")


def detect_provider() -> str:
"""Detect the LLM provider from environment variables.

Priority:
1. ``LLM_PROVIDER`` env var (explicit).
2. If ``MINIMAX_API_KEY`` is set and ``OPENAI_API_KEY`` is not → minimax.
3. Default → openai.
"""
explicit = os.environ.get("LLM_PROVIDER", "").lower().strip()
if explicit in SUPPORTED_PROVIDERS:
return explicit

has_minimax = bool(os.environ.get("MINIMAX_API_KEY"))
has_openai = bool(os.environ.get("OPENAI_API_KEY"))

if has_minimax and not has_openai:
return "minimax"

return "openai"


def create_llm(
provider: Optional[str] = None,
api_key: Optional[str] = None,
model_name: Optional[str] = None,
temperature: float = 0,
) -> LLM:
"""Create an LLM instance for the given provider.

Parameters
----------
provider : str, optional
``"openai"`` or ``"minimax"``. Auto-detected if *None*.
api_key : str, optional
API key override. Otherwise read from the environment.
model_name : str, optional
Model name override.
temperature : float
Sampling temperature (default 0).

Returns
-------
langchain.llms.base.LLM
A LangChain-compatible LLM instance.
"""
if provider is None:
provider = detect_provider()

provider = provider.lower().strip()

if provider == "minimax":
kwargs: dict = {"temperature": temperature}
if api_key:
kwargs["api_key"] = api_key
if model_name:
kwargs["model_name"] = model_name
return MiniMaxLLM(**kwargs)

# Default: OpenAI
kwargs_openai: dict = {"temperature": temperature}
if api_key:
kwargs_openai["openai_api_key"] = api_key
if model_name:
kwargs_openai["model_name"] = model_name
return OpenAI(**kwargs_openai)
Loading