Entities say what the data is before anyone asks where it lives.
1. Define a Logical Shape
Entities are Python classes that extend hb.Entity. Use hb.field(...) when you want metadata, defaults, storage placement, or compute Hooks; use annotations for the shortest plain-field form.
import heavenbase as hb
class Document(hb.Entity):
title = hb.field(hb.ShortText).desc("Display title")
body = hb.field(hb.LongText)
tags = hb.field(hb.Array[hb.ShortText]).default([])
embedding = hb.field(hb.Vector[2])
print(Document.schema().entity_id)
The class above creates a logical entity named document. HeavenBase also injects object_id and name when you do not define your own object_id.
2. Choose Logical Types
Logical types describe meaning. Backends decide how those meanings are physically stored.
| Type | Use it for |
|---|
Identifier | Stable IDs and slugs. |
ShortText, MediumText, LongText | Labels, descriptions, and full documents. |
Integer, Float, Boolean | Scalar values. |
Categorical([...]) | String or integer values limited to known choices. |
Timestamp(unit="ms"), Date() | Instants and calendar dates. |
Array[...] | Lists with an optional item type. |
Vector[dim] | Embeddings and other numeric vectors. |
Json | JSON-compatible objects. |
HyperG | Repeatable relation-like records. |
Artifact | Binary payloads. |
HeavenBase does not expose separate Datetime or Interval logical types yet. Use Timestamp for instants, Date for calendar dates, and a numeric field with a unit in its description for durations.
3. Understand Identity
Every entity row has exactly one user-facing object_id. If you omit it, HeavenBase uses the row’s name to derive a deterministic ID.
ws = hb.HeavenBase("core-entities", preset="debug")
ws.register(Document)
first_id = ws.upsert(
Document,
{
"name": "Agent guide",
"title": "Agent guide",
"body": "Use Catalog for objects and MetaSchema for structure.",
"tags": ["docs"],
"embedding": [1.0, 0.0],
},
)
second_id = ws.upsert(
Document,
{
"name": "Agent guide",
"title": "Agent guide",
"body": "Updated body.",
"tags": ["docs"],
"embedding": [1.0, 0.0],
},
)
print(first_id == second_id)
Define object_id yourself only when a different natural key should drive identity.
from heavenbase.utils import hash_id
def stock_id(sku: str, warehouse: str) -> str:
return hash_id("StockItem", sku, warehouse)
class StockItem(hb.Entity):
object_id = hb.field(hb.Identifier).compute(stock_id, inputs=["sku", "warehouse"])
sku = hb.field(hb.ShortText)
warehouse = hb.field(hb.ShortText)
quantity = hb.field(hb.Integer).default(0)
ws.register(StockItem)
stock_id_value = ws.upsert(StockItem, {"sku": "SKU-001", "warehouse": "east"})
print(ws.get(stock_id_value, entity=StockItem)["quantity"])
4. Add Compute Hooks
Computed fields run when rows are written. Query-compute Hooks run when a query value needs to be normalized before dispatch. The common case is accepting a text query for a vector field.
def embed_text(text: str) -> list[float]:
return [1.0, 0.0] if "agent" in text.lower() else [0.0, 1.0]
class Issue(hb.Entity):
title = hb.field(hb.ShortText)
summary = hb.field(hb.LongText)
emb = (
hb.field(hb.Vector[2])
.compute(embed_text, inputs=["summary"])
.query_compute(embed_text)
)
ws.register(Issue)
ws.upsert(Issue, {"object_id": "i1", "title": "Agent memory", "summary": "Design agent memory around search."})
frame = ws.query(Issue).near(Issue.emb, "agent search", top_k=1).select("title", "score").execute()
print(frame.rows()[0]["title"])
5. Compile from JSON
Agents and external clients can define entities with JSON-compatible schema dictionaries.
Product = hb.Entity.from_schema(
{
"entity_id": "product",
"name": "Product",
"fields": {
"name": "ShortText",
"price": "Float",
"status": {
"type": {
"type": "categorical",
"values": ["draft", "active"],
},
},
"created_at": {"type": {"type": "timestamp", "unit": "ms"}},
"available_on": "Date",
},
}
)
print(Product.schema().entity_id)
The generated class behaves like a normal hb.Entity subclass and can be passed to ws.register(...), ws.upsert(...), and ws.query(...).
Further Exploration
Related resources:
- Workspace - Register entities in an application boundary
- Routing - Store fields on selected backends
- Query - Filter, search, and inspect entity rows
- Catalog - Discover concrete objects after writes