Skip to content
Merged
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
3 changes: 3 additions & 0 deletions python/valuecell/server/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .routers.websocket import create_websocket_router
from .routers.watchlist import create_watchlist_router
from .routers.agent_stream import create_agent_stream_router
from .routers.agent import create_agent_router
from .schemas import SuccessResponse, AppInfoData
from ...adapters.assets import get_adapter_manager

Expand Down Expand Up @@ -133,6 +134,8 @@ async def root():
app.include_router(create_watchlist_router())
# Include agent stream router
app.include_router(create_agent_stream_router(), prefix="/api/v1")
# Include agent router
app.include_router(create_agent_router(), prefix="/api/v1")


# For uvicorn
Expand Down
2 changes: 2 additions & 0 deletions python/valuecell/server/api/routers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

from .i18n import create_i18n_router, get_i18n_router
from .system import create_system_router
from .agent import create_agent_router

__all__ = [
"create_i18n_router",
"get_i18n_router",
"create_system_router",
"create_agent_router",
]
126 changes: 126 additions & 0 deletions python/valuecell/server/api/routers/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""
Agent API router for handling agent-related endpoints.
"""

from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query, Path
from sqlalchemy.orm import Session

from valuecell.server.db import get_db
from valuecell.server.services.agent_service import AgentService
from valuecell.server.api.schemas.agent import AgentListResponse, AgentResponse
from valuecell.server.api.schemas.base import SuccessResponse


def create_agent_router() -> APIRouter:
"""Create and configure the agent router."""

router = APIRouter(
prefix="/agents",
tags=["agents"],
responses={404: {"description": "Not found"}},
)

@router.get(
"/",
response_model=AgentListResponse,
summary="Get all agents",
description="Get a list of all agents in the system, including basic information",
)
async def get_agents(
enabled_only: bool = Query(False, description="Return only enabled agents"),
name_filter: Optional[str] = Query(
None, description="Filter agents by name (supports fuzzy matching)"
),
db: Session = Depends(get_db),
) -> AgentListResponse:
"""
Get all agents list.

- **enabled_only**: If True, return only enabled agents
- **name_filter**: Filter by agent name or display name with fuzzy matching

Returns a response containing the agent list and statistics.
"""
try:
agent_list_data = AgentService.get_all_agents(
db=db, enabled_only=enabled_only, name_filter=name_filter
)
return SuccessResponse.create(
data=agent_list_data,
msg=f"Successfully retrieved {agent_list_data.total} agents",
)
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Failed to retrieve agent list: {str(e)}"
)

@router.get(
"/{agent_id}",
response_model=AgentResponse,
summary="Get agent by ID",
description="Get detailed information of an agent by its ID",
)
async def get_agent_by_id(
agent_id: int = Path(..., description="Unique identifier of the agent"),
db: Session = Depends(get_db),
) -> AgentResponse:
"""
Get detailed information of a specific agent by ID.

- **agent_id**: Unique identifier of the agent

Returns detailed agent information, or 404 error if agent doesn't exist.
"""
try:
agent = AgentService.get_agent_by_id(db=db, agent_id=agent_id)
if not agent:
raise HTTPException(
status_code=404, detail=f"Agent with ID {agent_id} not found"
)
return SuccessResponse.create(
data=agent, msg="Successfully retrieved agent information"
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Failed to retrieve agent information: {str(e)}",
)

@router.get(
"/by-name/{agent_name}",
response_model=AgentResponse,
summary="Get agent by name",
description="Get detailed information of an agent by its name",
)
async def get_agent_by_name(
agent_name: str = Path(..., description="Name of the agent"),
db: Session = Depends(get_db),
) -> AgentResponse:
"""
Get detailed information of a specific agent by name.

- **agent_name**: Name of the agent

Returns detailed agent information, or 404 error if agent doesn't exist.
"""
try:
agent = AgentService.get_agent_by_name(db=db, agent_name=agent_name)
if not agent:
raise HTTPException(
status_code=404, detail=f"Agent with name '{agent_name}' not found"
)
return SuccessResponse.create(
data=agent, msg="Successfully retrieved agent information"
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Failed to retrieve agent information: {str(e)}",
)

return router
108 changes: 108 additions & 0 deletions python/valuecell/server/api/schemas/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Agent API schemas for handling agent-related requests and responses.
"""

from typing import List, Optional, Dict, Any
from pydantic import BaseModel, Field
from datetime import datetime
from .base import SuccessResponse


class AgentCapabilities(BaseModel):
"""Agent capabilities model."""

streaming: bool = Field(False, description="Whether the agent supports streaming")
push_notifications: bool = Field(
False, description="Whether the agent supports push notifications"
)


class AgentMetadata(BaseModel):
"""Agent metadata model."""

version: Optional[str] = Field(None, description="Agent version")
author: Optional[str] = Field(None, description="Agent author")
tags: Optional[List[str]] = Field(None, description="Agent tags")


class AgentData(BaseModel):
"""Data model for a single agent."""

id: int = Field(..., description="Agent unique ID")
name: str = Field(..., description="Agent unique name/identifier")
display_name: Optional[str] = Field(None, description="Human-readable display name")
description: Optional[str] = Field(None, description="Agent description")
version: Optional[str] = Field(None, description="Agent version")
enabled: bool = Field(..., description="Whether the agent is enabled")
agent_metadata: Optional[Dict[str, Any]] = Field(None, description="Agent metadata")
config: Optional[Dict[str, Any]] = Field(None, description="Agent configuration")
created_at: Optional[datetime] = Field(None, description="Creation timestamp")
updated_at: Optional[datetime] = Field(None, description="Last update timestamp")

class Config:
json_schema_extra = {
"example": {
"id": 1,
"name": "MarketAnalystAgent",
"display_name": "Market Analyst Agent",
"description": "AI-powered market analysis agent",
"version": "1.0.0",
"enabled": True,
"agent_metadata": {
"author": "ValueCell Team",
"tags": ["market", "analysis", "ai"],
},
"config": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
}
}


class AgentListData(BaseModel):
"""Data model for agent list."""

agents: List[AgentData] = Field(..., description="List of agents")
total: int = Field(..., description="Total number of agents")
enabled_count: int = Field(..., description="Number of enabled agents")

class Config:
json_schema_extra = {
"example": {
"agents": [
{
"id": 1,
"name": "MarketAnalystAgent",
"display_name": "Market Analyst Agent",
"description": "AI-powered market analysis agent",
"version": "1.0.0",
"enabled": True,
"agent_metadata": {
"author": "ValueCell Team",
"tags": ["market", "analysis", "ai"],
},
"config": {},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
}
],
"total": 1,
"enabled_count": 1,
}
}


class AgentQueryParams(BaseModel):
"""Query parameters for agent list."""

enabled_only: Optional[bool] = Field(
False, description="Filter only enabled agents"
)
name_filter: Optional[str] = Field(
None, description="Filter by agent name (partial match)"
)


# Type aliases for SuccessResponse
AgentResponse = SuccessResponse[AgentData]
AgentListResponse = SuccessResponse[AgentListData]
8 changes: 0 additions & 8 deletions python/valuecell/server/db/init_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ def initialize_assets_with_service(self) -> bool:
symbol=asset_ticker,
name=asset_data["display_name"],
asset_type=asset_data["asset_type"],
is_active=True,
asset_metadata={
"exchange": asset_data.get("exchange")
or ticker.split(":")[0],
Expand All @@ -227,7 +226,6 @@ def initialize_assets_with_service(self) -> bool:
# Update existing asset with adapter data
existing_asset.name = asset_data["display_name"]
existing_asset.asset_type = asset_data["asset_type"]
existing_asset.is_active = True
# Update existing asset metadata
existing_metadata = existing_asset.asset_metadata or {}
existing_metadata.update(
Expand Down Expand Up @@ -426,7 +424,6 @@ def initialize_basic_data(self) -> bool:
"description": "AI-powered hedge fund analysis and trading agent",
"version": "1.0.0",
"enabled": True,
"is_active": True,
"capabilities": {
"streaming": False,
"push_notifications": False,
Expand All @@ -443,7 +440,6 @@ def initialize_basic_data(self) -> bool:
"description": "SEC 13F fund analysis and tracking agent",
"version": "1.0.0",
"enabled": True,
"is_active": True,
"capabilities": {
"streaming": False,
"push_notifications": False,
Expand All @@ -460,7 +456,6 @@ def initialize_basic_data(self) -> bool:
"description": "TradingAgents - Multi-agent trading analysis system with market, sentiment, news and fundamentals analysis",
"version": "1.0.0",
"enabled": True,
"is_active": True,
"capabilities": {
"streaming": True,
"push_notifications": False,
Expand Down Expand Up @@ -531,9 +526,6 @@ def initialize_basic_data(self) -> bool:
existing_agent.enabled = agent_data.get(
"enabled", existing_agent.enabled
)
existing_agent.is_active = agent_data.get(
"is_active", existing_agent.is_active
)
existing_agent.capabilities = agent_data.get(
"capabilities", existing_agent.capabilities
)
Expand Down
10 changes: 1 addition & 9 deletions python/valuecell/server/db/models/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,6 @@ class Agent(Base):
nullable=False,
comment="Whether the agent is currently enabled",
)
is_active = Column(
Boolean,
default=True,
nullable=False,
comment="Whether the agent is active and available",
)

# Capabilities and metadata
capabilities = Column(
Expand Down Expand Up @@ -100,7 +94,6 @@ def to_dict(self) -> Dict[str, Any]:
"description": self.description,
"version": self.version,
"enabled": self.enabled,
"is_active": self.is_active,
"capabilities": self.capabilities,
"agent_metadata": self.agent_metadata,
"config": self.config,
Expand All @@ -113,11 +106,10 @@ def from_config(cls, config_data: Dict[str, Any]) -> "Agent":
"""Create an Agent instance from configuration data."""
return cls(
name=config_data.get("name"),
display_name=config_data.get("display_name", config_data.get("name")),
display_name=config_data.get("display_name"),
description=config_data.get("description"),
version=config_data.get("version", "1.0.0"),
enabled=config_data.get("enabled", True),
is_active=config_data.get("is_active", True),
capabilities=config_data.get("capabilities"),
agent_metadata=config_data.get("metadata"),
config=config_data.get("config"),
Expand Down
Loading