Skip to content

Commit

Permalink
support for commands
Browse files Browse the repository at this point in the history
  • Loading branch information
danXyu committed Dec 12, 2024
1 parent e5d7f5c commit 11ca671
Show file tree
Hide file tree
Showing 12 changed files with 512 additions and 66 deletions.
32 changes: 26 additions & 6 deletions submodules/moragents_dockers/agents/src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
chat_manager_routes,
key_manager_routes,
wallet_manager_routes,
workflow_manager_routes
workflow_manager_routes,
)

# Constants
Expand All @@ -40,6 +40,8 @@
allow_methods=["*"],
allow_headers=["*"],
)


@app.on_event("startup")
async def startup_event():
await workflow_manager_instance.initialize()
Expand Down Expand Up @@ -119,18 +121,36 @@ def validate_agent_response(response: dict, current_agent: str) -> dict:

@app.post("/chat")
async def chat(chat_request: ChatRequest):
prompt = chat_request.prompt.dict()
chat_manager_instance.add_message(prompt)
"""Send a chat message and get a response"""
logger.info(f"Received chat request for conversation {chat_request.conversation_id}")

# Parse command if present
agent_name, message = agent_manager_instance.parse_command(
chat_request.prompt.dict()["content"]
)

if agent_name:
# If command was used, set that agent as active
agent_manager_instance.set_active_agent(agent_name)
# Update message content without command
chat_request.prompt.dict()["content"] = message

chat_manager_instance.add_message(chat_request.prompt.dict(), chat_request.conversation_id)

try:
delegator.reset_attempted_agents()
active_agent = await get_active_agent_for_chat(prompt)
if not agent_name:
delegator.reset_attempted_agents()
active_agent = await get_active_agent_for_chat(chat_request.prompt.dict())
else:
active_agent = agent_name

logger.info(f"Delegating chat to active agent: {active_agent}")
current_agent, response = delegator.delegate_chat(active_agent, chat_request)

validated_response = validate_agent_response(response, current_agent)
chat_manager_instance.add_response(validated_response, current_agent)
chat_manager_instance.add_response(
validated_response, current_agent, chat_request.conversation_id
)

logger.info(f"Sending response: {validated_response}")
return validated_response
Expand Down
10 changes: 10 additions & 0 deletions submodules/moragents_dockers/agents/src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Config:
"description": "Must be used for meta-queries that ask about active Morpheus agents, and also for general, simple questions",
"name": "default",
"human_readable_name": "Default General Purpose",
"command": "morpheus",
"upload_required": False,
},
{
Expand All @@ -30,6 +31,7 @@ class Config:
"description": "Must only be used for image generation tasks. Use when the query explicitly mentions generating or creating an image.",
"name": "imagen",
"human_readable_name": "Image Generator",
"command": "imagen",
"upload_required": False,
},
{
Expand All @@ -38,6 +40,7 @@ class Config:
"description": "Handles transactions on the Base crypto network. Use when the user makes any reference to Base, base, the base network, or Coinbase",
"name": "base",
"human_readable_name": "Base Transaction Manager",
"command": "base",
"upload_required": False,
},
{
Expand All @@ -46,6 +49,7 @@ class Config:
"description": "Crypto-specific. Provides real-time cryptocurrency data such as price, market cap, and fully diluted valuation (FDV).",
"name": "crypto data",
"human_readable_name": "Crypto Data Fetcher",
"command": "crypto",
"upload_required": False,
},
# DISABLED: Pending 1inch protocol fix
Expand All @@ -63,6 +67,7 @@ class Config:
"description": "Generates engaging tweets. Use ONLY when the query explicitly mentions Twitter, tweeting, or the X platform.",
"name": "tweet sizzler",
"human_readable_name": "Tweet / X-Post Generator",
"command": "tweet",
"upload_required": False,
},
{
Expand All @@ -71,6 +76,7 @@ class Config:
"description": "Sets up DCA strategies. Use when the user requests to set up a dollar-cost averaging strategy for crypto purchases or trades.",
"name": "dca",
"human_readable_name": "DCA Strategy Manager",
"command": "dca",
"upload_required": False,
},
{
Expand All @@ -79,6 +85,7 @@ class Config:
"description": "Answers questions about a document. Must be used anytime an upload, a document, Documents, or uploaded document is mentioned",
"name": "rag",
"human_readable_name": "Document Assistant",
"command": "document",
"upload_required": True,
},
# DISABLED:
Expand All @@ -96,6 +103,7 @@ class Config:
"description": "Provides information about user's accrued MOR rewards or tokens. Use when the query is about checking or querying reward balances.",
"name": "mor rewards",
"human_readable_name": "MOR Rewards Tracker",
"command": "rewards",
"upload_required": False,
},
{
Expand All @@ -104,6 +112,7 @@ class Config:
"description": f"Use when the query is about searching the web or asks about a recent / current event (The year is {datetime.datetime.now().year})",
"name": "realtime search",
"human_readable_name": "Real-Time Search",
"command": "search",
"upload_required": False,
},
{
Expand All @@ -112,6 +121,7 @@ class Config:
"description": "Fetches and analyzes cryptocurrency news for potential price impacts.",
"name": "crypto news",
"human_readable_name": "Crypto News Analyst",
"command": "news",
"upload_required": False,
},
]
Expand Down
2 changes: 2 additions & 0 deletions submodules/moragents_dockers/agents/src/models/messages.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import List, Optional
from fastapi import Query
from pydantic import BaseModel


Expand All @@ -11,6 +12,7 @@ class ChatRequest(BaseModel):
prompt: ChatMessage
chain_id: str
wallet_address: str
conversation_id: str = Query(default="default")


class ChatMessage(BaseModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,18 @@ async def set_selected_agents(request: Request) -> JSONResponse:
logger.info(f"Newly selected agents: {agent_manager_instance.get_selected_agents()}")

return JSONResponse(content={"status": "success", "agents": agent_names})


@router.get("/commands")
async def get_agent_commands() -> JSONResponse:
"""Get the list of available agent commands"""
available_agents = agent_manager_instance.get_available_agents()
commands = [
{
"command": agent["command"],
"description": agent["description"],
"name": agent["human_readable_name"],
}
for agent in available_agents
]
return JSONResponse(content={"commands": commands})
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@ async def clear_messages(conversation_id: str = Query(default="default")):
return {"response": "successfully cleared message history"}


@router.get("/conversations")
async def get_conversations():
"""Get all conversation IDs"""
logger.info("Getting all conversation IDs")
return {"conversation_ids": chat_manager_instance.get_all_conversation_ids()}


@router.post("/conversations")
async def create_conversation():
"""Create a new conversation"""
# The conversation will be created automatically when first accessed
new_id = f"conversation_{len(chat_manager_instance.get_all_conversation_ids())}"
chat_manager_instance.get_messages(new_id) # This creates the conversation
conversation = chat_manager_instance.create_conversation(new_id)
logger.info(f"Created new conversation with ID: {new_id}")
return {"conversation_id": new_id}
return {"conversation_id": new_id, "conversation": conversation}


@router.delete("/conversations/{conversation_id}")
Expand Down
37 changes: 36 additions & 1 deletion submodules/moragents_dockers/agents/src/stores/agent_manager.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import importlib
import logging

from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Tuple
from langchain_ollama import ChatOllama
from langchain_community.embeddings import OllamaEmbeddings

Expand Down Expand Up @@ -165,6 +165,41 @@ def get_agent(self, agent_name: str) -> Optional[Any]:
"""
return self.agents.get(agent_name)

def get_agent_by_command(self, command: str) -> Optional[str]:
"""
Get agent name by command.
Args:
command (str): Command to look up
Returns:
Optional[str]: Agent name if found, None otherwise
"""
for agent in self.config["agents"]:
if agent["command"] == command:
return agent["name"]
return None

def parse_command(self, message: str) -> Tuple[Optional[str], str]:
"""
Parse a message for commands.
Args:
message (str): Message to parse
Returns:
Tuple[Optional[str], str]: Tuple of (agent_name, message_without_command)
"""
if not message.startswith("/"):
return None, message

parts = message[1:].split(maxsplit=1)
command = parts[0]
remaining_message = parts[1] if len(parts) > 1 else ""

agent_name = self.get_agent_by_command(command)
return agent_name, remaining_message


# Create an instance to act as a singleton store
agent_manager_instance = AgentManager(Config.AGENTS_CONFIG)
Loading

0 comments on commit 11ca671

Please sign in to comment.