diff --git a/python/valuecell/services/application/__init__.py b/python/valuecell/server/__init__.py similarity index 100% rename from python/valuecell/services/application/__init__.py rename to python/valuecell/server/__init__.py diff --git a/python/valuecell/server/api/__init__.py b/python/valuecell/server/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/valuecell/server/api/app.py b/python/valuecell/server/api/app.py new file mode 100644 index 000000000..09c050997 --- /dev/null +++ b/python/valuecell/server/api/app.py @@ -0,0 +1,59 @@ +"""FastAPI application factory for ValueCell Server.""" + +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from contextlib import asynccontextmanager + +from ..config.settings import get_settings + + +def create_app() -> FastAPI: + """Create and configure FastAPI application.""" + settings = get_settings() + + @asynccontextmanager + async def lifespan(app: FastAPI): + # Startup + print( + f"ValueCell Server starting up on {settings.API_HOST}:{settings.API_PORT}..." + ) + yield + # Shutdown + print("ValueCell Server shutting down...") + + app = FastAPI( + title="ValueCell Server API", + description="A community-driven, multi-agent platform for financial applications", + version=settings.APP_VERSION, + lifespan=lifespan, + docs_url="/docs" if settings.API_DEBUG else None, + redoc_url="/redoc" if settings.API_DEBUG else None, + ) + + # Add middleware + _add_middleware(app, settings) + + # Add routes + _add_routes(app) + + return app + + +def _add_middleware(app: FastAPI, settings) -> None: + """Add middleware to the application.""" + # CORS middleware + app.add_middleware( + CORSMiddleware, + allow_origins=settings.CORS_ORIGINS, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + # Custom logging middleware removed + + +def _add_routes(app: FastAPI) -> None: + """Add routes to the application.""" + # app.include_router(health.router, prefix="/health", tags=["health"]) + # app.include_router(agents.router, prefix="/api/v1", tags=["agents"]) diff --git a/python/valuecell/server/api/routers/__init__.py b/python/valuecell/server/api/routers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/valuecell/server/api/schemas/__init__.py b/python/valuecell/server/api/schemas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/valuecell/server/config/__init__.py b/python/valuecell/server/config/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/valuecell/server/config/settings.py b/python/valuecell/server/config/settings.py new file mode 100644 index 000000000..2c21b775b --- /dev/null +++ b/python/valuecell/server/config/settings.py @@ -0,0 +1,85 @@ +"""Settings configuration for ValueCell Server.""" + +import os +from pathlib import Path +from functools import lru_cache + + +class Settings: + """Server configuration settings.""" + + def __init__(self): + """Initialize settings from environment variables.""" + # Application Configuration + self.APP_NAME = os.getenv("APP_NAME", "ValueCell Server") + self.APP_VERSION = os.getenv("APP_VERSION", "0.1.0") + self.APP_ENVIRONMENT = os.getenv("APP_ENVIRONMENT", "development") + + # API Configuration + self.API_HOST = os.getenv("API_HOST", "localhost") + self.API_PORT = int(os.getenv("API_PORT", "8000")) + self.API_DEBUG = os.getenv("API_DEBUG", "false").lower() == "true" + + # CORS Configuration + cors_origins = os.getenv("CORS_ORIGINS", "*") + self.CORS_ORIGINS = cors_origins.split(",") if cors_origins != "*" else ["*"] + + # Database Configuration + self.DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./valuecell.db") + self.DB_ECHO = os.getenv("DB_ECHO", "false").lower() == "true" + + # Redis Configuration + self.REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379") + + # Security Configuration + self.SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key-here") + self.ACCESS_TOKEN_EXPIRE_MINUTES = int( + os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30") + ) + + # Logging Configuration + self.LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO") + self.LOG_FORMAT = os.getenv("LOG_FORMAT", "json") + + # File Paths + self.BASE_DIR = Path(__file__).parent.parent.parent + self.LOGS_DIR = self.BASE_DIR / "logs" + self.LOGS_DIR.mkdir(exist_ok=True) + + # Agent Configuration + self.AGENT_TIMEOUT = int(os.getenv("AGENT_TIMEOUT", "300")) # 5 minutes + self.MAX_CONCURRENT_AGENTS = int(os.getenv("MAX_CONCURRENT_AGENTS", "10")) + + # External APIs + self.OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") + self.FINNHUB_API_KEY = os.getenv("FINNHUB_API_KEY") + self.ALPHA_VANTAGE_API_KEY = os.getenv("ALPHA_VANTAGE_API_KEY") + + @property + def is_development(self) -> bool: + """Check if running in development mode.""" + return self.APP_ENVIRONMENT == "development" + + @property + def is_production(self) -> bool: + """Check if running in production mode.""" + return self.APP_ENVIRONMENT == "production" + + def get_database_config(self) -> dict: + """Get database configuration.""" + return { + "url": self.DATABASE_URL, + "echo": self.DB_ECHO, + } + + def get_redis_config(self) -> dict: + """Get Redis configuration.""" + return { + "url": self.REDIS_URL, + } + + +@lru_cache() +def get_settings() -> Settings: + """Get cached settings instance.""" + return Settings() diff --git a/python/valuecell/server/db/__init__.py b/python/valuecell/server/db/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/valuecell/server/db/models/__init__.py b/python/valuecell/server/db/models/__init__.py new file mode 100644 index 000000000..7fde07179 --- /dev/null +++ b/python/valuecell/server/db/models/__init__.py @@ -0,0 +1,5 @@ +"""Database models for ValueCell Server.""" + +from .base import Base + +__all__ = ["Base"] diff --git a/python/valuecell/server/db/models/base.py b/python/valuecell/server/db/models/base.py new file mode 100644 index 000000000..2efbec3f2 --- /dev/null +++ b/python/valuecell/server/db/models/base.py @@ -0,0 +1,10 @@ +"""Base model for ValueCell Server.""" + +from sqlalchemy.ext.declarative import declarative_base + +# Create the base class for all models +Base = declarative_base() + +# Alternative approach using modern SQLAlchemy 2.0 style +# class Base(DeclarativeBase): +# pass diff --git a/python/valuecell/server/db/repositories/__init__.py b/python/valuecell/server/db/repositories/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/valuecell/server/main.py b/python/valuecell/server/main.py new file mode 100644 index 000000000..826a100cd --- /dev/null +++ b/python/valuecell/server/main.py @@ -0,0 +1,22 @@ +"""Main entry point for ValueCell Server Backend.""" + +import uvicorn +from .api.app import create_app +from .config.settings import get_settings + + +def main(): + """Start the server.""" + settings = get_settings() + app = create_app() + + uvicorn.run( + app, + host=settings.API_HOST, + port=settings.API_PORT, + reload=settings.API_DEBUG, + ) + + +if __name__ == "__main__": + main() diff --git a/python/valuecell/server/services/__init__.py b/python/valuecell/server/services/__init__.py new file mode 100644 index 000000000..e69de29bb