# API Agent Guide Read surrounding module, class, and function docstrings plus non-obvious comments before changing backend behavior. They are local contracts; update them only when their owned behavior changes, and keep them aligned with the current code. ## Commands Run backend checks from the repository root: - Format and lint: `make lint` - Type check: `make type-check` - Unit tests: `make test` - Targeted tests: `make test TARGET_TESTS=./api/tests/` Run direct Python commands through `uv run --project api`. Docker-backed integration suites are normally CI-owned. Do not start long-running services as part of routine agent work. Follow the backend developer guide's [test layout and naming requirements](README.md#testing) when adding or moving unit tests. ## Architecture And Boundaries Read all backend architecture documentation before modifying backend code: - [Backend architecture][architecture] - [Repositories][repositories] - [SQLAlchemy][sqlalchemy] - [Database schema][db-schema] - [API schema guide][schema-guide] ## Additional Backend Rules - Reconstruct trusted internal references from validated database state after payload or async boundaries. - Read configuration through `configs.dify_config`, access storage through `extensions.ext_storage.storage`, and route outbound HTTP through the existing SSRF-safe owner in `core.helper.ssrf_proxy`. - Use existing Celery task and queue owners for asynchronous work; do not route unrelated jobs through workflow-specific services. - Celery tasks that may be retried or redelivered must keep side effects idempotent and log affected resource identifiers. New SQLAlchemy models must inherit from `models.base.TypeBase`. The ast-grep guard rejects new `Base` subclasses. Existing `Base` subclasses may be edited or migrated without adding more legacy inheritance. `make lint` checks local changes, and CI checks the PR against its target revision. ## Billing And SaaS Calls - Call billing and saas only through the generated functions in `services/billing_client`; do not hand-write paths or response types. - Ruff's banned-api rule (TID251) enforces this: outside `services/billing_client`, reading the billing URL and secret settings or calling `transport.request` fails lint. - To call a new endpoint, add it to `ALLOWED_OPERATIONS` in `dev/generate_billing_client.py` and rerun that script against a dify-saas checkout. Never edit the generated modules or snapshots by hand. ## Typing Best Practises - Treat nullable types (`T | None`) strictly as union types; document the semantic meaning for each branch. - Treat default parameter values as introducing distinct function overloads; verify that every added default has demonstrable practical utility and avoids masking unsafe runtime states. ## Invariants And Construction - Start each design from explicit principles and domain invariants. An invariant is a condition that all valid states preserve. - Derive implementation rules and memory documents from these principles. Explain the invariant that each rule protects. - Document each invariant in the docstring of the module, class, or function that owns it. Use a comment for a local invariant. - Use abstract interpretation to reason about possible values and state transitions. Check that each operation preserves the relevant invariants. - Use dedicated types to distinguish domain values with different meanings. Use `NewType` for static distinctions between values with identical representations. - `NewType` does not validate values at runtime. Use validated value objects when an invariant requires runtime checks. - Model mutually exclusive alternatives (an OR relationship) with union types. Use a tagged union when variants need an explicit discriminator. - Give each tagged variant a distinct `Literal` tag. Include only the fields that are valid for that variant. - Prefer safe-by-construction functional idioms. Use immutable values, explicit result variants, and pure functions where practical. - Validate external values at boundaries with Pydantic v2 models or `TypeAdapter`. Construct trusted domain values only after validation succeeds. ## Control Flow And Contracts - Use structured control flow. Do not add `continue` or `break` statements. - Express iteration with explicit conditions, comprehensions, or small functions that return a result. - State preconditions and postconditions for each new or changed function in the code you produce. - Put these contracts in the function docstring. Include accepted inputs, required state, results, side effects, and failure behavior as applicable. - Specify which invariants remain true after success and failure. Use types and executable checks to enforce contracts where practical. - Include preconditions and postconditions in the change report. State which checks support the claimed postconditions. [architecture]: ../docs/BACKEND_ARCHITECTURE.md [repositories]: ../docs/BACKEND_REPOSITORIES.md [sqlalchemy]: ../docs/BACKEND_SQLALCHEMY.md [db-schema]: ../docs/BACKEND_DATABASE_SCHEMA.md [schema-guide]: controllers/API_SCHEMA_GUIDE.md