hy-memory 2.0.5.dev2__py3-none-any.whl → 2.0.5.dev4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hy_memory/client.py +255 -3
- hy_memory/pipelines/base.py +4 -0
- hy_memory/pipelines/writer.py +85 -31
- {hy_memory-2.0.5.dev2.dist-info → hy_memory-2.0.5.dev4.dist-info}/METADATA +1 -1
- {hy_memory-2.0.5.dev2.dist-info → hy_memory-2.0.5.dev4.dist-info}/RECORD +8 -8
- {hy_memory-2.0.5.dev2.dist-info → hy_memory-2.0.5.dev4.dist-info}/WHEEL +0 -0
- {hy_memory-2.0.5.dev2.dist-info → hy_memory-2.0.5.dev4.dist-info}/licenses/LICENSE +0 -0
- {hy_memory-2.0.5.dev2.dist-info → hy_memory-2.0.5.dev4.dist-info}/top_level.txt +0 -0
hy_memory/client.py
CHANGED
|
@@ -2631,6 +2631,7 @@ class HyMemoryClient:
|
|
|
2631
2631
|
branch: Optional[str] = None,
|
|
2632
2632
|
request_id: Optional[str] = None,
|
|
2633
2633
|
meta: Optional[Dict[str, Any]] = None,
|
|
2634
|
+
_existing_raw_ids: Optional[List[str]] = None,
|
|
2634
2635
|
) -> Dict[str, Any]:
|
|
2635
2636
|
"""写入记忆(异步)
|
|
2636
2637
|
|
|
@@ -2799,6 +2800,7 @@ class HyMemoryClient:
|
|
|
2799
2800
|
# 落库 memory_ops / WRITE_SUMMARY / pipeline_logs 的单一真相源。
|
|
2800
2801
|
request_id=request_id,
|
|
2801
2802
|
client_meta=meta,
|
|
2803
|
+
existing_raw_ids=list(_existing_raw_ids or []),
|
|
2802
2804
|
)
|
|
2803
2805
|
|
|
2804
2806
|
# mode → writer:
|
|
@@ -2847,7 +2849,10 @@ class HyMemoryClient:
|
|
|
2847
2849
|
logger.info(f"[add] done: memory_id={result.memory_id} elapsed={result.elapsed_ms:.0f}ms")
|
|
2848
2850
|
|
|
2849
2851
|
# 记录历史(非阻塞,失败仅 warning)
|
|
2850
|
-
if
|
|
2852
|
+
if (
|
|
2853
|
+
self._history_store is not None
|
|
2854
|
+
and not _existing_raw_ids
|
|
2855
|
+
):
|
|
2851
2856
|
try:
|
|
2852
2857
|
isolation_key = self._build_isolation_key(uid, agent_id)
|
|
2853
2858
|
# 从 content 或 messages 中提取用于记录的文本
|
|
@@ -2979,6 +2984,35 @@ class HyMemoryClient:
|
|
|
2979
2984
|
include_runtime_diag=True,
|
|
2980
2985
|
)
|
|
2981
2986
|
|
|
2987
|
+
def process_raw(
|
|
2988
|
+
self,
|
|
2989
|
+
raw_ids: List[str],
|
|
2990
|
+
*,
|
|
2991
|
+
user_id: str,
|
|
2992
|
+
agent_id: str = "default_agent",
|
|
2993
|
+
session_id: str = "default_session",
|
|
2994
|
+
source: str = "default",
|
|
2995
|
+
scene: Optional[str] = None,
|
|
2996
|
+
workspace_id: Optional[str] = None,
|
|
2997
|
+
branch: Optional[str] = None,
|
|
2998
|
+
request_id: Optional[str] = None,
|
|
2999
|
+
) -> Dict[str, Any]:
|
|
3000
|
+
"""Extract/reconcile existing L1 raw records without inserting raw again."""
|
|
3001
|
+
return self._loop_thread.run(
|
|
3002
|
+
self.async_process_raw(
|
|
3003
|
+
raw_ids,
|
|
3004
|
+
user_id=user_id,
|
|
3005
|
+
agent_id=agent_id,
|
|
3006
|
+
session_id=session_id,
|
|
3007
|
+
source=source,
|
|
3008
|
+
scene=scene,
|
|
3009
|
+
workspace_id=workspace_id,
|
|
3010
|
+
branch=branch,
|
|
3011
|
+
request_id=request_id,
|
|
3012
|
+
),
|
|
3013
|
+
include_runtime_diag=True,
|
|
3014
|
+
)
|
|
3015
|
+
|
|
2982
3016
|
def get_raw(self, raw_id: str) -> Optional[Dict[str, Any]]:
|
|
2983
3017
|
"""Read one SDK raw record through the explicit raw API."""
|
|
2984
3018
|
return self._loop_thread.run(self.async_get_raw(raw_id))
|
|
@@ -3266,6 +3300,148 @@ class HyMemoryClient:
|
|
|
3266
3300
|
raise RuntimeError("raw import changed the requested identity")
|
|
3267
3301
|
return {"success": True, "raw_id": node_id, "request_id": request_id}
|
|
3268
3302
|
|
|
3303
|
+
@_ensure_internal_loop
|
|
3304
|
+
async def async_process_raw(
|
|
3305
|
+
self,
|
|
3306
|
+
raw_ids: List[str],
|
|
3307
|
+
*,
|
|
3308
|
+
user_id: str,
|
|
3309
|
+
agent_id: str = "default_agent",
|
|
3310
|
+
session_id: str = "default_session",
|
|
3311
|
+
source: str = "default",
|
|
3312
|
+
scene: Optional[str] = None,
|
|
3313
|
+
workspace_id: Optional[str] = None,
|
|
3314
|
+
branch: Optional[str] = None,
|
|
3315
|
+
request_id: Optional[str] = None,
|
|
3316
|
+
) -> Dict[str, Any]:
|
|
3317
|
+
"""Process exact raw records through extraction/reconciliation in caller order."""
|
|
3318
|
+
from .models.memory import MemoryLayer
|
|
3319
|
+
from .utils.source_filter import normalize_source
|
|
3320
|
+
|
|
3321
|
+
if (
|
|
3322
|
+
not isinstance(raw_ids, list)
|
|
3323
|
+
or not raw_ids
|
|
3324
|
+
or len(raw_ids) > 100
|
|
3325
|
+
or any(not isinstance(raw_id, str) or not raw_id for raw_id in raw_ids)
|
|
3326
|
+
or len(set(raw_ids)) != len(raw_ids)
|
|
3327
|
+
):
|
|
3328
|
+
raise ValueError("raw_ids must contain 1-100 unique non-empty ids")
|
|
3329
|
+
if not user_id:
|
|
3330
|
+
raise ValueError("user_id is required")
|
|
3331
|
+
normalized_source = normalize_source(source)
|
|
3332
|
+
request_id = request_id or str(uuid.uuid4())
|
|
3333
|
+
replay = await self._claim_write_outcome(
|
|
3334
|
+
request_id=request_id,
|
|
3335
|
+
user_id=user_id,
|
|
3336
|
+
agent_id=agent_id,
|
|
3337
|
+
session_id=session_id,
|
|
3338
|
+
operation="process_raw",
|
|
3339
|
+
)
|
|
3340
|
+
if replay is not None:
|
|
3341
|
+
return replay
|
|
3342
|
+
pipeline_invoked = False
|
|
3343
|
+
try:
|
|
3344
|
+
nodes = []
|
|
3345
|
+
for raw_id in raw_ids:
|
|
3346
|
+
node = await self._vector_store.get_by_id(raw_id)
|
|
3347
|
+
if node is None or node.layer != MemoryLayer.L1_RAW:
|
|
3348
|
+
raise ValueError(f"raw record is unavailable: {raw_id}")
|
|
3349
|
+
if (
|
|
3350
|
+
node.user_id != user_id
|
|
3351
|
+
or node.agent_id != agent_id
|
|
3352
|
+
or node.session_id != session_id
|
|
3353
|
+
or node.source != normalized_source
|
|
3354
|
+
):
|
|
3355
|
+
raise ValueError("raw record is outside the requested write scope")
|
|
3356
|
+
nodes.append(node)
|
|
3357
|
+
source_orders = [(node.custom or {}).get("source_order") for node in nodes]
|
|
3358
|
+
numeric_orders = [value for value in source_orders if type(value) is int]
|
|
3359
|
+
if numeric_orders and (
|
|
3360
|
+
len(numeric_orders) != len(nodes)
|
|
3361
|
+
or numeric_orders != sorted(numeric_orders)
|
|
3362
|
+
or len(set(numeric_orders)) != len(numeric_orders)
|
|
3363
|
+
):
|
|
3364
|
+
raise ValueError("raw_ids are not in stable source order")
|
|
3365
|
+
roles = [str((node.custom or {}).get("role") or "user") for node in nodes]
|
|
3366
|
+
if any(role not in {"user", "assistant", "system", "tool"} for role in roles):
|
|
3367
|
+
raise ValueError("raw record has an unsupported message role")
|
|
3368
|
+
messages = [
|
|
3369
|
+
{
|
|
3370
|
+
"role": role,
|
|
3371
|
+
"content": node.content,
|
|
3372
|
+
**(
|
|
3373
|
+
{"name": str((node.custom or {})["name"])}
|
|
3374
|
+
if (node.custom or {}).get("name")
|
|
3375
|
+
else {}
|
|
3376
|
+
),
|
|
3377
|
+
**(
|
|
3378
|
+
{"tool_call_id": str((node.custom or {})["tool_call_id"])}
|
|
3379
|
+
if (node.custom or {}).get("tool_call_id")
|
|
3380
|
+
else {}
|
|
3381
|
+
),
|
|
3382
|
+
**(
|
|
3383
|
+
{"tool_name": str((node.custom or {})["tool_name"])}
|
|
3384
|
+
if (node.custom or {}).get("tool_name")
|
|
3385
|
+
else {}
|
|
3386
|
+
),
|
|
3387
|
+
}
|
|
3388
|
+
for node, role in zip(nodes, roles)
|
|
3389
|
+
]
|
|
3390
|
+
pipeline_invoked = True
|
|
3391
|
+
result = await self._async_add_impl(
|
|
3392
|
+
messages,
|
|
3393
|
+
user_id=user_id,
|
|
3394
|
+
agent_id=agent_id,
|
|
3395
|
+
session_id=session_id,
|
|
3396
|
+
source=normalized_source,
|
|
3397
|
+
_existing_raw_ids=list(raw_ids),
|
|
3398
|
+
memory_at=max(
|
|
3399
|
+
(node.memory_at for node in nodes if node.memory_at is not None),
|
|
3400
|
+
default=None,
|
|
3401
|
+
),
|
|
3402
|
+
scene=scene,
|
|
3403
|
+
workspace_id=workspace_id,
|
|
3404
|
+
branch=branch,
|
|
3405
|
+
request_id=request_id,
|
|
3406
|
+
)
|
|
3407
|
+
memory_ids = list(
|
|
3408
|
+
dict.fromkeys(
|
|
3409
|
+
result.get("memory_ids")
|
|
3410
|
+
or [
|
|
3411
|
+
*(result.get("experience_ids") or []),
|
|
3412
|
+
*(result.get("fact_ids") or []),
|
|
3413
|
+
]
|
|
3414
|
+
)
|
|
3415
|
+
)
|
|
3416
|
+
result = {
|
|
3417
|
+
**result,
|
|
3418
|
+
"raw_ids": list(raw_ids),
|
|
3419
|
+
"memory_ids": memory_ids,
|
|
3420
|
+
"request_id": request_id,
|
|
3421
|
+
}
|
|
3422
|
+
except BaseException:
|
|
3423
|
+
if pipeline_invoked:
|
|
3424
|
+
await self._mark_write_outcome_unknown(request_id)
|
|
3425
|
+
else:
|
|
3426
|
+
await self._finish_write_outcome(
|
|
3427
|
+
request_id,
|
|
3428
|
+
"failed",
|
|
3429
|
+
{
|
|
3430
|
+
"success": False,
|
|
3431
|
+
"request_id": request_id,
|
|
3432
|
+
"raw_ids": list(raw_ids),
|
|
3433
|
+
"memory_ids": [],
|
|
3434
|
+
"error_code": "process_raw_invalid",
|
|
3435
|
+
},
|
|
3436
|
+
)
|
|
3437
|
+
raise
|
|
3438
|
+
await self._finish_write_outcome(
|
|
3439
|
+
request_id,
|
|
3440
|
+
"succeeded" if result.get("success") is True else "failed",
|
|
3441
|
+
result,
|
|
3442
|
+
)
|
|
3443
|
+
return result
|
|
3444
|
+
|
|
3269
3445
|
@_ensure_internal_loop
|
|
3270
3446
|
async def async_import_memory_exact(
|
|
3271
3447
|
self,
|
|
@@ -3469,6 +3645,66 @@ class HyMemoryClient:
|
|
|
3469
3645
|
"offset": offset,
|
|
3470
3646
|
}
|
|
3471
3647
|
|
|
3648
|
+
async def _search_explicit_raw_layer(
|
|
3649
|
+
self,
|
|
3650
|
+
query: str,
|
|
3651
|
+
*,
|
|
3652
|
+
user_ids: List[str],
|
|
3653
|
+
agent_ids: List[str],
|
|
3654
|
+
session_ids: List[str],
|
|
3655
|
+
include_sources: List[str],
|
|
3656
|
+
exclude_sources: List[str],
|
|
3657
|
+
limit: int,
|
|
3658
|
+
min_score: float,
|
|
3659
|
+
created_after: Optional[float],
|
|
3660
|
+
request_id: str,
|
|
3661
|
+
t0: float,
|
|
3662
|
+
query_embedding: Optional[List[float]],
|
|
3663
|
+
) -> Dict[str, Any]:
|
|
3664
|
+
"""Implement `search(..., layers=['l1_raw'])` without a second API."""
|
|
3665
|
+
from .models.memory import MemoryLayer, MemoryStatus
|
|
3666
|
+
|
|
3667
|
+
embedding = query_embedding or await self._embed_service.embed(query)
|
|
3668
|
+
fetch = min(max(limit * (10 if session_ids else 2), limit), 10_000)
|
|
3669
|
+
hits = await self._vector_store.search(
|
|
3670
|
+
query_embedding=embedding,
|
|
3671
|
+
user_ids=user_ids,
|
|
3672
|
+
agent_ids=agent_ids,
|
|
3673
|
+
include_sources=include_sources,
|
|
3674
|
+
exclude_sources=exclude_sources,
|
|
3675
|
+
layers=[MemoryLayer.L1_RAW],
|
|
3676
|
+
limit=fetch,
|
|
3677
|
+
score_threshold=min_score,
|
|
3678
|
+
status_filter=[MemoryStatus.ACTIVE],
|
|
3679
|
+
only_latest=False,
|
|
3680
|
+
created_after=created_after,
|
|
3681
|
+
)
|
|
3682
|
+
sessions = set(session_ids)
|
|
3683
|
+
rows: List[Dict[str, Any]] = []
|
|
3684
|
+
for hit in hits:
|
|
3685
|
+
node = hit.get("node")
|
|
3686
|
+
if node is None or node.layer != MemoryLayer.L1_RAW:
|
|
3687
|
+
continue
|
|
3688
|
+
if sessions and node.session_id not in sessions:
|
|
3689
|
+
continue
|
|
3690
|
+
row = self._raw_node_to_public(node)
|
|
3691
|
+
rows.append(
|
|
3692
|
+
{
|
|
3693
|
+
"memory_id": row.pop("raw_id"),
|
|
3694
|
+
**row,
|
|
3695
|
+
"layer": MemoryLayer.L1_RAW.value,
|
|
3696
|
+
"status": MemoryStatus.ACTIVE.value,
|
|
3697
|
+
"score": round(float(hit.get("score", 0.0)), 4),
|
|
3698
|
+
}
|
|
3699
|
+
)
|
|
3700
|
+
if len(rows) >= limit:
|
|
3701
|
+
break
|
|
3702
|
+
return {
|
|
3703
|
+
"request_id": request_id,
|
|
3704
|
+
"memories": {"schema": [], "proactive": [], "normal": rows},
|
|
3705
|
+
"elapsed_ms": round((time.perf_counter() - t0) * 1000, 2),
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3472
3708
|
@staticmethod
|
|
3473
3709
|
def _public_search_views(
|
|
3474
3710
|
result_memories: List[Dict[str, Any]],
|
|
@@ -4129,6 +4365,22 @@ class HyMemoryClient:
|
|
|
4129
4365
|
effective_search_layers = [MemoryLayer.L1_RAW] if lite_mode else layers
|
|
4130
4366
|
effective_schema_limit = 0 if lite_mode else schema_limit
|
|
4131
4367
|
effective_intention_limit = 0 if lite_mode else intention_limit
|
|
4368
|
+
normalized_search_layers = self._normalize_layers(effective_search_layers)
|
|
4369
|
+
if normalized_search_layers == [MemoryLayer.L1_RAW]:
|
|
4370
|
+
return await self._search_explicit_raw_layer(
|
|
4371
|
+
effective_target_query,
|
|
4372
|
+
user_ids=effective_user_ids,
|
|
4373
|
+
agent_ids=effective_agent_ids,
|
|
4374
|
+
session_ids=effective_session_ids,
|
|
4375
|
+
include_sources=effective_include_sources,
|
|
4376
|
+
exclude_sources=effective_exclude_sources,
|
|
4377
|
+
limit=limit,
|
|
4378
|
+
min_score=min_score,
|
|
4379
|
+
created_after=effective_created_after,
|
|
4380
|
+
request_id=request_id,
|
|
4381
|
+
t0=t0,
|
|
4382
|
+
query_embedding=query_embedding,
|
|
4383
|
+
)
|
|
4132
4384
|
|
|
4133
4385
|
read_req = ReadRequest(
|
|
4134
4386
|
query=effective_target_query,
|
|
@@ -4149,7 +4401,7 @@ class HyMemoryClient:
|
|
|
4149
4401
|
layers=(
|
|
4150
4402
|
[
|
|
4151
4403
|
layer.value
|
|
4152
|
-
for layer in
|
|
4404
|
+
for layer in normalized_search_layers
|
|
4153
4405
|
]
|
|
4154
4406
|
if effective_search_layers else None
|
|
4155
4407
|
),
|
|
@@ -4174,7 +4426,7 @@ class HyMemoryClient:
|
|
|
4174
4426
|
_wanted_layers = (
|
|
4175
4427
|
{
|
|
4176
4428
|
layer.value
|
|
4177
|
-
for layer in
|
|
4429
|
+
for layer in normalized_search_layers
|
|
4178
4430
|
}
|
|
4179
4431
|
if effective_search_layers else None
|
|
4180
4432
|
)
|
hy_memory/pipelines/base.py
CHANGED
|
@@ -231,6 +231,10 @@ class WriteRequest:
|
|
|
231
231
|
# 已有记忆 (V1 合并检测用)
|
|
232
232
|
existing_memories: Optional[List[Dict[str, Any]]] = None
|
|
233
233
|
|
|
234
|
+
# Public client process_raw() only: reuse these exact L1 nodes as input.
|
|
235
|
+
# Ordinary add() never populates this field.
|
|
236
|
+
existing_raw_ids: List[str] = field(default_factory=list)
|
|
237
|
+
|
|
234
238
|
# 记忆时间戳(不传则用当前时间 datetime.now())
|
|
235
239
|
memory_at: Optional[datetime] = None
|
|
236
240
|
|
hy_memory/pipelines/writer.py
CHANGED
|
@@ -280,6 +280,27 @@ class MemoryWriter(WritePipeline):
|
|
|
280
280
|
custom["source_raw_memory_ids"] = source_ids
|
|
281
281
|
return custom
|
|
282
282
|
|
|
283
|
+
async def _attach_existing_raw_ids(
|
|
284
|
+
self,
|
|
285
|
+
vector_store,
|
|
286
|
+
memory_ids: List[str],
|
|
287
|
+
raw_ids: List[str],
|
|
288
|
+
) -> None:
|
|
289
|
+
"""Attach every reused raw record to canonical outputs."""
|
|
290
|
+
for memory_id in memory_ids:
|
|
291
|
+
node = await vector_store.get_by_id(memory_id)
|
|
292
|
+
if node is None:
|
|
293
|
+
continue
|
|
294
|
+
custom = dict(node.custom or {})
|
|
295
|
+
source_ids = list(custom.get("source_raw_memory_ids") or [])
|
|
296
|
+
legacy_source = str(getattr(node, "source_raw_memory_id", "") or "")
|
|
297
|
+
for raw_id in (legacy_source, *raw_ids):
|
|
298
|
+
if raw_id and raw_id not in source_ids:
|
|
299
|
+
source_ids.append(raw_id)
|
|
300
|
+
if source_ids:
|
|
301
|
+
custom["source_raw_memory_ids"] = source_ids
|
|
302
|
+
await vector_store.update_payload(memory_id, {"custom": custom})
|
|
303
|
+
|
|
283
304
|
@staticmethod
|
|
284
305
|
def _attach_client_meta(custom: Dict[str, Any], request: Optional[WriteRequest]) -> None:
|
|
285
306
|
"""给新建节点挂上调用方 meta(单份 list)。
|
|
@@ -2596,13 +2617,23 @@ class MemoryWriter(WritePipeline):
|
|
|
2596
2617
|
from ..metrics import MetricsCollector
|
|
2597
2618
|
MetricsCollector.get().sys1_start()
|
|
2598
2619
|
|
|
2599
|
-
|
|
2620
|
+
existing_raw_ids = request.existing_raw_ids
|
|
2621
|
+
if (
|
|
2622
|
+
not isinstance(existing_raw_ids, list)
|
|
2623
|
+
or any(not isinstance(raw_id, str) or not raw_id for raw_id in existing_raw_ids)
|
|
2624
|
+
):
|
|
2625
|
+
raise ValueError("_existing_raw_ids must be a list of non-empty ids")
|
|
2626
|
+
|
|
2627
|
+
# 2. 向量化(处理已有 raw 时复用已存节点,不再次 embed)
|
|
2600
2628
|
_t_embed = datetime.now()
|
|
2601
|
-
|
|
2602
|
-
embedding, embed_meta =
|
|
2603
|
-
|
|
2604
|
-
)
|
|
2605
|
-
|
|
2629
|
+
if existing_raw_ids:
|
|
2630
|
+
embedding, embed_meta = [], {"reused_raw": True}
|
|
2631
|
+
else:
|
|
2632
|
+
with tracer.span("embed") as s:
|
|
2633
|
+
embedding, embed_meta = await self.embed_service.embed_queued_with_meta(
|
|
2634
|
+
request.content
|
|
2635
|
+
)
|
|
2636
|
+
s.set_output({"dims": len(embedding), "content_len": len(request.content)})
|
|
2606
2637
|
_embed_ms = (datetime.now() - _t_embed).total_seconds() * 1000
|
|
2607
2638
|
response.extra["embedding"] = embedding
|
|
2608
2639
|
await self._emit_pipeline_step(
|
|
@@ -2625,27 +2656,43 @@ class MemoryWriter(WritePipeline):
|
|
|
2625
2656
|
with tracer.span("qdrant_upsert") as s:
|
|
2626
2657
|
try:
|
|
2627
2658
|
vector_store = await self._get_vector_store()
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2659
|
+
if existing_raw_ids:
|
|
2660
|
+
memory_id = existing_raw_ids[-1]
|
|
2661
|
+
mem_node = await vector_store.get_by_id(memory_id)
|
|
2662
|
+
if (
|
|
2663
|
+
mem_node is None
|
|
2664
|
+
or mem_node.layer != MemoryLayer.L1_RAW
|
|
2665
|
+
or mem_node.user_id != request.user_id
|
|
2666
|
+
or mem_node.agent_id != (request.agent_id or "default_agent")
|
|
2667
|
+
or mem_node.session_id != (request.session_id or "default_session")
|
|
2668
|
+
or mem_node.source != request.source
|
|
2669
|
+
):
|
|
2670
|
+
raise ValueError("existing raw record is outside the write scope")
|
|
2671
|
+
else:
|
|
2672
|
+
layer_enum = MemoryLayer.from_string(suggested_layer)
|
|
2673
|
+
_raw_custom: Dict[str, Any] = {}
|
|
2674
|
+
self._attach_client_meta(_raw_custom, request)
|
|
2675
|
+
mem_node = MemoryNode(
|
|
2676
|
+
user_id=request.user_id,
|
|
2677
|
+
agent_id=request.agent_id or "default_agent",
|
|
2678
|
+
session_id=request.session_id or "default_session",
|
|
2679
|
+
source=request.source,
|
|
2680
|
+
layer=layer_enum,
|
|
2681
|
+
content=request.content,
|
|
2682
|
+
source_type=SourceType.EXPLICIT,
|
|
2683
|
+
status=MemoryStatus.ACTIVE,
|
|
2684
|
+
is_latest=True,
|
|
2685
|
+
embedding=embedding,
|
|
2686
|
+
memory_at=request.memory_at,
|
|
2687
|
+
custom=_raw_custom,
|
|
2688
|
+
)
|
|
2689
|
+
memory_id = await vector_store.upsert(mem_node)
|
|
2646
2690
|
response.memory_id = memory_id
|
|
2647
2691
|
s.set_output({"memory_id": memory_id, "layer": suggested_layer})
|
|
2648
|
-
logger.debug(
|
|
2692
|
+
logger.debug(
|
|
2693
|
+
f"[write] {'reused' if existing_raw_ids else 'persisted'}: "
|
|
2694
|
+
f"id={memory_id} layer={suggested_layer}"
|
|
2695
|
+
)
|
|
2649
2696
|
except Exception as persist_err:
|
|
2650
2697
|
_l1_error = str(persist_err)
|
|
2651
2698
|
s.set_error(_l1_error)
|
|
@@ -2709,6 +2756,12 @@ class MemoryWriter(WritePipeline):
|
|
|
2709
2756
|
recent_history_count=_recent_history_count,
|
|
2710
2757
|
)
|
|
2711
2758
|
total_tokens += response.extra.get("_agent_tokens", 0)
|
|
2759
|
+
if existing_raw_ids and response.extra.get("agent_stored_ids"):
|
|
2760
|
+
await self._attach_existing_raw_ids(
|
|
2761
|
+
vector_store,
|
|
2762
|
+
list(response.extra["agent_stored_ids"]),
|
|
2763
|
+
existing_raw_ids,
|
|
2764
|
+
)
|
|
2712
2765
|
if response.extra.get("agent_status") == "failed":
|
|
2713
2766
|
await self._emit_pipeline_step(
|
|
2714
2767
|
request,
|
|
@@ -2718,12 +2771,13 @@ class MemoryWriter(WritePipeline):
|
|
|
2718
2771
|
"error": response.extra.get("agent_error", ""),
|
|
2719
2772
|
},
|
|
2720
2773
|
)
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2774
|
+
if not existing_raw_ids:
|
|
2775
|
+
await self._rollback_failed_l1(
|
|
2776
|
+
request=request,
|
|
2777
|
+
response=response,
|
|
2778
|
+
vector_store=vector_store,
|
|
2779
|
+
memory_id=memory_id,
|
|
2780
|
+
)
|
|
2727
2781
|
response.tokens_used = total_tokens
|
|
2728
2782
|
_failed_workflow_ms = (
|
|
2729
2783
|
datetime.now() - _t_workflow
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
hy_memory/__init__.py,sha256=idVBktG5xzToAXl6WDsAmZbBKX3_Q5D5iUtkKcdfmm0,4031
|
|
2
|
-
hy_memory/client.py,sha256=
|
|
2
|
+
hy_memory/client.py,sha256=bfSMfaWG8oi8NYshkGiThqq5-qxYwK18ERmX2Bgl2Bw,311202
|
|
3
3
|
hy_memory/config.py,sha256=J051sHyMnTysxWajFORNnGhy6SRKq8n6ZVXSS44lhGA,67809
|
|
4
4
|
hy_memory/inspector.py,sha256=Uj-mrxi_Piluq4DGVHCHzMfA1QSJLEOMBpiotDjqJSs,18380
|
|
5
5
|
hy_memory/metrics.py,sha256=sGhhV_aDEuws7ks4c-pI_z5o2rayKkJcNC6J1b9sPQY,16648
|
|
@@ -60,7 +60,7 @@ hy_memory/models/memory.py,sha256=5x62z7QiCoFd7-Km9Gp_GzhBsvgGLeRA-Aa7LOu867I,47
|
|
|
60
60
|
hy_memory/models/requests.py,sha256=jtSZ6GyaUXMf9RnHIDjX5T1D-w03ufkff1y0qIC4So8,20396
|
|
61
61
|
hy_memory/pipelines/__init__.py,sha256=XcLhI93wqLYqYkHPbmE1C5RW8f8X0b2TBGY68ozd_3s,1604
|
|
62
62
|
hy_memory/pipelines/balanced_catalog.py,sha256=Ww1Tf-waupn5PweWvZi8IqavS4W2FMvoJBJ85Wimn2M,39677
|
|
63
|
-
hy_memory/pipelines/base.py,sha256=
|
|
63
|
+
hy_memory/pipelines/base.py,sha256=HReILG4-DEfEx71d6YTokKMpXfxVKi71kVBDcRCTH10,18745
|
|
64
64
|
hy_memory/pipelines/cross_domain_sweeper.py,sha256=wL5PXuas38mO5G43WDhpQEUs82akc9M9YnJhwvsxEmg,29527
|
|
65
65
|
hy_memory/pipelines/reader.py,sha256=xYxdInMPH9Jb7Z9ZIUo0sNclrrpMOeHfeZVpDYKZYiY,3523
|
|
66
66
|
hy_memory/pipelines/reader_base.py,sha256=up9Z9hIw9cV3Z7x39cZtdge8ULmzZXI7WEHGCrEWA40,177951
|
|
@@ -72,7 +72,7 @@ hy_memory/pipelines/system2_agent.py,sha256=oV6wZ49SgDZwgtbNQ8cGcE3X4H7cjT8_MN2c
|
|
|
72
72
|
hy_memory/pipelines/system2_tools.py,sha256=mt-IY9Tbe6zUo5WGQR6uSjuUnF6s1nlrIUmEayvGhCg,42575
|
|
73
73
|
hy_memory/pipelines/system2_writer.py,sha256=qKc5wP5LfH-o1P0BtDKuKjGBU52nakm1RUKHVR9Ot-I,54224
|
|
74
74
|
hy_memory/pipelines/user_profile_scope.py,sha256=lSCYyocAE7kDOS5775s1YfCCkhUMZug32nChCuIspdg,635
|
|
75
|
-
hy_memory/pipelines/writer.py,sha256=
|
|
75
|
+
hy_memory/pipelines/writer.py,sha256=azd0m7v4EQefWiSBtiHyWs2RaZ_eqn8Uuk-olXLbM8g,177553
|
|
76
76
|
hy_memory/pipelines/_graph/__init__.py,sha256=R9RRj8veFgg7n6bl5IpvFp1rlLX4QtPSsyhw0sGJWFc,1441
|
|
77
77
|
hy_memory/pipelines/_graph/projector.py,sha256=WE-pFT_FT2jgO5hSpRPesW0Fh9ATvg-tvSIpYqBDAaA,27667
|
|
78
78
|
hy_memory/pipelines/_graph/rerank.py,sha256=IW0DS9v4y99lwGKq6rz_ns9wo5bq1INVM63mlYhBkHU,40583
|
|
@@ -128,8 +128,8 @@ hy_memory/utils/time_window.py,sha256=MGAAWLyxHHwLrNthmYDdnH5PA-pQP9ZdcoQUfpzJof
|
|
|
128
128
|
hy_memory/utils/token_count.py,sha256=WibfD9KNuERMrcEPNFZTNo0JCpE72XPaUSlD48Wz1mk,2388
|
|
129
129
|
hy_memory/utils/trace_file_writer.py,sha256=bS_Kbzzy49eLr5G7HwBKLZqU9j3P6vGjtBWUWa8MIiY,217
|
|
130
130
|
hy_memory/utils/tracer.py,sha256=dVAwue7zwMax7KQFOfXpD-4pdfrVfiFFxbLhTrqu5XU,12595
|
|
131
|
-
hy_memory-2.0.5.
|
|
132
|
-
hy_memory-2.0.5.
|
|
133
|
-
hy_memory-2.0.5.
|
|
134
|
-
hy_memory-2.0.5.
|
|
135
|
-
hy_memory-2.0.5.
|
|
131
|
+
hy_memory-2.0.5.dev4.dist-info/licenses/LICENSE,sha256=ywv3qL-z2K0y7z9CI4IMqXf6eLeIKEWewwrzNfsrYN0,9650
|
|
132
|
+
hy_memory-2.0.5.dev4.dist-info/METADATA,sha256=qbFy82UX7latN-6mVTk-2sbG7n_dQLd35aLQdZQ4A4s,18340
|
|
133
|
+
hy_memory-2.0.5.dev4.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
134
|
+
hy_memory-2.0.5.dev4.dist-info/top_level.txt,sha256=YipheDT9yI2XTaR3lQcsXb3P9MOkd3uN2JHGtlUbnFU,10
|
|
135
|
+
hy_memory-2.0.5.dev4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|