Code Generation Guide¶
OrmAI includes code generation utilities to create type-safe views, domain tools, and other artifacts from your schema and policy.
Overview¶
Code generation helps you:
- Create Pydantic view models aligned with policies
- Generate domain tool stubs
- Maintain type safety across your codebase
- Reduce boilerplate
View Generation¶
Basic Usage¶
from ormai.codegen import ViewCodeGenerator
generator = ViewCodeGenerator(
schema=adapter.introspect(),
policy=policy,
output_dir="./generated/views",
)
# Generate all views
generator.generate_all()
Output Structure¶
Generated Code¶
# ./generated/views/user_views.py
"""Auto-generated views for User model."""
from datetime import datetime
from pydantic import BaseModel, Field
from typing import Optional, List
class UserView(BaseModel):
"""View for User model aligned with policy."""
id: str
name: str
email: str = Field(..., description="Masked field")
created_at: datetime
class Config:
orm_mode = True
class UserListView(BaseModel):
"""Minimal view for User listings."""
id: str
name: str
class Config:
orm_mode = True
Customization Options¶
generator = ViewCodeGenerator(
schema=schema,
policy=policy,
output_dir="./generated/views",
# Options
generate_list_views=True, # Create minimal list views
generate_detail_views=True, # Create full detail views
include_relations=True, # Include relation views
max_relation_depth=2, # Limit nesting
base_class="BaseModel", # Pydantic base class
add_docstrings=True, # Include docstrings
)
Selective Generation¶
# Generate for specific models
generator.generate(["User", "Order"])
# Skip certain models
generator.generate_all(exclude=["AuditLog", "InternalConfig"])
Domain Tool Generation¶
Basic Usage¶
from ormai.codegen import DomainToolGenerator
generator = DomainToolGenerator(
schema=adapter.introspect(),
policy=policy,
output_dir="./generated/tools",
)
generator.generate_all()
Output Structure¶
Generated Stubs¶
# ./generated/tools/order_tools.py
"""Auto-generated domain tools for Order model."""
from ormai.tools import Tool, ToolResult
from ormai.core import RunContext
class CreateOrderTool(Tool):
"""Create a new Order."""
name = "create_order"
description = "Create a new Order record"
async def execute(
self,
ctx: RunContext,
status: str,
total: int,
user_id: str,
) -> ToolResult:
# TODO: Implement business logic
# Example implementation:
# return await self.toolset.create(
# ctx,
# model="Order",
# data={
# "status": status,
# "total": total,
# "user_id": user_id,
# },
# )
raise NotImplementedError("Implement create_order logic")
class UpdateOrderStatusTool(Tool):
"""Update the status of an Order."""
name = "update_order_status"
description = "Update the status of an existing Order"
async def execute(
self,
ctx: RunContext,
order_id: int,
new_status: str,
) -> ToolResult:
# TODO: Implement business logic
raise NotImplementedError("Implement update_order_status logic")
Tool Templates¶
Define custom templates:
generator = DomainToolGenerator(
schema=schema,
policy=policy,
output_dir="./generated/tools",
templates={
"create": "custom_create_template.py.jinja2",
"update": "custom_update_template.py.jinja2",
},
)
Full Code Generator¶
Generate everything at once:
from ormai.codegen import CodeGenerator
generator = CodeGenerator(
schema=adapter.introspect(),
policy=policy,
output_dir="./generated",
)
generator.generate_all()
Output Structure¶
./generated/
├── __init__.py
├── views/
│ ├── __init__.py
│ ├── user_views.py
│ └── order_views.py
├── tools/
│ ├── __init__.py
│ ├── user_tools.py
│ └── order_tools.py
├── schemas/
│ ├── __init__.py
│ └── openapi.json
└── types/
├── __init__.py
└── enums.py
CLI Usage¶
Generate from command line:
# Generate all
python -m ormai.codegen generate \
--config ./ormai.yaml \
--output ./generated
# Generate views only
python -m ormai.codegen generate-views \
--config ./ormai.yaml \
--output ./generated/views
# Generate tools only
python -m ormai.codegen generate-tools \
--config ./ormai.yaml \
--output ./generated/tools
Configuration File¶
# ormai.yaml
adapter:
type: sqlalchemy
connection_string: ${DATABASE_URL}
base_module: myapp.models
policy:
path: ./policy.yaml
codegen:
output_dir: ./generated
views:
enabled: true
list_views: true
detail_views: true
max_relation_depth: 2
tools:
enabled: true
include_create: true
include_update: true
include_domain: true
schemas:
enabled: true
format: openapi
Regeneration¶
Preserving Custom Code¶
Generated files include markers:
# ./generated/tools/order_tools.py
"""Auto-generated domain tools for Order model.
WARNING: This file is auto-generated. Do not edit directly.
Custom logic should be added in the designated sections.
"""
class CreateOrderTool(Tool):
# ... generated code ...
async def execute(self, ctx, **kwargs):
# === BEGIN CUSTOM CODE ===
# Your custom logic here (preserved on regeneration)
# === END CUSTOM CODE ===
pass
Force Regeneration¶
Incremental Updates¶
generator.generate_all(
incremental=True, # Only update changed models
backup=True, # Create backups before updating
)
Type Generation¶
Enums¶
# ./generated/types/enums.py
from enum import Enum
class OrderStatus(str, Enum):
PENDING = "pending"
CONFIRMED = "confirmed"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
class PaymentStatus(str, Enum):
UNPAID = "unpaid"
PAID = "paid"
REFUNDED = "refunded"
TypedDicts¶
# ./generated/types/dicts.py
from typing import TypedDict, Optional
class OrderDict(TypedDict):
id: int
status: str
total: int
user_id: str
created_at: str
updated_at: Optional[str]
OpenAPI Schema Generation¶
from ormai.codegen import OpenAPIGenerator
generator = OpenAPIGenerator(
schema=schema,
policy=policy,
toolset=toolset,
)
openapi_spec = generator.generate()
# Save to file
with open("./openapi.json", "w") as f:
json.dump(openapi_spec, f, indent=2)
Generated Spec¶
{
"openapi": "3.0.3",
"info": {
"title": "OrmAI API",
"version": "1.0.0"
},
"paths": {
"/query": {
"post": {
"operationId": "query",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/QueryRequest"}
}
}
}
}
}
},
"components": {
"schemas": {
"OrderView": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"status": {"type": "string", "enum": ["pending", "confirmed", "shipped"]}
}
}
}
}
}
Integration with Build¶
Pre-commit Hook¶
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: ormai-codegen
name: OrmAI Code Generation
entry: python -m ormai.codegen generate --config ./ormai.yaml
language: python
pass_filenames: false
CI/CD¶
# .github/workflows/codegen.yml
name: Code Generation
on: [push]
jobs:
codegen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install ormai
- name: Generate code
run: python -m ormai.codegen generate --config ./ormai.yaml
- name: Check for changes
run: |
if [[ -n $(git status --porcelain generated/) ]]; then
echo "Generated code is out of date!"
exit 1
fi
Best Practices¶
-
Version control generated code - Include in git for transparency
-
Don't edit generated files directly - Use custom code sections
-
Regenerate on schema changes - Keep views aligned with models
-
Use incremental mode - Faster regeneration
-
Review generated code - Ensure it meets your standards
-
Customize templates - Match your coding style
Next Steps¶
- Views - Using generated views
- Custom Tools - Implementing tool stubs
- Evaluation - Testing generated code