## Linter - Always follow `.ruff.toml`. - Run `uv run ruff check --fix --unsafe-fixes`. - Keep each line under 100 characters (including spaces). ## Code Style - `snake_case` for variables and functions. - `PascalCase` for classes. - `UPPER_CASE` for constants. ## Rules - Use Pydantic v2 standard. - Use `uv` for package management. - Do not override dunder methods like `__init__`, `__iadd__`, etc. - Never launch services (`uv run app.py`, `flask run`, etc.); running tests under `tests/` is allowed. - Prefer simple functions over classes for lightweight helpers. - Keep files below 800 lines; split when necessary. - Keep code readable—no clever hacks. - Never use `print`; log with `logger = logging.getLogger(__name__)`. ## Guiding Principles - Mirror the project’s layered architecture: controller → service → core/domain. - Reuse existing helpers in `core/`, `services/`, and `libs/` before creating new abstractions. - Optimise for observability: deterministic control flow, clear logging, actionable errors. ## SQLAlchemy Patterns - Models inherit from `models.base.Base`; never create ad-hoc metadata or engines. - Open sessions with context managers: ```python from sqlalchemy.orm import Session with Session(db.engine, expire_on_commit=False) as session: stmt = select(Workflow).where( Workflow.id == workflow_id, Workflow.tenant_id == tenant_id, ) workflow = session.execute(stmt).scalar_one_or_none() ``` - Use SQLAlchemy expressions; avoid raw SQL unless necessary. - Introduce repository abstractions only for very large tables (e.g., workflow executions) to support alternative storage strategies. - Always scope queries by `tenant_id` and protect write paths with safeguards (`FOR UPDATE`, row counts, etc.). ## Storage & External IO - Access storage via `extensions.ext_storage.storage`. - Use `core.helper.ssrf_proxy` for outbound HTTP fetches. - Background tasks that touch storage must be idempotent and log the relevant object identifiers. ## Pydantic Usage - Define DTOs with Pydantic v2 models and forbid extras by default. - Use `@field_validator` / `@model_validator` for domain rules. - Example: ```python from pydantic import BaseModel, ConfigDict, HttpUrl, field_validator class TriggerConfig(BaseModel): endpoint: HttpUrl secret: str model_config = ConfigDict(extra="forbid") @field_validator("secret") def ensure_secret_prefix(cls, value: str) -> str: if not value.startswith("dify_"): raise ValueError("secret must start with dify_") return value ``` ## Generics & Protocols - Use `typing.Protocol` to define behavioural contracts (e.g., cache interfaces). - Apply generics (`TypeVar`, `Generic`) for reusable utilities like caches or providers. - Validate dynamic inputs at runtime when generics cannot enforce safety alone. ## Error Handling & Logging - Raise domain-specific exceptions (`services/errors`, `core/errors`) and translate to HTTP responses in controllers. - Declare `logger = logging.getLogger(__name__)` at module top. - Include tenant/app/workflow identifiers in log context. - Log retryable events at `warning`, terminal failures at `error`. ## Tooling & Checks - Format/lint: `uv run --project api --dev ruff format ./api` and `uv run --project api --dev ruff check --fix --unsafe-fixes ./api`. - Type checks: `uv run --directory api --dev basedpyright`. - Tests: `uv run --project api --dev dev/pytest/pytest_unit_tests.sh`. - Run all of the above before submitting your work. ## Controllers & Services - Controllers: parse input via Pydantic, invoke services, return serialised responses; no business logic. - Services: coordinate repositories, providers, background tasks; keep side effects explicit. - Avoid repositories unless necessary; direct SQLAlchemy usage is preferred for typical tables. - Document non-obvious behaviour with concise comments. ## Miscellaneous - Use `configs.dify_config` for configuration—never read environment variables directly. - Maintain tenant awareness end-to-end; `tenant_id` must flow through every layer touching shared resources. - Queue async work through `services/async_workflow_service`; implement tasks under `tasks/` with explicit queue selection. - Keep experimental scripts under `dev/`; do not ship them in production builds.