Code Style Guide
This document outlines the code style conventions used in the Fabricatio project.
Rust Conventions
Edition and Build Configuration
Edition 2024: All Rust crates use
edition = "2024"[package] name = "thryd" version = "0.2.6" edition = "2024"
CDYLIB for PyO3: Python-bound crates use
crate-type = ["cdylib"][lib] crate-type = ["cdylib"]
Naming Conventions
Functions and variables:
snake_casepub fn new(model: Box<M>) -> Deployment<M> { ... } let identifier = self.model.identifier();
Types and structs:
PascalCasepub struct Deployment<M: ?Sized + Model> { ... } pub enum ThrydError { ... }
Constants:
SCREAMING_SNAKE_CASEpub const MINUTE_MS: u64 = 60_000; pub const SEPARATE: char = '/';
Module Organization
Use
pub modfor public modules,modfor privatepub mod cache; pub mod connections; mod deployment; // private
Module files: Use
mod.rsfor submodules or individual.rsfiles// In lib.rs pub mod models; // In models/mod.rs or models.rs
Error Handling
Use ``thiserror`` for creating error enums with
#[derive(Error)]use thiserror::Error; #[derive(Error, Debug)] pub enum ThrydError { #[error("Provider '{provider}' is not available: {reason}")] ProviderUnavailable { provider: String, reason: String }, #[error("HTTP request failed: {0}")] Reqwest(#[from] ReqwestError), } pub type Result<T> = std::result::Result<T, ThrydError>;
Error variants should have human-readable messages using
#[error(...)]
Doc Comments
Crate-level: Use
//!at the top of lib.rs//! Thryd - A lightweight, embedded LLM request router with caching. //! //! This library provides: //! - Multi-provider LLM request routing //! - Token usage tracking and rate limiting
Function-level: Use
///with markdown formatting/// Represents the unified error types for the Thryd system. /// /// This enum consolidates various failure scenarios including network issues, /// provider unavailability, configuration faults, and data validation errors.
PyO3 Patterns
PyModule:
#[pymodule]with `` Bound<’_, PyModule>`` parameter#[pymodule] fn rust(_python: Python, _m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) }
PyClass:
#[pyclass]with optional#[cfg_attr(feature = "stubgen", ...)]#[derive(Default)] #[cfg_attr(feature = "stubgen", pyo3_stub_gen::derive::gen_stub_pyclass)] #[pyclass] pub struct Logger;
PyMethods:
#[pymethods]with#[cfg_attr(...)]for conditional stub generation#[cfg_attr(feature = "stubgen", pyo3_stub_gen::derive::gen_stub_pymethods)] #[pymethods] impl Logger { fn info(&self, msg: &str) -> PyResult<()> { ... } }
Async Patterns
Use ``async-trait`` for async methods in traits
use async_trait::async_trait; #[async_trait] pub trait CompletionModel: Model { async fn completion(&self, request: CompletionRequest) -> crate::Result<String>; }
Let chains for conditional await
if let Some(tracker) = self.usage_tracker.as_ref() && res.is_ok() { tracker.lock().await.add_request_raw(input, "".to_string()); }
Tokio for async runtime with multi-thread features
tokio = { version = "1.51.0", features = ["rt-multi-thread", "macros"] }
Traits and Generics
Generic bounds use
?Sizedwhen appropriatepub struct Deployment<M: ?Sized + Model> { model: Box<M>, }
Builder pattern with
mut selfand-> Selfpub fn with_usage_constrain(mut self, rpm: Option<u64>, tpm: Option<u64>) -> Self { self.usage_tracker = Some(Mutex::new(UsageTracker::with_quota(tpm, rpm))); self }
Feature Flags
Use
#[cfg(feature = "...")]for optional functionality#[cfg(feature = "pyo3")] impl_as_pyerr!(thryd::ThrydError, PyRuntimeError);
Define features in
[features]section[features] pyo3 = ["dep:pyo3"] stubgen = ["dep:pyo3-stub-gen"]
Python Conventions
Type Hints
Full annotations required (project uses
Typing :: Typedclassifier)from typing import Any, ClassVar, Dict, Generator, Self, Sequence, Tuple, Type, Union, final async def _execute(self, *_: Any, **cxt) -> Any: ...
Generic type aliases using
typestatementtype NameSpace = Union[str, List[str]] type Callback[T] = Callable[[T], Coroutine[None, None, None]]
Self returns for method chaining
def update_init_context(self, /, **kwargs) -> Self: ...
Async Patterns
``async def`` for all async functions
@pytest.mark.asyncio async def test_router_completion(mock_router: Router, ret_value: str) -> None: response = await mock_router.completion(...)
Use asyncio primitives:
create_task,Queue,sleepfrom asyncio import Queue, create_task act_task = create_task(step.act(context))
Naming Conventions
Classes:
PascalCaseclass Action(WithBriefing, ABC): ... class WorkFlow(WithBriefing): ...
Functions and variables:
snake_casedef update_init_context(self, /, **kwargs) -> Self: ... extra_init_context: Dict[str, Any] = Field(default_factory=dict)
Private attributes:
_prefix_output: Queue[T | None] = PrivateAttr(default_factory=Queue) _status: TaskStatus = PrivateAttr(default=TaskStatus.Pending)
Docstring Style
Google-style docstrings (configured in ruff:
convention = "google")def update_init_context(self, /, **kwargs) -> Self: """Update the initial context with additional key-value pairs. Args: **kwargs: Key-value pairs to add to the initial context. Returns: Self: The workflow instance for method chaining. """ self.extra_init_context.update(kwargs) return self
Module-level docstrings at top of file
"""Module that contains the classes for defining and executing task workflows. This module provides the Action and WorkFlow classes for creating structured task execution pipelines. Classes: Action: Base class for defining executable actions with context management. WorkFlow: Manages action sequences, context propagation, and task lifecycle. """
Pydantic v2 Patterns
``ConfigDict`` for model configuration
model_config = ConfigDict(use_attribute_docstrings=True)
``Field`` for field definitions with docstrings as descriptions
name: str = Field(default="") """The name of the action.""" goals: List[str] = Field(default_factory=list) """Objectives the task aims to achieve."""
``PrivateAttr`` for private fields
from pydantic import Field, PrivateAttr _context: Queue[Dict[str, Any]] = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
Generic models with
[T]syntaxclass Task[T](WithBriefing, ProposedAble, WithDependency): """A class representing a task with status management and output handling."""
Imports Organization
Type checking blocks for circular imports
from typing import TYPE_CHECKING if TYPE_CHECKING: from fabricatio_core.models.task import Task as _Task
typing extensions for modern Python versions
from typing import Self, Never, Literal
Decorators
``@final`` from typing for preventing subclassing
@final def model_post_init(self, __context: Any) -> None: ...
Custom decorators in
decorators.pydef cfg_on[**P, R](feats: Sequence[str]) -> Callable[[Callable[P, R]], Callable[P, R]]: """Synchronous version of the cfg_on decorator.""" def _decorator(func: Callable[P, R]) -> Callable[P, R]: ...
Git Conventions
Branch Naming
Feature branches:
feat/<feature-name>git checkout -b feat/new-feature
Bugfix branches:
fix/<bug-name>Chore branches:
chore/<task-name>
Commit Messages
Subject line: Short, descriptive summary (50 chars or less recommended)
Body: Explain “why” not “what”
git commit -am 'Add new feature'
Types: Use conventional commit prefixes
feat:- New featurefix:- Bug fixdocs:- Documentationrefactor:- Code refactoringtest:- Adding testschore:- Maintenance tasks
Documentation Conventions
Rust Documentation
Crate documentation in
lib.rswith module-level doc commentsREADME files for each crate with:
Badges (crates.io, docs.rs, license)
Overview section
Key features list
Usage examples
Installation instructions
Configuration details
Python Documentation
Package README in
README.mdwith similar structureDocstrings on all public classes and functions
Testing Conventions
Python Testing (pytest)
Test file location:
python/tests/orpython/<package>/tests/packages/fabricatio-core/python/tests/test_usages.py
Test naming:
test_<functionality>.py@pytest.mark.asyncio async def test_router_completion(mock_router: Router, ret_value: str) -> None: """Test basic router completion functionality.""" response = await mock_router.completion(...) assert response == ret_value
Fixtures with descriptive docstrings
@pytest.fixture def mock_router(ret_value: str) -> Router: """Fixture to create a mocked router with predefined response. Args: ret_value: The value to be returned by the mocked completion Returns: Configured AsyncMock router object """ return return_string(ret_value)
Parametrized tests with
@pytest.mark.parametrize@pytest.mark.parametrize("ret_value", ["Hi", "Hello"]) @pytest.mark.asyncio async def test_router_completion(mock_router: Router, ret_value: str) -> None: ...
Async mode:
asyncio_mode = "auto"in pytest config[tool.pytest.ini_options] asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function"
Rust Testing
Inline tests with
#[test]or#[cfg(test)]module#[test] fn test_something() { ... }
Doc tests in documentation comments
/// # Examples /// /// ```rust /// use thryd::*; /// ```
Linting and Formatting
Rust
Formatting:
cargo fmtjust fix # Runs cargo fmt
Linting:
cargo clippyApplied automatically via
just fix
Python
Ruff for linting and formatting
[tool.ruff] line-length = 120 target-version = "py312" [tool.ruff.format] quote-style = "double" [tool.ruff.lint] select = ["F", "I", "N", "D", "W", "ANN", "ASYNC", ...] ignore = ["ANN401", "ANN003", ...] [tool.ruff.lint.pydocstyle] convention = "google"
Pyright for type checking
[tool.pyright] include = ["python/fabricatio/**/*.py", "packages/**/*.py"]
Format command:
just fix # Runs: cargo fmt && ruff format && ruff check --fix
Project Structure
Python Package Structure
packages/<package-name>/
├── python/
│ └── fabricatio_<name>/
│ ├── __init__.py # Package entry with exports
│ ├── actions/
│ │ └── __init__.py
│ ├── capabilities/
│ │ └── __init__.py
│ ├── models/
│ │ └── __init__.py
│ └── workflows/
│ └── __init__.py
├── pyproject.toml
├── README.md
└── LICENSE
Rust Crate Structure
crates/<crate-name>/
├── src/
│ ├── lib.rs # Main entry with pub modules
│ ├── error.rs # Error types (optional)
│ └── <feature>.rs # Feature modules (optional)
├── Cargo.toml
├── README.md
└── LICENSE
Module Exports
Python: Export in
__init__.py"""Fabricatio is a Python library for building llm app using event-based agent structure.""" from fabricatio_core import CONFIG, ROUTER, Action, Event, Role, Task, WorkFlow __all__ = ["CONFIG", "ROUTER", "Action", "Event", "Role", "Task", "WorkFlow"]
Rust: Re-export in
lib.rspub use cache::*; pub use constants::*; pub use error::{Result, ThrydError};
Development Commands
# Initialize development environment
make init
# Build in development mode
make dev
# Run tests
make tests
# Fix linting issues
make fix
# Using just (preferred)
just init
just dev
just test
just fix
See Also
Contributing - Contributing guidelines
Development Setup - Development setup
Architecture Overview - Project architecture