Compare commits

..

1 Commits

Author SHA1 Message Date
shokollm
4cc0d982d6 fix: update MiniMax API endpoint from api.minimax.chat to api.minimax.io
The MiniMax API endpoint should be api.minimax.io, not api.minimax.chat.

See: https://platform.minimax.io/docs/api-reference/text-anthropic-api

Fixes #43
2026-04-10 02:59:24 +00:00
9 changed files with 76 additions and 248 deletions

View File

@@ -32,7 +32,7 @@ MINIMAX_API_KEY=your-minimax-api-key
# MiniMax model to use
# Common options: MiniMax-Text-01, MiniMax-M2.1
MINIMAX_MODEL=MiniMax-M2.7
MINIMAX_MODEL=MiniMax-Text-01
# =============================================================================
# AVE CLOUD API

View File

@@ -58,7 +58,7 @@ def get_current_user(
@router.post(
"/register", response_model=Token, status_code=status.HTTP_201_CREATED
"/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED
)
def register(user: UserCreate, db: Session = Depends(get_db)):
existing_user = db.query(User).filter(User.email == user.email).first()
@@ -75,10 +75,7 @@ def register(user: UserCreate, db: Session = Depends(get_db)):
db.add(db_user)
db.commit()
db.refresh(db_user)
# Generate and return access token so frontend can proceed immediately
access_token = create_access_token(data={"sub": db_user.id})
return Token(access_token=access_token, token_type="bearer")
return db_user
@router.post("/login", response_model=Token)

View File

@@ -16,7 +16,6 @@ from ..db.schemas import (
)
from ..db.models import Bot, BotConversation, User
from ..services.ai_agent.crew import get_trading_crew
from ..services.ai_agent.conversational import get_conversational_agent
router = APIRouter()
MAX_BOTS_PER_USER = 3
@@ -184,45 +183,69 @@ def chat(
.order_by(BotConversation.created_at)
.all()
)
history_for_agent = [
history_for_crew = [
{"role": conv.role, "content": conv.content}
for conv in conversation_history[-10:]
]
user_message = request.message
# Use ConversationalAgent for natural chat with tool-calling
agent = get_conversational_agent(bot_id=bot_id)
result = agent.chat(user_message, history_for_agent)
if request.strategy_config:
crew = get_trading_crew()
result = crew.chat(user_message, history_for_crew)
assistant_content = result.get("response", "I couldn't process your request.")
assistant_content = result.get("response", "I couldn't process your request.")
if result.get("success") and result.get("strategy_config"):
bot.strategy_config = result["strategy_config"]
db.commit()
# Save conversation
db_conversation = BotConversation(
bot_id=bot_id,
role="user",
content=user_message,
)
db.add(db_conversation)
db_conversation = BotConversation(
bot_id=bot_id,
role="user",
content=user_message,
)
db.add(db_conversation)
db_assistant = BotConversation(
bot_id=bot_id,
role="assistant",
content=assistant_content,
)
db.add(db_assistant)
db.commit()
db.refresh(db_assistant)
db_assistant = BotConversation(
bot_id=bot_id,
role="assistant",
content=assistant_content,
)
db.add(db_assistant)
db.commit()
db.refresh(db_assistant)
# If strategy was updated via tool, refresh bot data
if result.get("strategy_updated"):
db.refresh(bot)
return BotChatResponse(
response=assistant_content,
strategy_config=result.get("strategy_config"),
success=result.get("success", False),
)
else:
crew = get_trading_crew()
result = crew.chat(user_message, history_for_crew)
return BotChatResponse(
response=assistant_content,
strategy_config=bot.strategy_config if result.get("strategy_updated") else None,
success=result.get("success", False),
)
assistant_content = result.get("response", "I couldn't process your request.")
db_conversation = BotConversation(
bot_id=bot_id,
role="user",
content=user_message,
)
db.add(db_conversation)
db_assistant = BotConversation(
bot_id=bot_id,
role="assistant",
content=assistant_content,
)
db.add(db_assistant)
db.commit()
db.refresh(db_assistant)
return BotChatResponse(
response=assistant_content,
strategy_config=result.get("strategy_config"),
success=result.get("success", False),
)
@router.get("/{bot_id}/history", response_model=List[BotConversationResponse])

View File

@@ -1,167 +0,0 @@
"""
Conversational Trading Agent
This agent can:
1. Have normal conversations with users
2. Update trading strategies when user provides specific instructions
Uses CrewAI's tool-calling capabilities for structured updates.
"""
from typing import List, Optional, Dict, Any
from crewai import Agent, LLM
from crewai.tools import tool
from sqlalchemy.orm import Session
from ...core.config import get_settings
from ...db.models import Bot
# Tool definitions
@tool
def get_current_strategy(bot_id: str) -> str:
"""Get the current trading strategy configuration for a bot.
Use this tool to check the current strategy before making changes.
Args:
bot_id: The ID of the bot to get strategy for
Returns:
JSON string with current strategy configuration
"""
from ...core.database import get_db
from ...db.models import Bot
db = next(get_db())
bot = db.query(Bot).filter(Bot.id == bot_id).first()
if not bot:
return '{"error": "Bot not found"}'
return str(bot.strategy_config)
@tool
def update_trading_strategy(
bot_id: str,
conditions: List[Dict],
actions: List[Dict],
risk_management: Optional[Dict] = None
) -> str:
"""Update the trading strategy configuration for a bot.
Call this tool when the user provides specific trading parameters like:
- Buy/sell conditions (price drops, price rises, etc.)
- Take profit percentages
- Stop loss percentages
Args:
bot_id: The ID of the bot to update
conditions: List of trigger conditions (e.g., [{"type": "price_drop", "token": "PEPE", "threshold": 5}])
actions: List of actions to take (e.g., [{"type": "buy", "amount_percent": 50}])
risk_management: Optional risk settings (e.g., {"stop_loss_percent": 10, "take_profit_percent": 50})
Returns:
Confirmation message with updated strategy
"""
from ...core.database import get_db
from ...db.models import Bot
db = next(get_db())
bot = db.query(Bot).filter(Bot.id == bot_id).first()
if not bot:
return '{"error": "Bot not found"}'
new_config = {
"conditions": conditions,
"actions": actions,
}
if risk_management:
new_config["risk_management"] = risk_management
bot.strategy_config = new_config
db.commit()
return f'Successfully updated trading strategy. New config: {new_config}'
SYSTEM_PROMPT = """You are a helpful AI trading assistant. You can:
1. Have normal conversations - answer questions about trading, tokens, strategies, etc.
2. Help users configure their trading bots when they provide specific parameters
When a user asks general questions, just answer conversationally.
When a user provides specific trading parameters (like percentages, tokens, conditions),
use the update_trading_strategy tool to save their configuration.
Example conversations:
- User: "What is this?" → Answer conversationally about the trading bot platform
- User: "I want take profit at 200%" → Use update_trading_strategy with that parameter
- User: "Alert me when PEPE drops 5%" → Use update_trading_strategy with that condition
Be friendly, helpful, and clear in your responses."""
class ConversationalAgent:
def __init__(self, api_key: str, model: str = "MiniMax-M2.7", bot_id: str = None):
self.api_key = api_key
self.model = model
self.bot_id = bot_id
self.llm = LLM(
model=model,
api_key=api_key,
api_base="https://api.minimax.io/v1"
)
# Create agent with tools
self.agent = Agent(
role="Trading Assistant",
goal="Help users with trading strategies and general questions",
backstory=SYSTEM_PROMPT,
tools=[get_current_strategy, update_trading_strategy],
llm=self.llm,
verbose=True,
allow_delegation=False,
)
def chat(self, user_message: str, conversation_history: List[Dict] = None) -> Dict[str, Any]:
"""Process a user message and return a response.
Args:
user_message: The user's message
conversation_history: Optional list of previous messages
Returns:
Dict with 'response' (the assistant's reply) and 'strategy_updated' (bool)
"""
# Execute agent using kickoff
try:
result = self.agent.kickoff(user_message)
# Check if strategy was updated
result_str = str(result)
strategy_updated = "update_trading_strategy" in result_str or \
"Successfully updated" in result_str
return {
"response": result_str,
"strategy_updated": strategy_updated,
"success": True
}
except Exception as e:
return {
"response": f"I encountered an error: {str(e)}. Please try again.",
"strategy_updated": False,
"success": False
}
def get_conversational_agent(api_key: str = None, model: str = None, bot_id: str = None) -> ConversationalAgent:
"""Get or create a ConversationalAgent instance."""
if api_key is None:
settings = get_settings()
api_key = settings.MINIMAX_API_KEY
if model is None:
settings = get_settings()
model = settings.MINIMAX_MODEL
return ConversationalAgent(api_key=api_key, model=model, bot_id=bot_id)

View File

@@ -1,6 +1,6 @@
from typing import List, Optional, Dict, Any
from crewai import Agent, Task, Crew, LLM
from .llm_connector import MiniMaxConnector
from crewai import Agent, Task, Crew
from .llm_connector import MiniMaxConnector, MiniMaxLLM
from ...core.config import get_settings
@@ -120,7 +120,7 @@ class StrategyExplainer:
def create_trading_designer_agent(
api_key: str, model: str = "MiniMax-M2.7"
api_key: str, model: str = "MiniMax-Text-01"
) -> Agent:
connector = MiniMaxConnector(api_key=api_key, model=model)
@@ -141,13 +141,13 @@ def create_trading_designer_agent(
role="Trading Strategy Designer",
goal="Convert natural language trading requests into precise strategy configurations",
backstory=system_prompt,
llm=LLM(model=model, api_key=api_key, api_base="https://api.minimax.io/v1"),
llm=MiniMaxLLM(api_key=api_key, model=model),
verbose=True,
)
def create_strategy_validator_agent(
api_key: str, model: str = "MiniMax-M2.7"
api_key: str, model: str = "MiniMax-Text-01"
) -> Agent:
return Agent(
role="Strategy Validator",
@@ -155,13 +155,13 @@ def create_strategy_validator_agent(
backstory="""You are a meticulous strategy validator with expertise in trading systems.
You check that all required parameters are present, values are reasonable, and the
strategy makes logical sense. You never approve strategies with missing or invalid data.""",
llm=LLM(model=model, api_key=api_key, api_base="https://api.minimax.io/v1"),
llm=MiniMaxLLM(api_key=api_key, model=model),
verbose=True,
)
def create_strategy_explainer_agent(
api_key: str, model: str = "MiniMax-M2.7"
api_key: str, model: str = "MiniMax-Text-01"
) -> Agent:
return Agent(
role="Strategy Explainer",
@@ -169,13 +169,13 @@ def create_strategy_explainer_agent(
backstory="""You are a patient trading strategy explainer. You translate complex
strategy configurations into easy-to-understand language. You help users understand
exactly what their strategies will do when triggered.""",
llm=LLM(model=model, api_key=api_key, api_base="https://api.minimax.io/v1"),
llm=MiniMaxLLM(api_key=api_key, model=model),
verbose=True,
)
class TradingCrew:
def __init__(self, api_key: str, model: str = "MiniMax-M2.7"):
def __init__(self, api_key: str, model: str = "MiniMax-Text-01"):
self.api_key = api_key
self.model = model
self.validator = StrategyValidator()

View File

@@ -1,9 +1,11 @@
from typing import Optional, List, Dict, Any
import httpx
from crewai import LLM
class MiniMaxLLM:
def __init__(self, api_key: str, model: str = "MiniMax-M2.7", **kwargs):
class MiniMaxLLM(LLM):
def __init__(self, api_key: str, model: str = "MiniMax-Text-01", **kwargs):
super().__init__(**kwargs)
self.api_key = api_key
self.model = model
self.base_url = "https://api.minimax.io/v1"
@@ -21,7 +23,7 @@ class MiniMaxLLM:
}
with httpx.Client(timeout=60.0) as client:
response = client.post(
f"{self.base_url}/text/chatcompletion_v2",
f"{self.base_url}/chat/completions",
headers=headers,
json=payload,
)
@@ -33,7 +35,7 @@ class MiniMaxLLM:
class MiniMaxConnector:
def __init__(self, api_key: str, model: str = "MiniMax-M2.7"):
def __init__(self, api_key: str, model: str = "MiniMax-Text-01"):
self.api_key = api_key
self.model = model

View File

@@ -104,12 +104,11 @@ export const api = {
}
},
async chat(id: string, message: string, signal?: AbortSignal): Promise<BotChatResponse> {
async chat(id: string, message: string): Promise<BotChatResponse> {
const response = await fetch(`${API_URL}/bots/${id}/chat`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ message } as BotChatRequest),
signal
body: JSON.stringify({ message } as BotChatRequest)
});
return handleResponse<BotChatResponse>(response);
},

View File

@@ -8,25 +8,12 @@ export interface ChatMessage {
timestamp: Date;
}
// Fallback UUID generator for environments where crypto.randomUUID is not available
function generateId(): string {
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
return crypto.randomUUID();
}
// Fallback: simple UUID v4 implementation
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
export const chatStore = writable<ChatMessage[]>([]);
export function addMessage(message: Omit<ChatMessage, 'id' | 'timestamp'>) {
const newMessage: ChatMessage = {
...message,
id: generateId(),
id: crypto.randomUUID(),
timestamp: new Date()
};
chatStore.update(messages => [...messages, newMessage]);

View File

@@ -44,17 +44,8 @@
isSending = true;
// Add user's message immediately so it shows even before API response
addMessage({ role: 'user', content: message });
try {
// Add timeout to prevent hanging requests
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
const response = await api.bots.chat(botId, message, controller.signal);
clearTimeout(timeoutId);
const response = await api.bots.chat(botId, message);
addMessage({ role: 'assistant', content: response.response });
if (response.strategy_config) {
@@ -62,11 +53,7 @@
setCurrentBot(bot);
}
} catch (e) {
if (e instanceof Error && e.name === 'AbortError') {
addMessage({ role: 'assistant', content: 'Request timed out. Please try again.' });
} else {
addMessage({ role: 'assistant', content: 'Sorry, I encountered an error. Please try again.' });
}
addMessage({ role: 'assistant', content: 'Sorry, I encountered an error. Please try again.' });
} finally {
isSending = false;
}