Skip to content

Example 1: Schema Definition

Define your data model using the Agentic Schema Definition.

examples/bookstore/01_schema_definition.py
#!/usr/bin/env python3
"""Example 1: Schema Definition — Defining your data model with the ASD.

Demonstrates:
- Creating entities with typed fields, constraints, and storage engines
- Defining relationships between entities (hard FK, soft semantic, graph)
- Grouping entities into domains with agent configuration
- Assembling the full AgenticSchema (project-level container)
- Serializing to .ninjastack/schema.json
"""

from ninja_core.schema.agent import AgentConfig, ReasoningLevel
from ninja_core.schema.domain import DomainSchema
from ninja_core.schema.entity import (
    EmbeddingConfig,
    EntitySchema,
    FieldConstraint,
    FieldSchema,
    FieldType,
    StorageEngine,
)
from ninja_core.schema.project import AgenticSchema
from ninja_core.schema.relationship import Cardinality, RelationshipSchema, RelationshipType
from ninja_core.serialization.io import save_schema

# ---------------------------------------------------------------------------
# 1. Define Entities
# ---------------------------------------------------------------------------

book = EntitySchema(
    name="Book",
    storage_engine=StorageEngine.SQL,
    description="A book in the catalog.",
    fields=[
        FieldSchema(name="id", field_type=FieldType.UUID, primary_key=True),
        FieldSchema(name="title", field_type=FieldType.STRING, indexed=True,
                    constraints=FieldConstraint(min_length=1, max_length=500)),
        FieldSchema(name="author", field_type=FieldType.STRING, indexed=True),
        FieldSchema(name="isbn", field_type=FieldType.STRING, unique=True,
                    constraints=FieldConstraint(pattern=r"^\d{13}$")),
        FieldSchema(name="price", field_type=FieldType.FLOAT,
                    constraints=FieldConstraint(ge=0.0)),
        FieldSchema(name="genre", field_type=FieldType.ENUM,
                    constraints=FieldConstraint(
                        enum_values=["fiction", "non-fiction", "sci-fi", "mystery", "biography"]
                    )),
        FieldSchema(name="published_date", field_type=FieldType.DATE, nullable=True),
        FieldSchema(name="in_stock", field_type=FieldType.BOOLEAN, default=True),
    ],
)

customer = EntitySchema(
    name="Customer",
    storage_engine=StorageEngine.SQL,
    description="A registered bookstore customer.",
    fields=[
        FieldSchema(name="id", field_type=FieldType.UUID, primary_key=True),
        FieldSchema(name="email", field_type=FieldType.STRING, unique=True),
        FieldSchema(name="name", field_type=FieldType.STRING),
        FieldSchema(name="joined_at", field_type=FieldType.DATETIME),
    ],
)

order = EntitySchema(
    name="Order",
    storage_engine=StorageEngine.SQL,
    description="A purchase order linking a customer to books.",
    fields=[
        FieldSchema(name="id", field_type=FieldType.UUID, primary_key=True),
        FieldSchema(name="customer_id", field_type=FieldType.UUID, indexed=True),
        FieldSchema(name="total", field_type=FieldType.FLOAT,
                    constraints=FieldConstraint(ge=0.0)),
        FieldSchema(name="status", field_type=FieldType.ENUM,
                    constraints=FieldConstraint(
                        enum_values=["pending", "confirmed", "shipped", "delivered", "cancelled"]
                    )),
        FieldSchema(name="created_at", field_type=FieldType.DATETIME),
    ],
)

review = EntitySchema(
    name="Review",
    storage_engine=StorageEngine.SQL,
    description="A customer review of a book. Text is semantic-searchable.",
    fields=[
        FieldSchema(name="id", field_type=FieldType.UUID, primary_key=True),
        FieldSchema(name="book_id", field_type=FieldType.UUID, indexed=True),
        FieldSchema(name="customer_id", field_type=FieldType.UUID, indexed=True),
        FieldSchema(name="rating", field_type=FieldType.INTEGER,
                    constraints=FieldConstraint(ge=1, le=5)),
        FieldSchema(name="text", field_type=FieldType.TEXT,
                    description="Free-text review body — vectorized for semantic search.",
                    embedding=EmbeddingConfig(
                        model="text-embedding-3-small",
                        dimensions=1536,
                        chunk_strategy="paragraph",
                    )),
        FieldSchema(name="created_at", field_type=FieldType.DATETIME),
    ],
)

print("✅ Entities defined:")
for e in [book, customer, order, review]:
    print(f"   {e.name} ({e.storage_engine.value}) — {len(e.fields)} fields")

# ---------------------------------------------------------------------------
# 2. Define Relationships
# ---------------------------------------------------------------------------

relationships = [
    # Hard FK: Order → Customer
    RelationshipSchema(
        name="order_customer",
        source_entity="Order",
        target_entity="Customer",
        relationship_type=RelationshipType.HARD,
        cardinality=Cardinality.MANY_TO_ONE,
        source_field="customer_id",
        target_field="id",
        description="Each order belongs to one customer.",
    ),
    # Hard FK: Review → Book
    RelationshipSchema(
        name="review_book",
        source_entity="Review",
        target_entity="Book",
        relationship_type=RelationshipType.HARD,
        cardinality=Cardinality.MANY_TO_ONE,
        source_field="book_id",
        target_field="id",
        description="Each review is about one book.",
    ),
    # Hard FK: Review → Customer
    RelationshipSchema(
        name="review_customer",
        source_entity="Review",
        target_entity="Customer",
        relationship_type=RelationshipType.HARD,
        cardinality=Cardinality.MANY_TO_ONE,
        source_field="customer_id",
        target_field="id",
        description="Each review is written by one customer.",
    ),
    # Soft/Semantic: Book ↔ Review (vector similarity for recommendations)
    RelationshipSchema(
        name="book_similar_reviews",
        source_entity="Book",
        target_entity="Review",
        relationship_type=RelationshipType.SOFT,
        cardinality=Cardinality.MANY_TO_MANY,
        description="Semantic similarity between book descriptions and review text.",
    ),
]

print(f"\n✅ Relationships defined: {len(relationships)}")
for r in relationships:
    print(f"   {r.name}: {r.source_entity}{r.target_entity} ({r.relationship_type.value}, {r.cardinality.value})")

# ---------------------------------------------------------------------------
# 3. Define Domains
# ---------------------------------------------------------------------------

catalog_domain = DomainSchema(
    name="Catalog",
    entities=["Book", "Review"],
    description="Book catalog and reviews — handles browsing, search, and recommendations.",
    agent_config=AgentConfig(
        model_provider="gemini",
        model_name="gemini-2.5-flash",
        reasoning_level=ReasoningLevel.MEDIUM,
        system_prompt="You are the Catalog domain agent. Help users find books, browse reviews, and get recommendations.",
    ),
)

commerce_domain = DomainSchema(
    name="Commerce",
    entities=["Customer", "Order"],
    description="Customer management and order processing.",
    agent_config=AgentConfig(
        model_provider="gemini",
        model_name="gemini-2.5-pro",
        reasoning_level=ReasoningLevel.HIGH,
        temperature=0.3,  # Lower temp for transactional operations
        system_prompt="You are the Commerce domain agent. Handle customer accounts and order operations accurately.",
    ),
)

print(f"\n✅ Domains defined: {len([catalog_domain, commerce_domain])}")
for d in [catalog_domain, commerce_domain]:
    print(f"   {d.name}: entities={d.entities}, model={d.agent_config.model_name}, reasoning={d.agent_config.reasoning_level.value}")

# ---------------------------------------------------------------------------
# 4. Assemble the Full Schema
# ---------------------------------------------------------------------------

schema = AgenticSchema(
    project_name="Bookstore",
    description="Online bookstore with catalog, commerce, and semantic review search.",
    entities=[book, customer, order, review],
    relationships=relationships,
    domains=[catalog_domain, commerce_domain],
)

print(f"\n✅ AgenticSchema assembled: '{schema.project_name}'")
print(f"   {len(schema.entities)} entities, {len(schema.relationships)} relationships, {len(schema.domains)} domains")

# ---------------------------------------------------------------------------
# 5. Serialize to JSON (what .ninjastack/schema.json looks like)
# ---------------------------------------------------------------------------

json_output = schema.model_dump_json(indent=2)
print(f"\n📄 Serialized schema ({len(json_output)} bytes):")
print(json_output[:500] + "..." if len(json_output) > 500 else json_output)

# You can also save to disk:
# save_schema(schema, ".ninjastack/schema.json")

What This Shows

  • Entities with typed fields, constraints, and storage engines
  • Relationships — hard (FK), soft (semantic), graph
  • Domains — logical groupings with agent config
  • AgenticSchema — the full project container
  • Serialization to JSON

Run It

PYTHONPATH=examples/bookstore uv run python examples/bookstore/01_schema_definition.py