Compare commits
4 Commits
fix/chat-t
...
feat/conve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
765e390b9b | ||
| 21ce282cae | |||
|
|
4fa9b0456a | ||
| af9900d0ba |
@@ -16,6 +16,7 @@ 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
|
||||
@@ -183,69 +184,45 @@ def chat(
|
||||
.order_by(BotConversation.created_at)
|
||||
.all()
|
||||
)
|
||||
history_for_crew = [
|
||||
history_for_agent = [
|
||||
{"role": conv.role, "content": conv.content}
|
||||
for conv in conversation_history[-10:]
|
||||
]
|
||||
|
||||
user_message = request.message
|
||||
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.")
|
||||
if result.get("success") and result.get("strategy_config"):
|
||||
bot.strategy_config = result["strategy_config"]
|
||||
db.commit()
|
||||
# Use ConversationalAgent for natural chat with tool-calling
|
||||
agent = get_conversational_agent(bot_id=bot_id)
|
||||
result = agent.chat(user_message, history_for_agent)
|
||||
|
||||
db_conversation = BotConversation(
|
||||
bot_id=bot_id,
|
||||
role="user",
|
||||
content=user_message,
|
||||
)
|
||||
db.add(db_conversation)
|
||||
assistant_content = result.get("response", "I couldn't process your request.")
|
||||
|
||||
db_assistant = BotConversation(
|
||||
bot_id=bot_id,
|
||||
role="assistant",
|
||||
content=assistant_content,
|
||||
)
|
||||
db.add(db_assistant)
|
||||
db.commit()
|
||||
db.refresh(db_assistant)
|
||||
# Save conversation
|
||||
db_conversation = BotConversation(
|
||||
bot_id=bot_id,
|
||||
role="user",
|
||||
content=user_message,
|
||||
)
|
||||
db.add(db_conversation)
|
||||
|
||||
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)
|
||||
db_assistant = BotConversation(
|
||||
bot_id=bot_id,
|
||||
role="assistant",
|
||||
content=assistant_content,
|
||||
)
|
||||
db.add(db_assistant)
|
||||
db.commit()
|
||||
db.refresh(db_assistant)
|
||||
|
||||
assistant_content = result.get("response", "I couldn't process your request.")
|
||||
# If strategy was updated via tool, refresh bot data
|
||||
if result.get("strategy_updated"):
|
||||
db.refresh(bot)
|
||||
|
||||
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),
|
||||
)
|
||||
return BotChatResponse(
|
||||
response=assistant_content,
|
||||
strategy_config=bot.strategy_config if result.get("strategy_updated") else None,
|
||||
success=result.get("success", False),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{bot_id}/history", response_model=List[BotConversationResponse])
|
||||
|
||||
167
src/backend/app/services/ai_agent/conversational.py
Normal file
167
src/backend/app/services/ai_agent/conversational.py
Normal file
@@ -0,0 +1,167 @@
|
||||
"""
|
||||
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)
|
||||
@@ -8,12 +8,25 @@ 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: crypto.randomUUID(),
|
||||
id: generateId(),
|
||||
timestamp: new Date()
|
||||
};
|
||||
chatStore.update(messages => [...messages, newMessage]);
|
||||
|
||||
Reference in New Issue
Block a user