Files
ai_agent/.claude/rules/api-conventions.md
T
furyhawk 8351e73d39 feat: add Zustand stores for conversation, file preview, sidebar, theme, and knowledge base selection
- Implemented `conversation-store` for managing conversations and messages.
- Created `file-preview-store` to handle file preview state.
- Added `sidebar-store` for sidebar visibility management.
- Developed `theme-store` for theme persistence and management.
- Introduced `kb-selection-store` for managing active knowledge base selections with persistence.

chore: define API and chat types

- Added types for API responses, authentication, chat messages, conversations, and projects.
- Defined interfaces for various entities including users, sessions, and message ratings.

build: configure TypeScript and testing setup

- Set up `tsconfig.json` for TypeScript configuration.
- Created `vitest.config.ts` for testing configuration with Vitest.
- Added `vitest.setup.ts` for global test setup including mocks for Next.js router and media queries.
- Configured Vercel deployment settings in `vercel.json`.
2026-06-11 16:54:43 +08:00

2.2 KiB

description, globs
description globs
API design, REST conventions, auth, pagination, response format
backend/app/api/**/*.py

API Conventions

Route Structure

  • All routes under /api/v1/ prefix
  • One file per domain entity in api/routes/v1/
  • Use APIRouter() with tags

HTTP Methods & Status Codes

# GET — read
@router.get("/{id}", response_model=EntityRead)

# GET list — paginated
@router.get("", response_model=EntityList)

# POST — create (201)
@router.post("", response_model=EntityRead, status_code=status.HTTP_201_CREATED)

# PATCH — partial update
@router.patch("/{id}", response_model=EntityRead)

# DELETE — no content (204)
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)

Pagination

@router.get("", response_model=ConversationList)
async def list_items(
    service: ConversationSvc,
    user: CurrentUser,
    skip: int = Query(0, ge=0, description="Items to skip"),
    limit: int = Query(50, ge=1, le=100, description="Max items to return"),
) -> Any:
    items, total = await service.list(user_id=user.id, skip=skip, limit=limit)
    return ConversationList(items=items, total=total)

Authentication

  • CurrentUser — JWT Bearer token (any authenticated user)
  • CurrentAdmin — JWT + admin role check via RoleChecker
  • ValidAPIKey — API key from header (service-to-service)
# Protected endpoint
async def get_profile(user: CurrentUser) -> Any: ...

# Admin-only endpoint
async def delete_user(user: CurrentAdmin) -> Any: ...

# API key endpoint
async def webhook_callback(api_key: ValidAPIKey) -> Any: ...

Response Format

All route handlers return -> Any. The response_model parameter handles serialization.

Error responses follow this JSON structure:

{
  "error": {
    "code": "NOT_FOUND",
    "message": "User not found",
    "details": {"user_id": "..."}
  }
}

File Upload

@router.post("/me/avatar", response_model=UserRead)
async def upload_avatar(
    file: UploadFile = File(...),
    user: CurrentUser,
    service: UserSvc,
) -> Any:
    data = await file.read()
    return await service.update_avatar(user.id, data, file.filename or "avatar.jpg")