Add langfuse and update logger (#952)

Co-authored-by: bytecraftii <bytecraftii@users.noreply.github.com>
Co-authored-by: Wendong-Fan <w3ndong.fan@gmail.com>
This commit is contained in:
bytecraftii 2026-01-24 16:13:07 -08:00 committed by GitHub
parent cf571b73a4
commit 49e148a2f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
63 changed files with 3095 additions and 2430 deletions

View file

@ -21,17 +21,16 @@ from fastapi_babel import _
from sqlmodel import Session, select, desc, case
from app.component.auth import Auth, auth_must
from app.component.database import session
from utils import traceroot_wrapper as traceroot
import logging
from typing import Optional, Dict, List
from collections import defaultdict
logger = traceroot.get_logger("server_chat_history")
logger = logging.getLogger("server_chat_history")
router = APIRouter(prefix="/chat", tags=["Chat History"])
@router.post("/history", name="save chat history", response_model=ChatHistoryOut)
@traceroot.trace()
def create_chat_history(data: ChatHistoryIn, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Save new chat history."""
user_id = auth.user.id
@ -51,7 +50,6 @@ def create_chat_history(data: ChatHistoryIn, session: Session = Depends(session)
@router.get("/histories", name="get chat history")
@traceroot.trace()
def list_chat_history(session: Session = Depends(session), auth: Auth = Depends(auth_must)) -> Page[ChatHistoryOut]:
"""List chat histories for current user."""
user_id = auth.user.id
@ -75,7 +73,6 @@ def list_chat_history(session: Session = Depends(session), auth: Auth = Depends(
@router.get("/histories/grouped", name="get grouped chat history")
@traceroot.trace()
def list_grouped_chat_history(
include_tasks: Optional[bool] = Query(True, description="Whether to include individual tasks in groups"),
session: Session = Depends(session),
@ -177,7 +174,6 @@ def list_grouped_chat_history(
@router.delete("/history/{history_id}", name="delete chat history")
@traceroot.trace()
def delete_chat_history(history_id: str, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Delete chat history."""
user_id = auth.user.id
@ -203,7 +199,6 @@ def delete_chat_history(history_id: str, session: Session = Depends(session), au
@router.put("/history/{history_id}", name="update chat history", response_model=ChatHistoryOut)
@traceroot.trace()
def update_chat_history(
history_id: int, data: ChatHistoryUpdate, session: Session = Depends(session), auth: Auth = Depends(auth_must)
):
@ -232,7 +227,6 @@ def update_chat_history(
@router.put("/project/{project_id}/name", name="update project name")
@traceroot.trace()
def update_project_name(
project_id: str,
new_name: str,

View file

@ -22,15 +22,14 @@ from starlette.responses import StreamingResponse
from app.model.chat.chat_share import ChatHistoryShareOut, ChatShare, ChatShareIn
from app.model.chat.chat_step import ChatStep
from app.model.chat.chat_history import ChatHistory
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_chat_share")
logger = logging.getLogger("server_chat_share")
router = APIRouter(prefix="/chat", tags=["Chat Share"])
@router.get("/share/info/{token}", name="Get shared chat info", response_model=ChatHistoryShareOut)
@traceroot.trace()
def get_share_info(token: str, session: Session = Depends(session)):
"""
Get shared chat history info by token, excluding sensitive data.
@ -56,7 +55,6 @@ def get_share_info(token: str, session: Session = Depends(session)):
@router.get("/share/playback/{token}", name="Playback shared chat via SSE")
@traceroot.trace()
async def share_playback(token: str, session: Session = Depends(session), delay_time: float = 0):
"""
Playbacks the chat history via a sharing token (SSE).
@ -109,7 +107,6 @@ async def share_playback(token: str, session: Session = Depends(session), delay_
@router.post("/share", name="Generate sharable link for a task(1 day expiration)")
@traceroot.trace()
def create_share_link(data: ChatShareIn):
"""Generate sharing token with 1-day expiration for task."""
try:

View file

@ -19,15 +19,14 @@ from sqlmodel import Session, select
from app.component.database import session
from app.component.auth import Auth, auth_must
from fastapi_babel import _
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_chat_snapshot")
logger = logging.getLogger("server_chat_snapshot")
router = APIRouter(prefix="/chat", tags=["Chat Snapshot Management"])
@router.get("/snapshots", name="list chat snapshots", response_model=List[ChatSnapshot])
@traceroot.trace()
async def list_chat_snapshots(
api_task_id: Optional[str] = None,
camel_task_id: Optional[str] = None,
@ -49,7 +48,6 @@ async def list_chat_snapshots(
@router.get("/snapshots/{snapshot_id}", name="get chat snapshot", response_model=ChatSnapshot)
@traceroot.trace()
async def get_chat_snapshot(snapshot_id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Get specific chat snapshot."""
user_id = auth.user.id
@ -64,7 +62,6 @@ async def get_chat_snapshot(snapshot_id: int, session: Session = Depends(session
@router.post("/snapshots", name="create chat snapshot", response_model=ChatSnapshot)
@traceroot.trace()
async def create_chat_snapshot(
snapshot: ChatSnapshotIn, auth: Auth = Depends(auth_must), session: Session = Depends(session)
):
@ -92,7 +89,6 @@ async def create_chat_snapshot(
@router.put("/snapshots/{snapshot_id}", name="update chat snapshot", response_model=ChatSnapshot)
@traceroot.trace()
async def update_chat_snapshot(
snapshot_id: int,
snapshot_update: ChatSnapshot,
@ -127,7 +123,6 @@ async def update_chat_snapshot(
@router.delete("/snapshots/{snapshot_id}", name="delete chat snapshot")
@traceroot.trace()
async def delete_chat_snapshot(snapshot_id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Delete chat snapshot."""
user_id = auth.user.id

View file

@ -23,15 +23,14 @@ from app.component.database import session
from app.component.auth import Auth, auth_must
from fastapi_babel import _
from app.model.chat.chat_step import ChatStep, ChatStepOut, ChatStepIn
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_chat_step")
logger = logging.getLogger("server_chat_step")
router = APIRouter(prefix="/chat", tags=["Chat Step Management"])
@router.get("/steps", name="list chat steps", response_model=List[ChatStepOut])
@traceroot.trace()
async def list_chat_steps(
task_id: str, step: Optional[str] = None, session: Session = Depends(session), auth: Auth = Depends(auth_must)
):
@ -49,7 +48,6 @@ async def list_chat_steps(
@router.get("/steps/playback/{task_id}", name="Playback Chat Step via SSE")
@traceroot.trace()
async def share_playback(
task_id: str, delay_time: float = 0, session: Session = Depends(session), auth: Auth = Depends(auth_must)
):
@ -97,7 +95,6 @@ async def share_playback(
@router.get("/steps/{step_id}", name="get chat step", response_model=ChatStepOut)
@traceroot.trace()
async def get_chat_step(step_id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Get specific chat step."""
user_id = auth.user.id
@ -112,7 +109,6 @@ async def get_chat_step(step_id: int, session: Session = Depends(session), auth:
@router.post("/steps", name="create chat step")
@traceroot.trace()
async def create_chat_step(step: ChatStepIn, session: Session = Depends(session)):
"""Create new chat step. TODO: Implement request source validation."""
try:
@ -134,7 +130,6 @@ async def create_chat_step(step: ChatStepIn, session: Session = Depends(session)
@router.put("/steps/{step_id}", name="update chat step", response_model=ChatStepOut)
@traceroot.trace()
async def update_chat_step(
step_id: int, chat_step_update: ChatStep, session: Session = Depends(session), auth: Auth = Depends(auth_must)
):
@ -162,7 +157,6 @@ async def update_chat_step(
@router.delete("/steps/{step_id}", name="delete chat step")
@traceroot.trace()
async def delete_chat_step(step_id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Delete chat step."""
user_id = auth.user.id

View file

@ -19,15 +19,14 @@ from app.component.database import session
from app.component.auth import Auth, auth_must
from fastapi_babel import _
from app.model.config.config import Config, ConfigCreate, ConfigUpdate, ConfigInfo, ConfigOut
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_config_controller")
logger = logging.getLogger("server_config_controller")
router = APIRouter(tags=["Config Management"])
@router.get("/configs", name="list configs", response_model=list[ConfigOut])
@traceroot.trace()
async def list_configs(
config_group: Optional[str] = None, session: Session = Depends(session), auth: Auth = Depends(auth_must)
):
@ -44,7 +43,6 @@ async def list_configs(
@router.get("/configs/{config_id}", name="get config", response_model=ConfigOut)
@traceroot.trace()
async def get_config(
config_id: int,
session: Session = Depends(session),
@ -66,7 +64,6 @@ async def get_config(
@router.post("/configs", name="create config", response_model=ConfigOut)
@traceroot.trace()
async def create_config(config: ConfigCreate, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Create new configuration."""
user_id = auth.user.id
@ -103,7 +100,6 @@ async def create_config(config: ConfigCreate, session: Session = Depends(session
@router.put("/configs/{config_id}", name="update config", response_model=ConfigOut)
@traceroot.trace()
async def update_config(
config_id: int, config_update: ConfigUpdate, session: Session = Depends(session), auth: Auth = Depends(auth_must)
):
@ -149,7 +145,6 @@ async def update_config(
@router.delete("/configs/{config_id}", name="delete config")
@traceroot.trace()
async def delete_config(config_id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Delete configuration."""
user_id = auth.user.id
@ -171,7 +166,6 @@ async def delete_config(config_id: int, session: Session = Depends(session), aut
@router.get("/config/info", name="get config info")
@traceroot.trace()
async def get_config_info(
show_all: bool = Query(False, description="Show all config info, including those with empty env_vars"),
):

View file

@ -27,9 +27,9 @@ from app.model.mcp.mcp_env import McpEnv, Status as McpEnvStatus
from app.model.mcp.mcp_user import McpImportType, McpUser, Status
from camel.toolkits.mcp_toolkit import MCPToolkit
from app.component.environment import env
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_mcp_controller")
logger = logging.getLogger("server_mcp_controller")
from app.component.validator.McpServer import (
McpRemoteServer,
@ -81,7 +81,6 @@ async def pre_instantiate_mcp_toolkit(config_dict: dict) -> bool:
@router.get("/mcps", name="mcp list")
@traceroot.trace()
async def gets(
keyword: str | None = None,
category_id: int | None = None,
@ -121,7 +120,6 @@ async def gets(
@router.get("/mcp", name="mcp detail", response_model=McpOut)
@traceroot.trace()
async def get(id: int, session: Session = Depends(session)):
"""Get MCP server details."""
try:
@ -135,7 +133,6 @@ async def get(id: int, session: Session = Depends(session)):
@router.post("/mcp/install", name="mcp install")
@traceroot.trace()
async def install(mcp_id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Install MCP server for user."""
user_id = auth.user.id
@ -191,7 +188,6 @@ async def install(mcp_id: int, session: Session = Depends(session), auth: Auth =
@router.post("/mcp/import/{mcp_type}", name="mcp import")
@traceroot.trace()
async def import_mcp(
mcp_type: McpImportType, mcp_data: dict, session: Session = Depends(session), auth: Auth = Depends(auth_must)
):

View file

@ -19,9 +19,9 @@ from app.component.environment import env_not_empty
from app.model.mcp.proxy import ExaSearch
from typing import Any, cast
import requests
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_proxy_controller")
logger = logging.getLogger("server_proxy_controller")
from app.model.user.key import Key
@ -30,7 +30,6 @@ router = APIRouter(prefix="/proxy", tags=["Mcp Servers"])
@router.post("/exa")
@traceroot.trace()
def exa_search(search: ExaSearch, key: Key = Depends(key_must)):
"""Search using Exa API."""
EXA_API_KEY = env_not_empty("EXA_API_KEY")
@ -100,7 +99,6 @@ def exa_search(search: ExaSearch, key: Key = Depends(key_must)):
@router.get("/google")
@traceroot.trace()
def google_search(query: str, search_type: str = "web", key: Key = Depends(key_must)):
"""Search using Google Custom Search API."""
# https://developers.google.com/custom-search/v1/overview

View file

@ -23,9 +23,9 @@ from app.model.mcp.mcp_user import McpUser, McpUserIn, McpUserOut, McpUserUpdate
from app.model.mcp.mcp import Mcp
from camel.toolkits.mcp_toolkit import MCPToolkit
from app.component.environment import env
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_mcp_user_controller")
logger = logging.getLogger("server_mcp_user_controller")
router = APIRouter(tags=["McpUser Management"])
@ -70,7 +70,6 @@ async def pre_instantiate_mcp_toolkit(config_dict: dict) -> bool:
@router.get("/mcp/users", name="list mcp users", response_model=List[McpUserOut])
@traceroot.trace()
async def list_mcp_users(
mcp_id: Optional[int] = None,
session: Session = Depends(session),
@ -89,7 +88,6 @@ async def list_mcp_users(
@router.get("/mcp/users/{mcp_user_id}", name="get mcp user", response_model=McpUserOut)
@traceroot.trace()
async def get_mcp_user(mcp_user_id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Get MCP user details."""
query = select(McpUser).where(McpUser.id == mcp_user_id)
@ -102,7 +100,6 @@ async def get_mcp_user(mcp_user_id: int, session: Session = Depends(session), au
@router.post("/mcp/users", name="create mcp user", response_model=McpUserOut)
@traceroot.trace()
async def create_mcp_user(mcp_user: McpUserIn, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Create MCP user installation."""
user_id = auth.user.id
@ -145,7 +142,6 @@ async def create_mcp_user(mcp_user: McpUserIn, session: Session = Depends(sessio
@router.put("/mcp/users/{id}", name="update mcp user")
@traceroot.trace()
async def update_mcp_user(
id: int,
update_item: McpUserUpdate,
@ -175,7 +171,6 @@ async def update_mcp_user(
@router.delete("/mcp/users/{mcp_user_id}", name="delete mcp user")
@traceroot.trace()
async def delete_mcp_user(mcp_user_id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Delete MCP user installation."""
user_id = auth.user.id

View file

@ -17,15 +17,14 @@ from fastapi.responses import RedirectResponse, JSONResponse, HTMLResponse
from app.component.environment import env
from app.component.oauth_adapter import OauthCallbackPayload, get_oauth_adapter
from typing import Optional
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_oauth_controller")
logger = logging.getLogger("server_oauth_controller")
router = APIRouter(prefix="/oauth", tags=["Oauth Servers"])
@router.get("/{app}/login", name="OAuth Login Redirect")
@traceroot.trace()
def oauth_login(app: str, request: Request, state: Optional[str] = None):
"""Redirect user to OAuth provider's authorization endpoint."""
try:
@ -50,7 +49,6 @@ def oauth_login(app: str, request: Request, state: Optional[str] = None):
@router.get("/{app}/callback", name="OAuth Callback")
@traceroot.trace()
def oauth_callback(app: str, request: Request, code: Optional[str] = None, state: Optional[str] = None):
"""Handle OAuth provider callback and redirect to client app."""
if not code:
@ -78,7 +76,6 @@ def oauth_callback(app: str, request: Request, code: Optional[str] = None, state
@router.post("/{app}/token", name="OAuth Fetch Token")
@traceroot.trace()
def fetch_token(app: str, request: Request, data: OauthCallbackPayload):
"""Exchange authorization code for access token."""
try:

View file

@ -24,15 +24,14 @@ from sqlalchemy.exc import SQLAlchemyError
from app.component.database import session
from app.component.auth import Auth, auth_must
from app.model.provider.provider import Provider, ProviderIn, ProviderOut, ProviderPreferIn
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_provider_controller")
logger = logging.getLogger("server_provider_controller")
router = APIRouter(tags=["Provider Management"])
@router.get("/providers", name="list providers", response_model=Page[ProviderOut])
@traceroot.trace()
async def gets(
keyword: str | None = None,
prefer: Optional[bool] = Query(None, description="Filter by prefer status"),
@ -52,7 +51,6 @@ async def gets(
@router.get("/provider", name="get provider detail", response_model=ProviderOut)
@traceroot.trace()
async def get(id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Get provider details."""
user_id = auth.user.id
@ -66,7 +64,6 @@ async def get(id: int, session: Session = Depends(session), auth: Auth = Depends
@router.post("/provider", name="create provider", response_model=ProviderOut)
@traceroot.trace()
async def post(data: ProviderIn, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Create a new provider."""
user_id = auth.user.id
@ -81,7 +78,6 @@ async def post(data: ProviderIn, session: Session = Depends(session), auth: Auth
@router.put("/provider/{id}", name="update provider", response_model=ProviderOut)
@traceroot.trace()
async def put(id: int, data: ProviderIn, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Update provider details."""
user_id = auth.user.id
@ -109,7 +105,6 @@ async def put(id: int, data: ProviderIn, session: Session = Depends(session), au
@router.delete("/provider/{id}", name="delete provider")
@traceroot.trace()
async def delete(id: int, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Delete a provider."""
user_id = auth.user.id
@ -130,7 +125,6 @@ async def delete(id: int, session: Session = Depends(session), auth: Auth = Depe
@router.post("/provider/prefer", name="set provider prefer")
@traceroot.trace()
async def set_prefer(data: ProviderPreferIn, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Set preferred provider for user."""
user_id = auth.user.id

View file

@ -29,16 +29,15 @@ from app.model.user.user import (
RegisterIn,
)
from app.component.environment import env
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_login_controller")
logger = logging.getLogger("server_login_controller")
router = APIRouter(tags=["Login/Registration"])
@router.post("/login", name="login by email or password")
@traceroot.trace()
async def by_password(
data: LoginByPasswordIn, session: Session = Depends(session)
) -> LoginResponse:
@ -63,7 +62,6 @@ async def by_password(
@router.post("/dev_login", name="OAuth2 password flow login (for Swagger UI)")
@traceroot.trace()
async def dev_login(
username: str = Form(...), # OAuth2 uses 'username' but we accept email
password: str = Form(...),
@ -96,7 +94,6 @@ async def dev_login(
@router.post("/login-by_stack", name="login by stack")
@traceroot.trace()
async def by_stack_auth(
token: str,
type: str = "signup",
@ -169,7 +166,6 @@ async def by_stack_auth(
@router.post("/register", name="register by email/password")
@traceroot.trace()
async def register(data: RegisterIn, session: Session = Depends(session)):
email = data.email

View file

@ -25,15 +25,14 @@ from app.model.mcp.mcp_user import McpUser
from app.model.config.config import Config
from app.model.chat.chat_snpshot import ChatSnapshot
from app.model.user.user_credits_record import UserCreditsRecord
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_user_controller")
logger = logging.getLogger("server_user_controller")
router = APIRouter(tags=["User"])
@router.get("/user", name="user info", response_model=UserOut)
@traceroot.trace()
def get(auth: Auth = Depends(auth_must), session: Session = Depends(session)):
"""Get current user information and refresh credits."""
user: User = auth.user
@ -43,7 +42,6 @@ def get(auth: Auth = Depends(auth_must), session: Session = Depends(session)):
@router.put("/user", name="update user info", response_model=UserOut)
@traceroot.trace()
def put(data: UserIn, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Update user basic information."""
model = auth.user
@ -54,7 +52,6 @@ def put(data: UserIn, session: Session = Depends(session), auth: Auth = Depends(
@router.put("/user/profile", name="update user profile", response_model=UserProfile)
@traceroot.trace()
def put_profile(data: UserProfile, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Update user profile details."""
model = auth.user
@ -67,7 +64,6 @@ def put_profile(data: UserProfile, session: Session = Depends(session), auth: Au
@router.get("/user/privacy", name="get user privacy")
@traceroot.trace()
def get_privacy(session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Get user privacy settings."""
user_id = auth.user.id
@ -83,7 +79,6 @@ def get_privacy(session: Session = Depends(session), auth: Auth = Depends(auth_m
@router.put("/user/privacy", name="update user privacy")
@traceroot.trace()
def put_privacy(data: UserPrivacySettings, session: Session = Depends(session), auth: Auth = Depends(auth_must)):
"""Update user privacy settings."""
user_id = auth.user.id
@ -104,7 +99,6 @@ def put_privacy(data: UserPrivacySettings, session: Session = Depends(session),
@router.get("/user/current_credits", name="get user current credits")
@traceroot.trace()
def get_user_credits(auth: Auth = Depends(auth_must), session: Session = Depends(session)):
"""Get user's current credit balance."""
user = auth.user
@ -121,7 +115,6 @@ def get_user_credits(auth: Auth = Depends(auth_must), session: Session = Depends
@router.get("/user/stat", name="get user stat", response_model=UserStatOut)
@traceroot.trace()
def get_user_stat(auth: Auth = Depends(auth_must), session: Session = Depends(session)):
"""Get current user's operation statistics."""
user_id = auth.user.id
@ -152,7 +145,6 @@ def get_user_stat(auth: Auth = Depends(auth_must), session: Session = Depends(se
@router.post("/user/stat", name="record user stat")
@traceroot.trace()
def record_user_stat(
data: UserStatActionIn,
auth: Auth = Depends(auth_must),

View file

@ -22,15 +22,14 @@ from app.component.encrypt import password_hash, password_verify
from app.exception.exception import UserException
from app.model.user.user import UpdatePassword, UserOut
from fastapi_babel import _
from utils import traceroot_wrapper as traceroot
import logging
logger = traceroot.get_logger("server_password_controller")
logger = logging.getLogger("server_password_controller")
router = APIRouter(tags=["User"])
@router.put("/user/update-password", name="update password", response_model=UserOut)
@traceroot.trace()
def update_password(data: UpdatePassword, auth: Auth = Depends(auth_must), session: Session = Depends(session)):
"""Update user password after verifying current password."""
user_id = auth.user.id