Skip to content

Commit fdbeece

Browse files
committed
docs: update examples with commit_model
1 parent 258bd25 commit fdbeece

File tree

5 files changed

+63
-67
lines changed

5 files changed

+63
-67
lines changed

README.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,29 @@ pip install memstate[chromadb]
2929
```
3030

3131
```python
32-
from memstate import MemoryStore, Fact, SQLiteStorage, HookError
32+
from pydantic import BaseModel
33+
from memstate import MemoryStore, SQLiteStorage, HookError
3334
from memstate.integrations.chroma import ChromaSyncHook
3435
import chromadb
3536

37+
# Define your Data Schema
38+
class UserPref(BaseModel):
39+
content: str
40+
role: str
41+
3642
# Storage
3743
sqlite = SQLiteStorage("agent_memory.db")
3844
chroma = chromadb.Client()
3945

4046
# Hook: sync vectors atomically with SQL
4147
mem = MemoryStore(sqlite)
4248
mem.add_hook(ChromaSyncHook(chroma, "agent_memory", text_field="content", metadata_fields=["role"]))
49+
mem.register_schema("preference", UserPref) # Register type for validation
4350

44-
# Multi-step agent workflow
45-
# Each commit is atomic: if vector DB fails, SQL write is automatically rolled back
46-
mem.commit(Fact(
47-
type="profile_update",
48-
payload={"content": "User prefers vegetarian", "role": "preference"}
49-
))
50-
51-
# Attempt a task that may fail
51+
# Atomic Commit (Type-Safe)
52+
# Validates Pydantic model -> Writes SQL -> Upserts Vector
5253
try:
53-
mem.commit(Fact(
54-
type="shopping_list",
55-
payload={"content": "Generate shopping list based on plan", "role": "task"}
56-
))
54+
mem.commit_model(model=UserPref(content="User prefers vegetarian", role="preference"))
5755
except HookError as e:
5856
print("Commit failed, operation rolled back automatically:", e)
5957

@@ -135,7 +133,7 @@ SQL write + Vector upsert
135133
* **Rollback**: undo N steps across SQL and vectors
136134
* **Type safety**: Pydantic validation prevents malformed JSON
137135
* **Append-only Fact Log**: full versioned history
138-
* **Crash-safe atomicity**: if a vector DB write fails, the entire memory operation (SQL + vector) is **rolled back**.
136+
* **Crash-safe atomicity**: if a vector DB write fails, the entire memory operation (SQL + vector) is **rolled back**.
139137
No partial writes, no ghost embeddings, no inconsistent checkpoints.
140138

141139
---

examples/demo_with_memstate.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from pydantic import BaseModel
44

5-
from memstate import Constraint, Fact, HookError, MemoryStore, Operation
5+
from memstate import Constraint, HookError, MemoryStore, Operation
66
from memstate.backends.sqlite import SQLiteStorage
77

88

@@ -35,13 +35,15 @@ def __call__(self, op, fact_id, data):
3535
mem.register_schema("food", Food, Constraint(singleton_key="user_id"))
3636

3737
# Seed initial state
38-
mem.commit(Fact(type="food", payload={"user_id": "bob", "diet": "likes meat"}), session_id="session_id")
38+
meet_food = Food(user_id="bob", diet="likes meat")
39+
mem.commit_model(meet_food, session_id="session_id")
3940

4041
print("Initial state:", mem.query())
4142

4243
print("\nStarting transaction to update diet → 'vegetarian'...")
4344
try:
44-
mem.commit(Fact(type="food", payload={"user_id": "bob", "diet": "vegetarian"}), session_id="session_id")
45+
vegetarian_food = Food(user_id="bob", diet="vegetarian")
46+
mem.commit_model(vegetarian_food, session_id="session_id")
4547
except HookError as e:
4648
print("ERROR:", e)
4749
print("Rollback triggered...")

examples/document_lifecycle.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22
import shutil
33

44
import chromadb
5+
from pydantic import BaseModel
56

6-
from memstate import Fact, MemoryStore
7+
from memstate import MemoryStore
78
from memstate.backends.sqlite import SQLiteStorage
89
from memstate.integrations.chroma import ChromaSyncHook
910

1011

12+
class Project(BaseModel):
13+
content: str
14+
status: str
15+
author: str
16+
17+
1118
def clean_environment():
1219
"""Removes old database files for a clean run."""
1320
if os.path.exists("lifecycle_demo.db"):
@@ -56,18 +63,16 @@ def run_demo():
5663

5764
memory = MemoryStore(sqlite)
5865
memory.add_hook(hook)
66+
memory.register_schema("project", Project)
5967
collection = chroma_client.get_collection("docs_memory")
6068

6169
# =========================================================================
6270
# STEP 1: CREATE (INSERT)
6371
# =========================================================================
6472
print(f"1️⃣ Creating new document...")
6573

66-
fact = Fact(
67-
type="doc",
68-
payload={"content": "Project Alpha is currently in planning phase.", "status": "draft", "author": "Alice"},
69-
)
70-
doc_id = memory.commit(fact)
74+
project = Project(content="Project Alpha is currently in planning phase.", status="draft", author="Alice")
75+
doc_id = memory.commit_model(model=project)
7176

7277
print_state("AFTER INSERT", doc_id, sqlite, collection)
7378

@@ -77,17 +82,12 @@ def run_demo():
7782
print(f"\n2️⃣ Updating document (content + metadata)...")
7883

7984
# We use the SAME fact ID to perform an update
80-
memory.commit(
81-
Fact(
82-
id=doc_id,
83-
type="doc",
84-
payload={
85-
"content": "Project Alpha has been CANCELLED.", # Content changed
86-
"status": "archived", # Metadata changed
87-
"author": "Alice",
88-
},
89-
)
85+
updated_project = Project(
86+
content="Project Alpha has been CANCELLED.", # Content changed
87+
status="archived", # Metadata changed
88+
author="Alice",
9089
)
90+
memory.commit_model(fact_id=doc_id, model=updated_project)
9191

9292
print_state("AFTER UPDATE", doc_id, sqlite, collection)
9393

examples/main_demo.py

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
77

8-
from memstate import Constraint, Fact, InMemoryStorage, MemoryStore
8+
from memstate import Constraint, InMemoryStorage, MemoryStore
99

1010
# --- 1. Defining Schemes ---
1111

@@ -45,17 +45,8 @@ class MeetingNote(BaseModel):
4545

4646
# Scenario 1: Agent learns about the user
4747
print("--- Step 1: Creating User Profile ---")
48-
fact_profile = Fact(
49-
type="user_profile",
50-
payload={
51-
"email": "alex@corp.com",
52-
"full_name": "Alex Dev",
53-
"role": "Backend",
54-
# 'level' is not passed, Pydantic will substitute 'Junior' automatically!
55-
},
56-
source="chat_onboarding",
57-
)
58-
memory.commit(fact_profile, actor="Agent_Smith")
48+
fact_profile = UserProfile(email="alex@corp.com", full_name="Alex Dev", role="Backend")
49+
memory.commit_model(model=fact_profile, source="chat_onboarding", actor="Agent_Smith")
5950

6051
# Let's check what was recorded
6152
saved_profile = memory.query(typename="user_profile")[0]
@@ -66,12 +57,8 @@ class MeetingNote(BaseModel):
6657
print("\n--- Step 2: Updating Profile (Singleton Logic) ---")
6758
# The agent realized that Alex was actually a Senior.
6859
# He simply commits a new fact. The system will automatically find the old one by email and update it.
69-
fact_update = Fact(
70-
type="user_profile",
71-
payload={"email": "alex@corp.com", "full_name": "Alex Dev", "role": "Backend Lead", "level": "Senior"},
72-
source="linkedin_parser",
73-
)
74-
memory.commit(fact_update, actor="Agent_Smith", reason="found linkedin profile")
60+
fact_update = UserProfile(email="alex@corp.com", full_name="Alex Dev", role="Backend Lead", level="Senior")
61+
memory.commit_model(fact_update, source="linkedin_parser", actor="Agent_Smith", reason="found linkedin profile")
7562

7663
# We're checking. There should be one fact left, but it should be updated.
7764
profiles = memory.query(typename="user_profile")
@@ -81,15 +68,11 @@ class MeetingNote(BaseModel):
8168

8269
# Scenario 3: Agent hallucinates (writes down delusions)
8370
print("\n--- Step 3: Agent Hallucination ---")
84-
bad_fact = Fact(
85-
type="meeting_note",
86-
payload={
87-
"topic": "Salary Negotiation",
88-
"summary": "Alex agreed to work for free.", # Hallucination!
89-
},
90-
source="voice_transcription_error",
71+
bad_fact = MeetingNote(
72+
topic="Salary Negotiation",
73+
summary="Alex agreed to work for free.", # Hallucination!
9174
)
92-
memory.commit(bad_fact, actor="Agent_Smith")
75+
memory.commit_model(bad_fact, actor="Agent_Smith")
9376
print("⚠️ Bad fact committed.")
9477

9578

examples/rag_hook_demo.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,19 @@
44

55
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
66

7+
from pydantic import BaseModel
8+
79
from memstate import Fact, InMemoryStorage, MemoryStore
810

11+
12+
class KnowledgeBase(BaseModel):
13+
content: str
14+
15+
16+
class UserPref(BaseModel):
17+
theme: str
18+
19+
920
# --- Simulated Vector Store ---
1021
# In real life, this would use OpenAI Embeddings and Qdrant/Chroma
1122

@@ -80,20 +91,22 @@ def main():
8091

8192
storage = InMemoryStorage() # Our "Postgres"
8293
memory = MemoryStore(storage) # Our "Brain"
94+
memory.register_schema("knowledge_base", KnowledgeBase)
95+
memory.register_schema("user_pref", UserPref)
8396

8497
# Add hook
8598
memory.add_hook(hook)
8699

87100
print("\n--- Phase 1: Ingestion ---")
88101
# We simply commit the facts to MemState. The hook will automatically transfer them to Vectors.
89102

90-
doc1 = Fact(type="knowledge_base", payload={"content": "The moon is made of rock and dust."})
91-
doc2 = Fact(type="knowledge_base", payload={"content": "Mars is known as the Red Planet due to iron oxide."})
92-
doc3 = Fact(type="user_pref", payload={"theme": "dark"}) # This should NOT be in vectors (filter by type)
103+
doc1 = KnowledgeBase(content="The moon is made of rock and dust.")
104+
doc2 = KnowledgeBase(content="Mars is known as the Red Planet due to iron oxide.")
105+
doc3 = UserPref(theme="dark") # This should NOT be in vectors (filter by type)
93106

94-
memory.commit(doc1)
95-
memory.commit(doc2)
96-
memory.commit(doc3)
107+
memory.commit_model(model=doc1)
108+
doc2_id = memory.commit_model(model=doc2)
109+
memory.commit_model(model=doc3)
97110

98111
print("\n--- Phase 2: RAG Search (Emulation) ---")
99112
# User asks: "Tell me about the red planet"
@@ -118,7 +131,7 @@ def main():
118131
# We update ONLY MemState.
119132

120133
print("🛠 Updating Mars fact in SQL...")
121-
memory.update(doc2.id, {"payload": {"content": "Mars is the Red Planet and has two moons: Phobos and Deimos."}})
134+
memory.update(doc2_id, {"payload": {"content": "Mars is the Red Planet and has two moons: Phobos and Deimos."}})
122135

123136
# Check if the vector index has been updated AUTOMATICALLY?
124137
print("🔎 Searching again for 'Phobos'...")
@@ -132,7 +145,7 @@ def main():
132145
print("\n--- Phase 4: Forgetting (Deletion) ---")
133146
# Deleting a fact from the database
134147
print("🗑 Deleting Mars fact...")
135-
memory.delete(doc2.id)
148+
memory.delete(doc2_id)
136149

137150
# Checking the search
138151
found_ids = vector_db.search("Mars")

0 commit comments

Comments
 (0)