Skip to content
Closed
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
25 changes: 25 additions & 0 deletions llmstack/processors/providers/minimax/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import Optional

from pydantic import Field

from llmstack.processors.providers.config import ProviderConfig


class MiniMaxProviderConfig(ProviderConfig):
provider_slug: str = "minimax"
api_key: str = Field(
title="API Key",
description="Your MiniMax API Key",
default="",
json_schema_extra={"widget": "password", "advanced_parameter": False},
)
base_url: Optional[str] = Field(
title="Base URL",
description="Base URL for the MiniMax OpenAI-compatible API.",
default="https://api.minimax.io/v1",
)
organization: Optional[str] = Field(
title="Organization ID",
description="Organization ID to use in MiniMax API requests",
default=None,
)
167 changes: 167 additions & 0 deletions llmstack/processors/providers/minimax/chat_completions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import logging
from typing import List, Optional

from asgiref.sync import async_to_sync
from pydantic import Field

from llmstack.apps.schemas import OutputTemplate
from llmstack.common.blocks.base.schema import StrEnum
from llmstack.processors.providers.api_processor_interface import (
ApiProcessorInterface,
ApiProcessorSchema,
)

logger = logging.getLogger(__name__)


class MessagesModel(StrEnum):
MINIMAX_M3 = "MiniMax-M3"

def model_name(self):
return self.value


class Role(StrEnum):
USER = "user"
SYSTEM = "system"
ASSISTANT = "assistant"


class ChatMessage(ApiProcessorSchema):
role: Role = Field(
default=Role.USER,
description="The role of the message. Can be 'system', 'user', or 'assistant'.",
)
message: str = Field(
default="",
description="The message text.",
json_schema_extra={"widget": "textarea"},
)


class MessagesInput(ApiProcessorSchema):
messages: List[ChatMessage] = Field(
default=[
ChatMessage(),
],
description="A list of messages, each with a role and message text.",
)


class MessagesOutput(ApiProcessorSchema):
result: str = Field(description="The response message.")


class MessagesConfiguration(ApiProcessorSchema):
system_prompt: str = Field(
default="",
description="A system prompt is a way of providing context and instructions to the model.",
json_schema_extra={"widget": "textarea", "advanced_parameter": False},
)

model: MessagesModel = Field(
default=MessagesModel.MINIMAX_M3,
description="The MiniMax model that will generate the responses.",
json_schema_extra={"advanced_parameter": False},
)
max_tokens: int = Field(
ge=1,
default=256,
description="The maximum number of tokens to generate before stopping.",
json_schema_extra={"advanced_parameter": False},
)
temperature: float = Field(
default=0.5,
description="Amount of randomness injected into the response.",
multiple_of=0.1,
ge=0.0,
le=1.0,
json_schema_extra={"advanced_parameter": False},
)
retain_history: Optional[bool] = Field(
default=False,
description="Retain and use the chat history. (Only works in apps)",
)
seed: Optional[int] = Field(
default=None,
description="The seed to use for random sampling. If set, different calls will generate deterministic results.",
)


class MessagesProcessor(ApiProcessorInterface[MessagesInput, MessagesOutput, MessagesConfiguration]):
@staticmethod
def name() -> str:
return "Chat Completions"

@staticmethod
def slug() -> str:
return "chat_completions"

@staticmethod
def description() -> str:
return "MiniMax Chat Completions"

@staticmethod
def provider_slug() -> str:
return "minimax"

@classmethod
def get_output_template(cls) -> OutputTemplate:
return OutputTemplate(
markdown="""{{ result }}""",
jsonpath="$.result",
)

def process_session_data(self, session_data):
self._chat_history = session_data["chat_history"] if "chat_history" in session_data else []

def session_data_to_persist(self) -> dict:
if self._config.retain_history:
return {"chat_history": self._chat_history}
return {}

def process(self) -> MessagesOutput:
from llmstack.common.utils.sslr import LLM

minimax_provider_config = self.get_provider_config(model_slug=self._config.model.model_name())
client = LLM(
provider="openai",
openai_api_key=minimax_provider_config.api_key,
openai_base_url=minimax_provider_config.base_url,
organization=minimax_provider_config.organization,
)
messages = []

if self._config.system_prompt:
messages.append({"role": "system", "content": self._config.system_prompt})

if self._chat_history:
for message in self._chat_history:
messages.append({"role": message["role"], "content": message["message"]})

for message in self._input.messages:
messages.append({"role": str(message.role), "content": str(message.message)})

response = client.chat.completions.create(
messages=messages,
model=self._config.model.model_name(),
max_tokens=self._config.max_tokens,
stream=True,
temperature=self._config.temperature,
seed=self._config.seed,
)

for result in response:
choice = result.choices[0]
if choice.delta.content:
async_to_sync(self._output_stream.write)(MessagesOutput(result=choice.delta.content))

output = self._output_stream.finalize()

if self._config.retain_history:
for message in self._input.messages:
self._chat_history.append({"role": str(message.role), "message": str(message.message)})

self._chat_history.append({"role": "assistant", "message": output.message})

return output
6 changes: 6 additions & 0 deletions llmstack/server/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,12 @@
"slug": "mistral",
"config_schema": "llmstack.processors.providers.mistral.MistralProviderConfig",
},
{
"name": "MiniMax",
"processor_packages": ["llmstack.processors.providers.minimax"],
"slug": "minimax",
"config_schema": "llmstack.processors.providers.minimax.MiniMaxProviderConfig",
},
{
"name": "Meta",
"processor_packages": ["llmstack.processors.providers.meta"],
Expand Down