Skip to content

Commit ccf5146

Browse files
Aayush KatariaAayush Kataria
authored andcommitted
resolving comments
1 parent c326409 commit ccf5146

22 files changed

Lines changed: 255 additions & 64 deletions

agent_memory_toolkit/aio/chat.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,6 @@ def _build_kwargs(
146146
len(messages),
147147
)
148148
kwargs: dict[str, Any] = {"model": self._model, "messages": messages}
149-
# Force temperature=1.0 across all callers — see chat.py for rationale.
150-
kwargs["temperature"] = 1.0
151149
if response_format is not None:
152150
kwargs["response_format"] = response_format
153151
kwargs.update(extra)

agent_memory_toolkit/aio/cosmos_memory_client.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ async def close(self) -> None:
124124
self._turns_container_client = None
125125
self._counter_container_client = None
126126
self._store = None
127+
self._pipeline = None
128+
if self._processor is not None and not self._processor_explicit:
129+
await self._close_maybe_async(self._processor)
130+
self._processor = None
127131
await self._embeddings_client.close()
128132
await self._close_maybe_async(self._chat_client)
129133
for owns, cred in (
@@ -659,11 +663,12 @@ async def extract_memories(self, user_id: str, thread_id: str, recent_k: Optiona
659663
async def synthesize_procedural(self, user_id: str, *, force: bool = False) -> dict[str, Any]:
660664
processor = self._get_processor()
661665
if not isinstance(processor, AsyncInProcessProcessor):
662-
return {
663-
"status": "deferred",
664-
"reason": "durable_auto_trigger",
665-
"message": "Procedural synthesis runs reactively in the Function App; call get_procedural_prompt() later.",
666-
}
666+
raise NotImplementedError(
667+
"Procedural synthesis runs automatically after reconcile in durable mode; "
668+
"manual invocation via the SDK is not supported when the Durable Function "
669+
"app is the active processor. Use get_procedural_prompt() to read the "
670+
"latest synthesized prompt."
671+
)
667672
return await processor.synthesize_procedural(user_id=user_id, force=force)
668673

669674
async def generate_thread_summary(

agent_memory_toolkit/aio/processors/durable.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,11 @@ async def synthesize_procedural(
9999
user_id: str,
100100
force: bool = False,
101101
) -> dict[str, Any]:
102-
logger.debug(
103-
"AsyncDurableFunctionProcessor.synthesize_procedural deferred user_id=%s force=%s",
104-
user_id,
105-
force,
102+
raise NotImplementedError(
103+
"Procedural synthesis runs automatically after reconcile in durable mode; "
104+
"manual invocation via the SDK is not supported when the Durable Function "
105+
"app is the active processor."
106106
)
107-
return {"status": "deferred", "reason": "durable_auto_trigger"}
108107

109108
async def close(self) -> None:
110109
logger.debug("AsyncDurableFunctionProcessor.close no-op")

agent_memory_toolkit/aio/services/pipeline.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@
4343
parse_llm_json,
4444
)
4545
from agent_memory_toolkit.prompts._schemas import response_format_for
46+
from agent_memory_toolkit.services.pipeline import (
47+
PROCEDURAL_MAX_EPISODES,
48+
PROCEDURAL_MAX_FACTS,
49+
)
4650

4751
logger = get_logger("agent_memory_toolkit.pipeline.aio")
4852

@@ -679,6 +683,14 @@ async def synthesize_procedural(
679683
for doc in behavioral_fact_docs
680684
if isinstance(doc.get("content"), str) and doc.get("content", "").strip()
681685
]
686+
if len(behavioral_fact_docs) > PROCEDURAL_MAX_FACTS:
687+
logger.warning(
688+
"synthesize_procedural truncating behavioral facts user_id=%s total=%d cap=%d",
689+
user_id,
690+
len(behavioral_fact_docs),
691+
PROCEDURAL_MAX_FACTS,
692+
)
693+
behavioral_fact_docs = behavioral_fact_docs[:PROCEDURAL_MAX_FACTS]
682694
behavioral_fact_ids = [doc["id"] for doc in behavioral_fact_docs]
683695

684696
episodic_docs = await self._container.query_items(
@@ -709,6 +721,14 @@ async def synthesize_procedural(
709721
if isinstance(doc.get("metadata", {}).get("lesson"), str)
710722
and doc.get("metadata", {}).get("lesson", "").strip()
711723
]
724+
if len(episodic_with_lessons) > PROCEDURAL_MAX_EPISODES:
725+
logger.warning(
726+
"synthesize_procedural truncating episodic lessons user_id=%s total=%d cap=%d",
727+
user_id,
728+
len(episodic_with_lessons),
729+
PROCEDURAL_MAX_EPISODES,
730+
)
731+
episodic_with_lessons = episodic_with_lessons[:PROCEDURAL_MAX_EPISODES]
712732
source_episodic_ids = [doc["id"] for doc in episodic_with_lessons]
713733

714734
current_source_ids = set(behavioral_fact_ids) | set(source_episodic_ids)

agent_memory_toolkit/aio/store/memory_store.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -540,12 +540,22 @@ async def list_tags(
540540

541541
async def _mutate_tags(self, memory_id: str, user_id: str, thread_id: str, tags: list[str], *, add: bool) -> None:
542542
from azure.core import MatchConditions
543-
from azure.cosmos.exceptions import CosmosAccessConditionFailedError
543+
from azure.cosmos.exceptions import (
544+
CosmosAccessConditionFailedError,
545+
CosmosResourceNotFoundError,
546+
)
544547

545548
normalized = {t.strip().lower() for t in tags if t and t.strip()}
546549
attempts = 0
547550
while True:
548-
doc = await self._container.read_item(item=memory_id, partition_key=[user_id, thread_id])
551+
try:
552+
doc = await self._container.read_item(item=memory_id, partition_key=[user_id, thread_id])
553+
target_container = self._container
554+
except CosmosResourceNotFoundError:
555+
if self._turns_container is None:
556+
raise
557+
doc = await self._turns_container.read_item(item=memory_id, partition_key=[user_id, thread_id])
558+
target_container = self._turns_container
549559
existing_tags = set(doc.get("tags", []))
550560
if add:
551561
existing_tags.update(normalized)
@@ -558,7 +568,7 @@ async def _mutate_tags(self, memory_id: str, user_id: str, thread_id: str, tags:
558568
if etag := doc.get("_etag"):
559569
kwargs.update(match_condition=MatchConditions.IfNotModified, etag=etag)
560570
try:
561-
await self._container.replace_item(**kwargs)
571+
await target_container.replace_item(**kwargs)
562572
return
563573
except CosmosAccessConditionFailedError as exc:
564574
attempts += 1

agent_memory_toolkit/chat.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,17 +154,16 @@ def _build_kwargs(
154154
len(messages),
155155
)
156156
kwargs: dict[str, Any] = {"model": self._model, "messages": messages}
157+
if response_format is not None:
158+
kwargs["response_format"] = response_format
159+
kwargs.update(extra)
157160
# Force temperature=1.0 across all callers. Newer Azure OpenAI models
158161
# (gpt-5.x family, o-series reasoning models) only accept the default
159162
# value (1.0) and reject any other; older models (gpt-4o, gpt-4o-mini)
160163
# accept 1.0 as a valid value. Hardcoding to 1.0 keeps behavior uniform
161164
# across the deployment matrix and lets prompt engineering — not a
162165
# sampling knob — be the sole control for output determinism.
163166
kwargs["temperature"] = 1.0
164-
if response_format is not None:
165-
kwargs["response_format"] = response_format
166-
kwargs.update(extra)
167-
kwargs["temperature"] = 1.0
168167
return kwargs
169168

170169
def generate(

agent_memory_toolkit/cosmos_memory_client.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ def close(self) -> None:
113113
self._counter_container_client = None
114114
self._store = None
115115
self._pipeline = None
116+
if self._processor is not None and not self._processor_explicit:
117+
self._close_sync_closeable(self._processor)
118+
self._processor = None
116119
self._close_sync_closeable(self._chat_client)
117120
self._close_sync_closeable(self._embeddings_client)
118121
for owns, cred in (
@@ -657,15 +660,14 @@ def extract_memories(
657660
return self._get_pipeline().extract_memories(user_id, thread_id, recent_k)
658661

659662
def synthesize_procedural(self, user_id: str, *, force: bool = False) -> dict[str, Any]:
660-
"""Trigger synthesized procedural prompt generation for a user."""
661663
processor = self._get_processor()
662664
if not isinstance(processor, InProcessProcessor):
663-
logger.debug("synthesize_procedural deferred to Function App auto-trigger user_id=%s", user_id)
664-
return {
665-
"status": "deferred",
666-
"reason": "durable_auto_trigger",
667-
"message": "Procedural synthesis runs reactively in the Function App; call get_procedural_prompt() later.",
668-
}
665+
raise NotImplementedError(
666+
"Procedural synthesis runs automatically after reconcile in durable mode; "
667+
"manual invocation via the SDK is not supported when the Durable Function "
668+
"app is the active processor. Use get_procedural_prompt() to read the "
669+
"latest synthesized prompt."
670+
)
669671
return processor.synthesize_procedural(user_id=user_id, force=force)
670672

671673
def generate_thread_summary(

agent_memory_toolkit/processors/durable.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,11 @@ def synthesize_procedural(
104104
user_id: str,
105105
force: bool = False,
106106
) -> dict[str, Any]:
107-
logger.debug(
108-
"DurableFunctionProcessor.synthesize_procedural deferred user_id=%s force=%s",
109-
user_id,
110-
force,
107+
raise NotImplementedError(
108+
"Procedural synthesis runs automatically after reconcile in durable mode; "
109+
"manual invocation via the SDK is not supported when the Durable Function "
110+
"app is the active processor."
111111
)
112-
return {"status": "deferred", "reason": "durable_auto_trigger"}
113112

114113
def close(self) -> None:
115114
logger.debug("DurableFunctionProcessor.close no-op")

agent_memory_toolkit/prompts/_schemas.py

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,42 +174,91 @@
174174

175175
# ---------------------------------------------------------------------------
176176
# summarize.prompty — first-pass thread summary
177+
#
178+
# Mirrors the 6-field shape the prompty actually instructs the model to emit
179+
# (see ``summarize.prompty`` lines ~69-88). Strict mode would silently drop
180+
# every field outside this list; the consumer in ``services/pipeline.py``
181+
# reads ``parsed["overview"]`` and stores ``parsed`` whole under
182+
# ``metadata.structured_summary``, so the schema must carry the full shape.
177183
# ---------------------------------------------------------------------------
184+
_SUMMARY_ACTION_ITEM = {
185+
"type": "object",
186+
"properties": {
187+
"owner": {"type": ["string", "null"]},
188+
"task": {"type": "string"},
189+
"deadline": {"type": ["string", "null"]},
190+
},
191+
"required": ["owner", "task", "deadline"],
192+
"additionalProperties": False,
193+
}
194+
178195
SUMMARIZE_SCHEMA: dict[str, Any] = {
179196
"type": "object",
180197
"properties": {
181-
"summary": {"type": "string"},
198+
"overview": {"type": "string"},
182199
"key_points": {"type": "array", "items": {"type": "string"}},
200+
"decisions": {"type": "array", "items": {"type": "string"}},
201+
"open_issues": {"type": "array", "items": {"type": "string"}},
202+
"action_items": {"type": "array", "items": _SUMMARY_ACTION_ITEM},
183203
"topics": {"type": "array", "items": {"type": "string"}},
184204
},
185-
"required": ["summary", "key_points", "topics"],
205+
"required": [
206+
"overview",
207+
"key_points",
208+
"decisions",
209+
"open_issues",
210+
"action_items",
211+
"topics",
212+
],
186213
"additionalProperties": False,
187214
}
188215

189216

190217
# ---------------------------------------------------------------------------
191218
# summarize_update.prompty — incremental thread summary update
219+
# Same shape as the first-pass schema; both prompties emit the same payload.
192220
# ---------------------------------------------------------------------------
193221
SUMMARIZE_UPDATE_SCHEMA: dict[str, Any] = SUMMARIZE_SCHEMA
194222

195223

196224
# ---------------------------------------------------------------------------
197225
# user_summary.prompty — first-pass user profile
226+
#
227+
# Mirrors the 8 sections the prompty body documents (see
228+
# ``user_summary.prompty`` lines ~35-86 and the JSON example block). Each
229+
# section is a flat string array; the full ``parsed`` dict is persisted under
230+
# ``metadata.structured_summary`` and the ``content`` field is composed from
231+
# ``key_facts`` for vector retrieval.
198232
# ---------------------------------------------------------------------------
199233
USER_SUMMARY_SCHEMA: dict[str, Any] = {
200234
"type": "object",
201235
"properties": {
202-
"summary": {"type": "string"},
203-
"key_attributes": {"type": "array", "items": {"type": "string"}},
236+
"key_facts": {"type": "array", "items": {"type": "string"}},
237+
"personal_preferences": {"type": "array", "items": {"type": "string"}},
238+
"account_environment": {"type": "array", "items": {"type": "string"}},
239+
"goals_current_work": {"type": "array", "items": {"type": "string"}},
240+
"behavioral_patterns": {"type": "array", "items": {"type": "string"}},
241+
"compliance_requirements": {"type": "array", "items": {"type": "string"}},
242+
"open_items": {"type": "array", "items": {"type": "string"}},
204243
"topics": {"type": "array", "items": {"type": "string"}},
205244
},
206-
"required": ["summary", "key_attributes", "topics"],
245+
"required": [
246+
"key_facts",
247+
"personal_preferences",
248+
"account_environment",
249+
"goals_current_work",
250+
"behavioral_patterns",
251+
"compliance_requirements",
252+
"open_items",
253+
"topics",
254+
],
207255
"additionalProperties": False,
208256
}
209257

210258

211259
# ---------------------------------------------------------------------------
212260
# user_summary_update.prompty — incremental user profile update
261+
# Same shape as the first-pass schema.
213262
# ---------------------------------------------------------------------------
214263
USER_SUMMARY_UPDATE_SCHEMA: dict[str, Any] = USER_SUMMARY_SCHEMA
215264

agent_memory_toolkit/prompts/dedup.prompty

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ description: Reconcile a pool of active facts — collapse duplicates and resolv
55
model:
66
apiType: chat
77
options:
8-
temperature: 0.0
98
seed: 43
109
maxOutputTokens: 2000
1110
additionalProperties:

0 commit comments

Comments
 (0)