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