Skip to content

Commit 4512359

Browse files
Aayush KatariaAayush Kataria
authored andcommitted
making some code improvements
1 parent 961a4fb commit 4512359

28 files changed

Lines changed: 1154 additions & 127 deletions

.env.template

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ COSMOS_DB_LEASE_CONTAINER=leases
2424
COSMOS_DB_THROUGHPUT_MODE=serverless
2525
COSMOS_DB_AUTOSCALE_MAX_RU=1000
2626

27-
# ---- Change Feed Thresholds (set to 0 to disable) ----
28-
THREAD_SUMMARY_EVERY_N=0
29-
FACT_EXTRACTION_EVERY_N=0
30-
USER_SUMMARY_EVERY_N=0
27+
# ---- Processing thresholds (set to 0 to disable) ----
28+
THREAD_SUMMARY_EVERY_N=10
29+
FACT_EXTRACTION_EVERY_N=1
30+
USER_SUMMARY_EVERY_N=20
3131

3232
# ---- AI Foundry / Azure OpenAI ----
3333
AI_FOUNDRY_ENDPOINT=https://<your-account>.openai.azure.com/

README.md

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ memory.add_cosmos(user_id=USER, thread_id=THREAD, role="user", content="I love C
137137
memory.add_cosmos(user_id=USER, thread_id=THREAD, role="assistant", content="It is fantastic.")
138138

139139
# Run the processing pipeline (thread summary + fact extraction + user summary)
140-
memory.flush(user_id=USER, thread_id=THREAD)
140+
memory.process_now(user_id=USER, thread_id=THREAD)
141141

142142
# Search semantically across the stored memory
143143
hits = memory.search_cosmos(user_id=USER, query_text="Cosmos DB preferences", top=5)
@@ -169,12 +169,45 @@ See [`Samples/`](Samples/) for end-to-end scenarios (chat memory, RAG, multi-age
169169
|---|---|---|
170170
| **Turn** | One message (user or assistant) — the raw conversation atom | `add_cosmos(...)`, `add_local(...)` |
171171
| **Thread summary** | LLM-generated, incrementally updated rollup of a single thread | `generate_thread_summary(...)` |
172-
| **Fact** | Discrete, independently searchable assertion extracted from turns | `extract_facts(...)` |
172+
| **Fact** | Discrete, independently searchable assertion extracted from turns | `extract_memories(...)` |
173+
| **Procedural** | Behavioral rule / instruction the user wants followed | `extract_memories(...)` |
174+
| **Episodic** | Past situation → action → outcome experience (90-day TTL) | `extract_memories(...)` |
173175
| **User summary** | Cross-thread profile of what's known about a user | `generate_user_summary(...)`, `get_user_summary(...)` |
174-
| **Search** | Vector + full-text + filter; returns turns, summaries, and facts | `search_cosmos(...)` |
175-
| **Flush** | Run the full pipeline (summary → facts → user profile) for recent turns | `flush(...)`, `flush_and_wait(...)` |
176+
| **Search** | Vector + full-text + filter; returns any of the above | `search_cosmos(...)` |
177+
| **Process now** | Run the full pipeline (summary → facts → user profile) for recent turns | `process_now(...)`, `process_now_and_wait(...)` |
176178

177-
All four memory kinds live in the same Cosmos container, partitioned by `(user_id, thread_id)`, distinguished by a `memory_type` discriminator.
179+
All memory kinds live in the same Cosmos container, partitioned by `(user_id, thread_id)`, distinguished by a `type` discriminator.
180+
181+
### Memory Type Taxonomy
182+
183+
The `extract_memories` pipeline classifies each item it pulls from the conversation into one of four buckets. Every memory carries a top-level `confidence` (0.0–1.0) so retrieval can suppress weakly-grounded extractions.
184+
185+
| Bucket | Meaning | Storage type | TTL |
186+
|---|---|---|---|
187+
| Fact | Declarative knowledge ("user prefers dark mode") | `type="fact"` | none |
188+
| Procedural | Behavioral rule ("always confirm before deleting") | `type="procedural"` | none |
189+
| Episodic | Past experience: situation → action → outcome | `type="episodic"` | 90 days |
190+
| Unclassified | Item worth keeping but the LLM couldn't confidently classify | `type="fact"` + tag `sys:unclassified` | none |
191+
192+
#### Confidence Scale
193+
194+
| Range | Meaning |
195+
|---|---|
196+
| 0.9–1.0 | Directly stated and unambiguous |
197+
| 0.7–0.9 | Clearly implied, no contradicting evidence |
198+
| 0.5–0.7 | Inferred from context — plausible but not explicit |
199+
| < 0.5 | Should be in `unclassified` instead |
200+
201+
Filter at retrieval time:
202+
203+
```python
204+
results = memory.search_cosmos("user preferences", user_id="u1", min_confidence=0.7)
205+
high_conf_facts = memory.get_memories(user_id="u1", memory_type="fact", min_confidence=0.7)
206+
```
207+
208+
### Auto-trigger (per-turn extraction)
209+
210+
By default, the **InProcess processor** runs fact extraction after **every turn** (`FACT_EXTRACTION_EVERY_N=1`) and a thread summary every **10 turns** (`THREAD_SUMMARY_EVERY_N=10`). The trigger fires inside `push_to_cosmos()` after the turn is durably written, so calling `process_now()` is normally redundant — it remains as an explicit "flush now" hook. The Durable backend uses the same defaults via the change-feed function app.
178211

179212
---
180213

@@ -186,8 +219,8 @@ Pick at construction time via the `processor=` kwarg.
186219
|---|---|---|
187220
| Infra | None — just `pip install` | Sibling Azure Function app |
188221
| Best for | Prototypes, low TPS, single-agent | Fleet / multi-agent / high TPS |
189-
| `flush()` | Synchronous, returns when done | No-op (work runs async on change feed) |
190-
| `flush_and_wait()` | Returns immediately after flush | Polls until summary visible (RU-costly; tests/demos) |
222+
| `process_now()` | Synchronous, returns when done | No-op (work runs async on change feed) |
223+
| `process_now_and_wait()` | Returns immediately after flush | Polls until summary visible (RU-costly; tests/demos) |
191224

192225
```python
193226
from agent_memory_toolkit import CosmosMemoryClient, DurableFunctionProcessor
@@ -208,8 +241,8 @@ memory = CosmosMemoryClient(..., processor=DurableFunctionProcessor())
208241
| `MemoryProcessor` | `agent_memory_toolkit` | Protocol that any processor backend implements |
209242
| `InProcessProcessor` | `agent_memory_toolkit` | Default backend — runs the pipeline in-process |
210243
| `DurableFunctionProcessor` | `agent_memory_toolkit` | Marker backend — work runs in sibling Function app via change feed |
211-
| `client.flush()` || Run the pipeline for recent turns (in-process) or no-op (remote) |
212-
| `client.flush_and_wait()` || Opt-in poll until processing completes; useful for tests/demos with the remote backend |
244+
| `client.process_now()` || Run the pipeline for recent turns (in-process) or no-op (remote) |
245+
| `client.process_now_and_wait()` || Opt-in poll until processing completes; useful for tests/demos with the remote backend |
213246
| `MemoryRecord`, `MemoryType`, `Role` | `agent_memory_toolkit` | Pydantic models / enums |
214247

215248
Async equivalents (`AsyncInProcessProcessor`, `AsyncDurableFunctionProcessor`) live in `agent_memory_toolkit.aio`.
@@ -243,6 +276,6 @@ tests/ Unit + integration tests (pytest)
243276

244277
## Migration notes
245278

246-
- **`agent_memory_toolkit.processing.ProcessingClient` is removed.** Drop the import and call `client.flush()` (or `client.flush_and_wait()`) instead. Same for the async `AsyncProcessingClient`.
279+
- **`agent_memory_toolkit.processing.ProcessingClient` is removed.** Drop the import and call `client.process_now()` (or `client.process_now_and_wait()`) instead. Same for the async `AsyncProcessingClient`.
247280
- **New `processor=` kwarg.** Defaults to `InProcessProcessor()` — existing code keeps its current behavior with no edits.
248281
- **`adf_endpoint` / `adf_key` constructor kwargs are gone.** The SDK no longer makes HTTP calls to the Function app at runtime; the Function app reads from the Cosmos change feed.

Samples/Demo_function_app.ipynb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
"\n",
3333
"* `add_cosmos(..., memory_type=\"turn\")` → writes the raw turn to Cosmos.\n",
3434
"* The change-feed-triggered Function App reads new turns and runs orchestrators.\n",
35-
"* `flush()` is a **debug-logged no-op** — the Function App owns processing.\n",
36-
"* `flush_and_wait()` polls Cosmos for the summary doc; useful for demos / tests (RU-costly)."
35+
"* `process_now()` is a **debug-logged no-op** — the Function App owns processing.\n",
36+
"* `process_now_and_wait()` polls Cosmos for the summary doc; useful for demos / tests (RU-costly)."
3737
]
3838
},
3939
{
@@ -215,7 +215,7 @@
215215
"source": [
216216
"## 4. Verify the SDK did NOT process locally\n",
217217
"\n",
218-
"`flush()` is a debug-logged no-op when using `DurableFunctionProcessor`. The Function App's change-feed\n",
218+
"`process_now()` is a debug-logged no-op when using `DurableFunctionProcessor`. The Function App's change-feed\n",
219219
"trigger fires asynchronously — usually within a second or two."
220220
]
221221
},
@@ -236,14 +236,14 @@
236236
"name": "stdout",
237237
"output_type": "stream",
238238
"text": [
239-
"flush() returned — no LLM call was made by the SDK.\n"
239+
"process_now() returned — no LLM call was made by the SDK.\n"
240240
]
241241
}
242242
],
243243
"source": [
244244
"# No-op: the function app owns processing.\n",
245-
"memory.flush(user_id=USER_ID, thread_id=THREAD_ID)\n",
246-
"print(\"flush() returned — no LLM call was made by the SDK.\")"
245+
"memory.process_now(user_id=USER_ID, thread_id=THREAD_ID)\n",
246+
"print(\"process_now() returned — no LLM call was made by the SDK.\")"
247247
]
248248
},
249249
{
@@ -253,7 +253,7 @@
253253
"source": [
254254
"## 5. Wait for the Function App to produce a summary\n",
255255
"\n",
256-
"`flush_and_wait` polls Cosmos for the thread's summary doc until `timeout` seconds. This is **RU-costly**\n",
256+
"`process_now_and_wait` polls Cosmos for the thread's summary doc until `timeout` seconds. This is **RU-costly**\n",
257257
"(repeated `get_memories(memory_type='summary')` queries) — only use it for demos and tests.\n",
258258
"\n",
259259
"> If this returns `False`, check that:\n",
@@ -453,7 +453,7 @@
453453
],
454454
"source": [
455455
"print(\"Polling Cosmos for the auto-generated summary…\")\n",
456-
"ok = memory.flush_and_wait(user_id=USER_ID, thread_id=THREAD_ID, timeout=120.0)\n",
456+
"ok = memory.process_now_and_wait(user_id=USER_ID, thread_id=THREAD_ID, timeout=120.0)\n",
457457
"print(f\"Summary available: {ok}\")"
458458
]
459459
},

Samples/Demo_function_app_async.ipynb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
"id": "99f63822",
187187
"metadata": {},
188188
"source": [
189-
"## 4. `flush()` is a no-op locally"
189+
"## 4. `process_now()` is a no-op locally"
190190
]
191191
},
192192
{
@@ -206,13 +206,13 @@
206206
"name": "stdout",
207207
"output_type": "stream",
208208
"text": [
209-
"flush() returned — no LLM call was made by the SDK.\n"
209+
"process_now() returned — no LLM call was made by the SDK.\n"
210210
]
211211
}
212212
],
213213
"source": [
214-
"await memory.flush(user_id=USER_ID, thread_id=THREAD_ID)\n",
215-
"print(\"flush() returned — no LLM call was made by the SDK.\")"
214+
"await memory.process_now(user_id=USER_ID, thread_id=THREAD_ID)\n",
215+
"print(\"process_now() returned — no LLM call was made by the SDK.\")"
216216
]
217217
},
218218
{
@@ -407,7 +407,7 @@
407407
],
408408
"source": [
409409
"print(\"Polling Cosmos for the auto-generated summary…\")\n",
410-
"ok = await memory.flush_and_wait(user_id=USER_ID, thread_id=THREAD_ID, timeout=120.0)\n",
410+
"ok = await memory.process_now_and_wait(user_id=USER_ID, thread_id=THREAD_ID, timeout=120.0)\n",
411411
"print(f\"Summary available: {ok}\")"
412412
]
413413
},

Samples/scenario_counter_tuning.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
+--------------------------+---------+------------------------------------+
1818
1919
This sample writes a transcript long enough to cross the thread-level
20-
thresholds and uses :meth:`flush_and_wait` to *poll* Cosmos until the
21-
function app has produced a summary. ``flush_and_wait`` is intended for
20+
thresholds and uses :meth:`process_now_and_wait` to *poll* Cosmos until the
21+
function app has produced a summary. ``process_now_and_wait`` is intended for
2222
demos/tests; production code should not poll Cosmos for orchestration
2323
state.
2424
@@ -80,14 +80,14 @@ def main() -> None:
8080
)
8181

8282
# ------------------------------------------------------------------
83-
# In durable mode .flush() is a no-op locally — the function app's
83+
# In durable mode .process_now() is a no-op locally — the function app's
8484
# change-feed trigger has already begun work asynchronously. Use
85-
# flush_and_wait() to *poll* for the summary doc; it's RU-costly so
85+
# process_now_and_wait() to *poll* for the summary doc; it's RU-costly so
8686
# use it only in demos/tests.
8787
# ------------------------------------------------------------------
8888
print("\nWaiting (poll) for thread summary to be written by function app...")
8989
t0 = time.monotonic()
90-
ok = client.flush_and_wait(
90+
ok = client.process_now_and_wait(
9191
user_id=user_id,
9292
thread_id=thread_id,
9393
timeout=90.0,

Samples/scenario_remote_processor.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
1515
Behavior notes
1616
--------------
17-
* :meth:`CosmosMemoryClient.flush` is a debug-logged no-op when using the
17+
* :meth:`CosmosMemoryClient.process_now` is a debug-logged no-op when using the
1818
durable processor — the function app owns processing.
19-
* :meth:`CosmosMemoryClient.flush_and_wait` *polls* Cosmos for the resulting
19+
* :meth:`CosmosMemoryClient.process_now_and_wait` *polls* Cosmos for the resulting
2020
summary; that costs RUs so it's opt-in and intended for demos / tests.
2121
"""
2222
from __future__ import annotations
@@ -59,12 +59,12 @@ def main() -> None:
5959
print(f" wrote {role:>9}: {content}")
6060

6161
# No-op locally; the change-feed trigger in the function app does the work.
62-
client.flush(user_id=user_id, thread_id=thread_id)
62+
client.process_now(user_id=user_id, thread_id=thread_id)
6363

6464
# Optional: block until the function app has produced a summary for this
6565
# thread. Polls Cosmos every 0.5s until ``timeout`` — RU-costly, demo only.
6666
print("\nWaiting for the function app to produce a summary...")
67-
ok = client.flush_and_wait(user_id=user_id, thread_id=thread_id, timeout=60.0)
67+
ok = client.process_now_and_wait(user_id=user_id, thread_id=thread_id, timeout=60.0)
6868
print(f"Summary available: {ok}")
6969

7070

Samples/scenario_remote_processor_async.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ async def main() -> None:
5151
print(f" wrote {role:>9}: {content}")
5252

5353
# No-op locally; the change-feed trigger in the function app does the work.
54-
await client.flush(user_id=user_id, thread_id=thread_id)
54+
await client.process_now(user_id=user_id, thread_id=thread_id)
5555

5656
# Optional: poll until the function app produces a summary. RU-costly.
5757
print("\nWaiting for the function app to produce a summary...")
58-
ok = await client.flush_and_wait(
58+
ok = await client.process_now_and_wait(
5959
user_id=user_id, thread_id=thread_id, timeout=60.0
6060
)
6161
print(f"Summary available: {ok}")

0 commit comments

Comments
 (0)