feat(dependencies): add psycopg2 for PostgreSQL support and update README description
This commit is contained in:
@@ -32,7 +32,7 @@ router = APIRouter(
|
||||
|
||||
|
||||
# Session Routes
|
||||
@router.get("/client/{client_id}", response_model=List[Adk_Session])
|
||||
@router.get("/client/{client_id}")
|
||||
async def get_client_sessions(
|
||||
client_id: uuid.UUID,
|
||||
db: Session = Depends(get_db),
|
||||
@@ -43,7 +43,7 @@ async def get_client_sessions(
|
||||
return get_sessions_by_client(db, client_id)
|
||||
|
||||
|
||||
@router.get("/agent/{agent_id}", response_model=List[Adk_Session])
|
||||
@router.get("/agent/{agent_id}")
|
||||
async def get_agent_sessions(
|
||||
agent_id: uuid.UUID,
|
||||
db: Session = Depends(get_db),
|
||||
|
||||
@@ -146,6 +146,7 @@ class MCPServer(Base):
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
config_type = Column(String, nullable=False, default="studio")
|
||||
config_json = Column(JSON, nullable=False, default={})
|
||||
environments = Column(JSON, nullable=False, default={})
|
||||
tools = Column(JSON, nullable=False, default=[])
|
||||
@@ -157,6 +158,9 @@ class MCPServer(Base):
|
||||
CheckConstraint(
|
||||
"type IN ('official', 'community')", name="check_mcp_server_type"
|
||||
),
|
||||
CheckConstraint(
|
||||
"config_type IN ('studio', 'sse')", name="check_mcp_server_config_type"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ class ToolConfig(BaseModel):
|
||||
class MCPServerBase(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
config_type: str = Field(default="studio")
|
||||
config_json: Dict[str, Any] = Field(default_factory=dict)
|
||||
environments: Dict[str, Any] = Field(default_factory=dict)
|
||||
tools: List[ToolConfig] = Field(default_factory=list)
|
||||
|
||||
@@ -12,6 +12,21 @@ import httpx
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _convert_uuid_to_str(obj):
|
||||
"""
|
||||
Recursively convert all UUID objects to strings in a dictionary, list or scalar value.
|
||||
This ensures JSON serialize for complex nested objects.
|
||||
"""
|
||||
if isinstance(obj, dict):
|
||||
return {key: _convert_uuid_to_str(value) for key, value in obj.items()}
|
||||
elif isinstance(obj, list):
|
||||
return [_convert_uuid_to_str(item) for item in obj]
|
||||
elif isinstance(obj, uuid.UUID):
|
||||
return str(obj)
|
||||
else:
|
||||
return obj
|
||||
|
||||
|
||||
def validate_sub_agents(db: Session, sub_agents: List[uuid.UUID]) -> bool:
|
||||
"""Validate if all sub-agents exist"""
|
||||
for agent_id in sub_agents:
|
||||
@@ -143,8 +158,13 @@ async def create_agent(db: Session, agent: AgentCreate) -> Agent:
|
||||
if "mcp_servers" in config:
|
||||
processed_servers = []
|
||||
for server in config["mcp_servers"]:
|
||||
# Convert server id to UUID if it's a string
|
||||
server_id = server["id"]
|
||||
if isinstance(server_id, str):
|
||||
server_id = uuid.UUID(server_id)
|
||||
|
||||
# Search for MCP server in the database
|
||||
mcp_server = get_mcp_server(db, server["id"])
|
||||
mcp_server = get_mcp_server(db, server_id)
|
||||
if not mcp_server:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
@@ -185,7 +205,22 @@ async def create_agent(db: Session, agent: AgentCreate) -> Agent:
|
||||
|
||||
agent.config = config
|
||||
|
||||
db_agent = Agent(**agent.model_dump())
|
||||
# Ensure all config objects are serializable (convert UUIDs to strings)
|
||||
if agent.config is not None:
|
||||
agent.config = _convert_uuid_to_str(agent.config)
|
||||
|
||||
# Convert agent to dict ensuring all UUIDs are converted to strings
|
||||
agent_dict = agent.model_dump()
|
||||
agent_dict = _convert_uuid_to_str(agent_dict)
|
||||
|
||||
# Create agent from the processed dictionary
|
||||
db_agent = Agent(**agent_dict)
|
||||
|
||||
# Make one final check to ensure all nested objects are serializable
|
||||
# (especially nested UUIDs in config)
|
||||
if db_agent.config is not None:
|
||||
db_agent.config = _convert_uuid_to_str(db_agent.config)
|
||||
|
||||
db.add(db_agent)
|
||||
db.commit()
|
||||
db.refresh(db_agent)
|
||||
@@ -195,9 +230,20 @@ async def create_agent(db: Session, agent: AgentCreate) -> Agent:
|
||||
except SQLAlchemyError as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error creating agent: {str(e)}")
|
||||
|
||||
# Add debugging info
|
||||
try:
|
||||
import json
|
||||
|
||||
if "agent_dict" in locals():
|
||||
agent_json = json.dumps(agent_dict)
|
||||
logger.info(f"Agent creation attempt with: {agent_json[:200]}...")
|
||||
except Exception as json_err:
|
||||
logger.error(f"Could not serialize agent for debugging: {str(json_err)}")
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Error creating agent",
|
||||
detail=f"Error creating agent: {str(e)}",
|
||||
)
|
||||
|
||||
|
||||
@@ -294,8 +340,13 @@ async def update_agent(
|
||||
if "mcp_servers" in config:
|
||||
processed_servers = []
|
||||
for server in config["mcp_servers"]:
|
||||
# Convert server id to UUID if it's a string
|
||||
server_id = server["id"]
|
||||
if isinstance(server_id, str):
|
||||
server_id = uuid.UUID(server_id)
|
||||
|
||||
# Search for MCP server in the database
|
||||
mcp_server = get_mcp_server(db, server["id"])
|
||||
mcp_server = get_mcp_server(db, server_id)
|
||||
if not mcp_server:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
@@ -336,6 +387,10 @@ async def update_agent(
|
||||
|
||||
agent_data["config"] = config
|
||||
|
||||
# Ensure all config objects are serializable (convert UUIDs to strings)
|
||||
if "config" in agent_data and agent_data["config"] is not None:
|
||||
agent_data["config"] = _convert_uuid_to_str(agent_data["config"])
|
||||
|
||||
for key, value in agent_data.items():
|
||||
setattr(agent, key, value)
|
||||
|
||||
@@ -348,21 +403,23 @@ async def update_agent(
|
||||
|
||||
|
||||
def delete_agent(db: Session, agent_id: uuid.UUID) -> bool:
|
||||
"""Remove an agent (soft delete)"""
|
||||
"""Remove an agent from the database"""
|
||||
try:
|
||||
db_agent = get_agent(db, agent_id)
|
||||
if not db_agent:
|
||||
return False
|
||||
|
||||
# Actually delete the agent from the database
|
||||
db.delete(db_agent)
|
||||
db.commit()
|
||||
logger.info(f"Agent deactivated successfully: {agent_id}")
|
||||
logger.info(f"Agent deleted successfully: {agent_id}")
|
||||
return True
|
||||
except SQLAlchemyError as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error deactivating agent {agent_id}: {str(e)}")
|
||||
logger.error(f"Error deleting agent {agent_id}: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Error deactivating agent",
|
||||
detail="Error deleting agent",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -11,20 +11,36 @@ from src.services.agent_service import get_agents_by_client
|
||||
|
||||
import uuid
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _session_to_dict(session: SessionModel):
|
||||
"""Convert Session model to dictionary with created_at field"""
|
||||
result = {
|
||||
"id": session.id,
|
||||
"app_name": session.app_name,
|
||||
"user_id": session.user_id,
|
||||
"state": session.state,
|
||||
"create_time": session.create_time,
|
||||
"update_time": session.update_time,
|
||||
"created_at": session.create_time,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def get_sessions_by_client(
|
||||
db: Session,
|
||||
client_id: uuid.UUID,
|
||||
) -> List[SessionModel]:
|
||||
) -> List[dict]:
|
||||
"""Search for sessions of a client with pagination"""
|
||||
try:
|
||||
agents_by_client = get_agents_by_client(db, client_id)
|
||||
sessions = []
|
||||
for agent in agents_by_client:
|
||||
sessions.extend(get_sessions_by_agent(db, agent.id))
|
||||
db_sessions = get_sessions_by_agent(db, agent.id)
|
||||
sessions.extend(db_sessions)
|
||||
|
||||
return sessions
|
||||
except SQLAlchemyError as e:
|
||||
@@ -40,13 +56,15 @@ def get_sessions_by_agent(
|
||||
agent_id: uuid.UUID,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
) -> List[SessionModel]:
|
||||
) -> List[dict]:
|
||||
"""Search for sessions of an agent with pagination"""
|
||||
try:
|
||||
agent_id_str = str(agent_id)
|
||||
query = db.query(SessionModel).filter(SessionModel.app_name == agent_id_str)
|
||||
|
||||
return query.offset(skip).limit(limit).all()
|
||||
db_sessions = query.offset(skip).limit(limit).all()
|
||||
# Convert each session to dictionary with created_at field
|
||||
return [_session_to_dict(session) for session in db_sessions]
|
||||
except SQLAlchemyError as e:
|
||||
logger.error(f"Error searching for sessions of agent {agent_id_str}: {str(e)}")
|
||||
raise HTTPException(
|
||||
|
||||
@@ -53,7 +53,7 @@ def create_user(
|
||||
try:
|
||||
# If not admin and no client_id, create an associated client
|
||||
if not is_admin and local_client_id is None:
|
||||
client = Client(name=user_data.name)
|
||||
client = Client(name=user_data.name, email=user_data.email)
|
||||
db.add(client)
|
||||
db.flush() # Get the client ID
|
||||
local_client_id = client.id
|
||||
|
||||
Reference in New Issue
Block a user