Skip to content

Commit 8b1de2e

Browse files
committed
🔨 Update mdc cursor rules and claude.md for backend services, apps, english comments and global environments, which would be applied automatically during the chat.
🐛 Bugfix: agent thread improper declaration
1 parent ae952d6 commit 8b1de2e

File tree

6 files changed

+464
-158
lines changed

6 files changed

+464
-158
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
globs: backend/apps/**/*.py
3+
description: App layer (API) contract for FastAPI endpoints in backend/apps. Parse/validate input, call services, map domain errors to HTTP, return JSONResponse on success.
4+
---
5+
6+
### Purpose and Scope
7+
8+
- The App layer is the HTTP boundary for the backend. It applies to files under `backend/apps/*.py`.
9+
- Responsibilities:
10+
- Parse and validate HTTP inputs.
11+
- Call underlying services; do not implement core business logic here.
12+
- Translate domain/service exceptions into `HTTPException` with proper status codes.
13+
- Return `JSONResponse(status_code=HTTPStatus.OK, content=payload)` on success.
14+
- Configuration: Do not access environment variables directly. Read configuration via `consts.const` or pass values through from the request to services.
15+
16+
References: [backend/consts/exceptions.py](mdc:backend/consts/exceptions.py)
17+
18+
### Routing and URL Design
19+
20+
- Keep existing top-level prefixes for compatibility (e.g., `"/agent"`, `"/memory"`). When adding new modules or endpoints, follow these rules:
21+
- Use plural nouns for collection-style resources (e.g., `"/agents"`, `"/memories"`).
22+
- Use snake_case for all path segments. Avoid hyphens and camelCase.
23+
- Prefer resource-oriented paths for CRUD-style operations. Example: `"/agents"` (collection), `"/agents/{agent_id}"` (single resource).
24+
- Use action-style paths only when necessary to match current patterns or when the operation is not naturally CRUD (e.g., `"/agent/run"`, `"/agent/stop/{conversation_id}"`).
25+
- Path parameters must be singular, semantic nouns: `"/agents/{agent_id}"`, `"/memories/{memory_id}"`.
26+
- Keep backwards compatibility: do not rename existing routes; new routes should follow these conventions.
27+
28+
### HTTP Methods
29+
30+
- GET: Read and list operations only. Maintain existing special cases where GET performs safe actions (e.g., `GET /agent/stop/{conversation_id}`), but do not introduce new side-effecting GETs.
31+
- POST: Create resources, perform searches, or trigger actions with side effects (e.g., `POST /memory/add`, `POST /memory/search`, `POST /agent/run`).
32+
- DELETE: Delete resources or clear collections (e.g., `DELETE /memory/clear`). Ensure idempotency.
33+
- PUT/PATCH: Update resources. Prefer `PUT` for full updates and `PATCH` for partial updates. Preserve legacy `POST /update` endpoints for compatibility but favor PUT/PATCH for new code.
34+
35+
### Authorization and Identity
36+
37+
- Retrieve the bearer token via header injection: `authorization: Optional[str] = Header(None)`.
38+
- Use utility helpers to parse identity (prefer functions in `utils.auth_utils`, such as `get_current_user_id` or `get_current_user_info`) and pass `user_id` and/or `tenant_id` down to services. The App layer should not implement token parsing logic itself.
39+
40+
### Request Validation
41+
42+
- Prefer Pydantic models in `consts.model` as request bodies for complex payloads (e.g., `AgentRequest`).
43+
- For simple atomic fields, use `Body(..., embed=True)` to pin the JSON key name.
44+
- Use `Query(...)` for filters and pagination, `Path(...)` for path parameters, and `Header(...)` for headers.
45+
- Pagination recommendations for listing endpoints: `page: int = Query(1, ge=1)`, `page_size: int = Query(20, ge=1, le=100)`, plus optional `order_by`, `filters` as appropriate. Return pagination metadata (`items`, `total`) or match existing return shapes in the codebase.
46+
47+
### Responses
48+
49+
- On success, return `JSONResponse(status_code=HTTPStatus.OK, content=payload)`.
50+
- If a standard response model exists in the project (e.g., conversation responses), continue to use it for consistency.
51+
- For new endpoints, return a structured content dictionary with necessary fields (e.g., `{"data": ..., "message": "OK"}`) while staying consistent with existing patterns.
52+
53+
### Exception Mapping
54+
55+
- Catch domain/service exceptions from `backend/consts/exceptions.py` and map to `HTTPException` with appropriate status codes. Examples:
56+
- `UnauthorizedError` → 401 UNAUTHORIZED
57+
- `LimitExceededError` → 429 TOO_MANY_REQUESTS
58+
- Parameter/validation errors (e.g., invalid enum, unknown config key) → 400 BAD_REQUEST or 406 NOT_ACCEPTABLE (follow existing precedent such as `set_single_config` using 406)
59+
- Unexpected errors → 500 INTERNAL_SERVER_ERROR (log the error; do not leak internal details)
60+
61+
### Logging and Observability
62+
63+
- Use a module-level logger: `logger = logging.getLogger("<module_name>")`.
64+
- Log key events and errors. For listing/search endpoints, optionally log query scope and timing while avoiding sensitive data.
65+
66+
### Async/Sync Conventions
67+
68+
- Match the existing style in each module. Keep `async def` where already used.
69+
- When calling async services, prefer direct `await`. When calling sync services, invoke them directly without creating new event loops.
70+
71+
### Backward Compatibility
72+
73+
- Do not break existing routes, payload shapes, or response structures.
74+
- New endpoints should follow these conventions strictly to converge the API style across modules.
75+
76+
### Correct Example (parse input, call service, map exceptions, return JSONResponse)
77+
```python
78+
from http import HTTPStatus
79+
import logging
80+
from fastapi import APIRouter, HTTPException
81+
from starlette.responses import JSONResponse
82+
83+
from consts.exceptions import LimitExceededError, AgentRunException, MemoryPreparationException
84+
from services.agent_service import run_agent
85+
86+
logger = logging.getLogger(__name__)
87+
router = APIRouter()
88+
89+
@router.post("/agent/run")
90+
def run_agent_endpoint(payload: dict):
91+
try:
92+
result = run_agent(payload)
93+
return JSONResponse(status_code=HTTPStatus.OK, content=result)
94+
except LimitExceededError as exc:
95+
raise HTTPException(status_code=HTTPStatus.TOO_MANY_REQUESTS, detail=str(exc))
96+
except MemoryPreparationException as exc:
97+
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(exc))
98+
except AgentRunException as exc:
99+
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc))
100+
```
101+
102+
### Incorrect Example (business logic in App layer or non-HTTP error handling)
103+
```python
104+
from starlette.responses import JSONResponse
105+
106+
def run_agent_endpoint(payload: dict):
107+
# WRONG: performing core business logic inside the app layer
108+
if payload.get("force"):
109+
return {"status": "forced"} # WRONG: returns plain dict without HTTP status context
110+
111+
# WRONG: not translating domain errors to HTTP
112+
result = risky_logic(payload)
113+
return JSONResponse(result)
114+
```
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
globs: backend/services/**/*.py
3+
description: Service layer implements core business logic orchestration; raise custom exceptions; no HTTP handling
4+
---
5+
6+
### Service Layer Rules
7+
8+
- **Scope**: Applies to `backend/services/*.py`.
9+
- **Goal**: Implement core business logic and orchestrate complex workflows. Coordinate repositories/SDKs. Keep HTTP concerns out of this layer.
10+
- **Exceptions**: Raise domain/service exceptions declared in `backend/consts/exceptions.py`. If a new case is needed, add a new class there, then raise it here. Do not translate to HTTP here.
11+
- **Environment variables**: Do not access `os.getenv()` directly. Read configuration from `consts.const` (see `environment_variable` rule) or accept parameters.
12+
13+
Reference: [backend/consts/exceptions.py](mdc:backend/consts/exceptions.py)
14+
15+
### Correct example (service orchestrates business logic and raises domain exceptions)
16+
```python
17+
# backend/services/agent_service.py
18+
from typing import Any, Dict
19+
20+
from consts.exceptions import LimitExceededError, AgentRunException, MemoryPreparationException
21+
# from consts.const import APPID, TOKEN # Example: read config via consts, not os.getenv
22+
23+
24+
def run_agent(task_payload: Dict[str, Any]) -> Dict[str, Any]:
25+
"""Run agent core workflow and return domain result dict.
26+
Raises domain exceptions on failure; no HTTP concerns here.
27+
"""
28+
if _is_rate_limited(task_payload):
29+
raise LimitExceededError("Too many requests for this tenant.")
30+
31+
try:
32+
memory = _prepare_memory(task_payload)
33+
except Exception as exc:
34+
# Wrap low-level error in a domain exception for the app layer to translate
35+
raise MemoryPreparationException("Failed to prepare memory.") from exc
36+
37+
try:
38+
result = _execute_core_logic(task_payload, memory)
39+
except Exception as exc:
40+
raise AgentRunException("Agent execution failed.") from exc
41+
42+
# Return a plain Python object, not a Response
43+
return {"status": "ok", "data": result}
44+
45+
46+
def _is_rate_limited(_: Dict[str, Any]) -> bool:
47+
return False
48+
49+
50+
def _prepare_memory(_: Dict[str, Any]) -> Dict[str, Any]:
51+
return {"memo": "prepared"}
52+
53+
54+
def _execute_core_logic(_: Dict[str, Any], __: Dict[str, Any]) -> Dict[str, Any]:
55+
return {"answer": "42"}
56+
```
57+
58+
### Incorrect example (service leaks HTTP/web concerns or reads env directly)
59+
```python
60+
# backend/services/agent_service.py
61+
from fastapi import HTTPException # WRONG: HTTP in service
62+
from starlette.responses import JSONResponse # WRONG: Response in service
63+
import os # WRONG: direct env access in service
64+
65+
66+
def run_agent(_: dict):
67+
# WRONG: translating to HTTP inside service
68+
if os.getenv("RATE_LIMIT", "0") == "1": # WRONG: direct getenv here
69+
raise HTTPException(status_code=429, detail="Too many requests")
70+
71+
# WRONG: returning framework response from service
72+
return JSONResponse({"status": "ok"})
73+
```
74+
75+
### Declaring a new custom exception (do this in exceptions module)
76+
```python
77+
# backend/consts/exceptions.py
78+
class OrderProcessingError(Exception):
79+
"""Raised when order processing fails in service layer."""
80+
pass
81+
```
82+
83+
### Existing exceptions (excerpt from current code)
84+
```python
85+
"""
86+
Custom exception classes for the application.
87+
"""
88+
89+
90+
class AgentRunException(Exception):
91+
"""Exception raised when agent run fails."""
92+
pass
93+
94+
95+
class LimitExceededError(Exception):
96+
"""Raised when an outer platform calling too frequently"""
97+
pass
98+
99+
100+
class UnauthorizedError(Exception):
101+
"""Raised when a user from outer platform is unauthorized."""
102+
pass
103+
104+
105+
class SignatureValidationError(Exception):
106+
"""Raised when X-Signature header is missing or does not match the expected HMAC value."""
107+
pass
108+
109+
110+
class MemoryPreparationException(Exception):
111+
"""Raised when memory preprocessing or retrieval fails prior to agent run."""
112+
pass
113+
```

.cursor/rules/english_comments.mdc

Lines changed: 24 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,37 @@
11
---
22
alwaysApply: true
3+
description: Enforce English-only comments and docstrings across the codebase
34
---
4-
# English Comments Rule
5+
# English-only Comments
56

6-
## English-Only Comments Requirement
7+
- All comments and docstrings must be written in clear, concise English.
8+
- Do not use non-English characters in comments (string literals may contain any language).
9+
- Use proper grammar and spelling; avoid ambiguous abbreviations.
710

8-
All code comments MUST be written in English. No Chinese, Japanese, Korean, or other non-English languages are allowed in comments.
9-
10-
### ✅ Correct Pattern:
11+
## Do
1112
```python
12-
# Initialize the processor with default settings
13-
processor = DataProcessor()
14-
15-
# Cache inspector for 60 seconds
16-
self._inspector_ttl = 60
17-
18-
# Ensure current application configuration is correct
19-
if not self._validate_config():
20-
raise ConfigurationError("Invalid configuration")
21-
22-
# Concurrently fetch all task details asynchronously
23-
tasks = await self._fetch_all_tasks_async()
24-
25-
# Lazy load CLIP model
26-
self._clip_model = None
13+
# Initialize cache for 60 seconds
14+
self.cache_ttl = 60
2715
```
2816

29-
### ❌ Forbidden Pattern:
17+
## Don't
3018
```python
31-
# 初始化处理器
32-
processor = DataProcessor()
33-
34-
# inspector缓存时间,秒
35-
self._inspector_ttl = 60
36-
37-
# 确保当前应用配置正确
38-
if not self._validate_config():
39-
raise ConfigurationError("Invalid configuration")
40-
41-
# 并发异步获取所有任务详情
42-
tasks = await self._fetch_all_tasks_async()
43-
44-
# 延迟加载CLIP模型
45-
self._clip_model = None
19+
# 初始化缓存 60 秒 - FORBIDDEN
20+
# データキャッシュ60秒 - FORBIDDEN
21+
# 데이터 캐시 60초 - FORBIDDEN
4622
```
4723

48-
## Architecture Rules
49-
50-
1. **English-Only Comments**: All comments must be written in English, regardless of the target audience or deployment region.
51-
52-
2. **Code Documentation**:
53-
- Function docstrings must be in English
54-
- Inline comments must be in English
55-
- TODO comments must be in English
56-
- FIXME comments must be in English
57-
58-
3. **Variable and Function Names**:
59-
- Variable names can be in English or follow existing naming conventions
60-
- Function names should be descriptive in English
61-
- Class names should be in English
62-
63-
4. **String Literals**:
64-
- User-facing strings can be localized
65-
- Error messages can be localized
66-
- Comments and documentation must remain in English
24+
## Scope
25+
- Docstrings, inline comments, TODO/FIXME/NOTE, header comments
26+
- Configuration comments in YAML/JSON and other config files
6727

68-
## File Structure Requirements
69-
70-
- All `.py` files: English comments only
71-
- All `.js`/`.ts` files: English comments only
72-
- All `.md` files: English documentation only
73-
- All `.yaml`/`.yml` files: English comments only
74-
75-
## Migration Checklist
76-
77-
When refactoring existing code:
78-
1. ✅ Identify all non-English comments
79-
2. ✅ Translate comments to English while preserving meaning
80-
3. ✅ Update docstrings to English
81-
4. ✅ Ensure inline comments are in English
82-
5. ✅ Verify all TODO/FIXME comments are in English
83-
6. ✅ Update any related documentation
84-
85-
## Validation
86-
87-
Before committing changes:
88-
- No Chinese characters in comments
89-
- No Japanese characters in comments
90-
- No Korean characters in comments
28+
## Validation Checklist
9129
- All comments are in English
92-
- All docstrings are in English
93-
- All TODO/FIXME comments are in English
94-
95-
## Examples of Refactoring
96-
97-
### Before:
98-
```python
99-
# 解析输入URL
100-
def parse_url(url: str) -> str:
101-
"""解析并验证URL格式"""
102-
# 检查URL是否有效
103-
if not url.startswith('http'):
104-
raise ValueError("无效的URL格式")
105-
return url
106-
```
107-
108-
### After:
109-
```python
110-
# Parse input URL
111-
def parse_url(url: str) -> str:
112-
"""Parse and validate URL format"""
113-
# Check if URL is valid
114-
if not url.startswith('http'):
115-
raise ValueError("Invalid URL format")
116-
return url
117-
```
118-
description:
119-
globs:
120-
alwaysApply: false
121-
---
30+
- Docstrings use proper English grammar
31+
- No non-Latin characters in comments (except inside strings)
32+
- Comments are clear and provide value
33+
34+
## Optional Automation
35+
- Pre-commit hook to detect non-English comments
36+
- IDE extensions for real-time detection
37+
- CI checks for compliance

0 commit comments

Comments
 (0)