AI Parrot Code Style Guide¶
This guide defines conventions for Python code in the ai-parrot project. It complements PEP 8 and focuses on clarity, correctness, and maintainability across bots, tools, clients, stores, handlers, and models.
Python & Tooling¶
- Version: Target Python 3.10+ (project target
>=3.10.1; CI uses 3.11). - Formatting: Follow PEP 8. Prefer Black-compatible formatting (120 cols max if long data structures demand it). Use the repo
pyproject.tomlsettings. - Typing: Use type hints for public APIs, function signatures, dataclasses/models, and complex local variables. Avoid
Anyunless necessary. - Imports:
- Standard lib, third-party, local imports (in that order).
- Absolute imports within the package (e.g.,
from parrot.tools.manager import ToolManager). - Avoid circular imports; refactor shared code to dedicated modules.
Naming¶
- Files/Modules: snake_case.
- Classes: PascalCase.
- Functions/Methods: snake_case, must be verbs/verb phrases.
- Variables: descriptive nouns; avoid 1–2 letter names (except for trivial indices).
- Constants/Enums: UPPER_SNAKE_CASE.
Docstrings¶
- Use triple double quotes.
- For modules, classes, and all public functions/methods:
- 1-line summary
- Optional paragraphs with context
- Args/Returns/Raises sections where applicable
- Keep user-facing wording clear and concise (this project’s APIs are used by other developers and by UI surfaces).
Example:
def create_sql_agent(database_flavor: str, connection_string: str, **kwargs) -> SQLDbAgent:
"""Factory for SQL database agents.
Args:
database_flavor: Database type ('postgresql', 'mysql', 'sqlserver').
connection_string: SQLAlchemy-style connection string.
Returns:
Configured `SQLDbAgent` instance.
"""
Error Handling¶
- Fail fast with clear messages. Prefer
ValueError,TypeError,RuntimeError, etc. - Validate external inputs early (HTTP payloads, DB params, tool args).
- Use guard clauses and avoid deep nesting.
- When catching exceptions, narrow the scope and log with context. Prefer raising with context over silent failures.
Logging¶
- Use the component logger pattern (e.g.,
self.logger = logging.getLogger(f"{self.name}.Bot")). - Log at appropriate levels:
debug(dev details),info(major events),warning(recoverable anomalies),error(failures),exception(with traceback). - Avoid logging sensitive credentials or large payloads.
Concurrency & Async IO¶
- Handlers and tools that perform IO (HTTP, DB, filesystem) should be async where supported.
- Prefer
async with/awaitand use connection pools/clients designed for concurrency. - Avoid blocking CPU work in async paths; offload to workers if needed.
- When enqueueing background work, use the provided
register_background_taskon handlers and keep task functions pure and idempotent. Usedone_callbackto persist results; handle exceptions and log context. - Job tracking is Redis-backed; ensure
CACHE_URLis configured. Do not rely onKEYSin production debugging; useSCANand preferUNLINKoverDEL.
Configuration & Defaults¶
- Use explicit, documented defaults. For LLMs and models, prefer centralized presets (
LLM_PRESETS) or constructor kwargs. - Read config from kwargs/
model_config; keep runtime overrides explicit. - For time values, prefer epoch ms where interoperating with BigQuery/Scylla, or timezone-aware
datetimefor PG.
Models (datamodel.Field)¶
- Define constraints/choices and provide
ui_helpfor all user-facing fields. - Keep
to_bot_config()mapping consistent with bot constructors. - Validate enumerations at init (
operation_mode,memory_type, etc.).
Tools & ToolManager¶
- Tools must derive from
AbstractTooland define: name,description- Pydantic args schema (input validation)
- Deterministic outputs; prefer
ToolResult - Register tools via
ToolManagerand keep legacy pathways backwards compatible. - Never hardcode secrets; pass via config or environment.
Clients (LLMs)¶
- Clients must extend
AbstractClientand support: - Streaming and non-streaming
- Tool calling and (where supported) structured output
- Clear error surfaces for provider-specific limitations
- Keep provider quirks encapsulated (e.g., Groq tools + JSON mode limitations). Use
ToolSchemaAdapterto normalize tool schemas per provider (openai,anthropic,google,groq,vertex).
Stores & Loaders¶
- Stores implement
AbstractStorewith consistent CRUD/search semantics. - Vector operations must document metric types and dimensions.
- Loaders should be pure and idempotent; avoid global state.
Handlers & Interfaces¶
- Keep handlers thin; validate, dispatch to bots/tools, serialize results.
- Return structured responses (
AIMessage,AgentResponse). - Reuse decorators for auth/permissions (
auth_groups,auth_by_attribute). - Expose predictable HTTP surfaces: POST to enqueue (202 +
task_id), GET to poll job (results/{task_id}), and GET to list jobs for current user. Avoid leaking internal errors; provideerrorandstacktracefields when available. For chatbots, supportPOST /api/v1/chat/{chatbot_name}/{method_name}with dynamic param validation viainspect.signature.
Testing¶
- Unit-test tools, clients, and model mappings.
- Prefer small, deterministic fixtures; avoid network in unit tests.
- Add regression tests when fixing bugs (e.g., ToolManager initialization).
Performance¶
- Batch external calls where possible; use pagination/limits.
- Avoid unnecessary copies of large dataframes/blobs.
- Measure with timers in debug logs when optimizing.
Style Do’s & Don’ts¶
- Prefer early returns over deep nesting.
- Use meaningful variable names; avoid magic numbers/strings (introduce constants).
- Keep functions short and focused; extract helpers.
- Do not add inline commentary comments; document intent via names and docstrings.