hy-memory 2.0.7__tar.gz → 2.0.8__tar.gz

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.
Files changed (143) hide show
  1. {hy_memory-2.0.7/hy_memory.egg-info → hy_memory-2.0.8}/PKG-INFO +1 -1
  2. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/extractor.py +4 -3
  3. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/llm_provider.py +1 -2
  4. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/mem_agent.py +1 -1
  5. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/reconciler.py +11 -9
  6. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/tools/base.py +3 -1
  7. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/client.py +19 -13
  8. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/extractor.py +4 -1
  9. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/fact_reconciler.py +15 -6
  10. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/judge.py +21 -10
  11. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/reconciler.py +21 -10
  12. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/store.py +7 -2
  13. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/embed_service.py +15 -4
  14. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/reconcile_retrieval.py +6 -1
  15. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/cross_domain_sweeper.py +2 -1
  16. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/reader_base.py +2 -1
  17. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/system2_agent.py +2 -1
  18. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/async_executor.py +4 -1
  19. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/pipeline_log_writer.py +2 -1
  20. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/pipeline_observability.py +71 -30
  21. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/token_count.py +2 -1
  22. {hy_memory-2.0.7 → hy_memory-2.0.8/hy_memory.egg-info}/PKG-INFO +1 -1
  23. {hy_memory-2.0.7 → hy_memory-2.0.8}/pyproject.toml +1 -1
  24. {hy_memory-2.0.7 → hy_memory-2.0.8}/LICENSE +0 -0
  25. {hy_memory-2.0.7 → hy_memory-2.0.8}/MANIFEST.in +0 -0
  26. {hy_memory-2.0.7 → hy_memory-2.0.8}/README.md +0 -0
  27. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/__init__.py +0 -0
  28. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/__init__.py +0 -0
  29. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/abstractor.py +0 -0
  30. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/emotion_analyzer.py +0 -0
  31. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/episode_summary.py +0 -0
  32. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/intention_detector.py +0 -0
  33. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/prompts_zh.py +0 -0
  34. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/reflector.py +0 -0
  35. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/tools/__init__.py +0 -0
  36. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/tools/basic_profile.py +0 -0
  37. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/__init__.py +0 -0
  38. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/chain_store.py +0 -0
  39. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/lexical.py +0 -0
  40. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/preproc.py +0 -0
  41. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/prompts_zh.py +0 -0
  42. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/types.py +0 -0
  43. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/writer.py +0 -0
  44. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/config.py +0 -0
  45. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/__init__.py +0 -0
  46. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/merger.py +0 -0
  47. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/scorer.py +0 -0
  48. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/workload.py +0 -0
  49. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/__init__.py +0 -0
  50. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/cache.py +0 -0
  51. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/cache_base.py +0 -0
  52. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/cache_mysql.py +0 -0
  53. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/cache_sqlite.py +0 -0
  54. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/graph_store.py +0 -0
  55. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/graph_store_base.py +0 -0
  56. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/graph_store_kuzu.py +0 -0
  57. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/graph_store_neo4j.py +0 -0
  58. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/history_store.py +0 -0
  59. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/kv_store.py +0 -0
  60. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/rdb.py +0 -0
  61. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store.py +0 -0
  62. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store_base.py +0 -0
  63. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store_chroma.py +0 -0
  64. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store_faiss.py +0 -0
  65. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store_qdrant.py +0 -0
  66. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/embedding.py +0 -0
  67. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/inspector.py +0 -0
  68. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/metrics.py +0 -0
  69. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/models/__init__.py +0 -0
  70. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/models/memory.py +0 -0
  71. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/models/requests.py +0 -0
  72. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/__init__.py +0 -0
  73. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_graph/__init__.py +0 -0
  74. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_graph/projector.py +0 -0
  75. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_graph/rerank.py +0 -0
  76. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_graph/topic_summary.py +0 -0
  77. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/__init__.py +0 -0
  78. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/bm25.py +0 -0
  79. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/bm25_fastembed.py +0 -0
  80. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/candidate_guards.py +0 -0
  81. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/config.py +0 -0
  82. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/date_view.py +0 -0
  83. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/dedup.py +0 -0
  84. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/diversity.py +0 -0
  85. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/entities.py +0 -0
  86. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/entity_store.py +0 -0
  87. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/evolution.py +0 -0
  88. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/hierarchy_bundle.py +0 -0
  89. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/intent.py +0 -0
  90. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/intention.py +0 -0
  91. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/lemmatize.py +0 -0
  92. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/local_rerank.py +0 -0
  93. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/memory_format.py +0 -0
  94. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/multi_seed_bundle.py +0 -0
  95. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/ner.py +0 -0
  96. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/profile_evidence.py +0 -0
  97. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/profile_owner_gate.py +0 -0
  98. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/query_expand.py +0 -0
  99. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/record_date_view.py +0 -0
  100. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/retrieval_clauses.py +0 -0
  101. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/rrf.py +0 -0
  102. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/scoring.py +0 -0
  103. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/series_tail_guard.py +0 -0
  104. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/session_episode.py +0 -0
  105. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/strength.py +0 -0
  106. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/supplemental_propositions.py +0 -0
  107. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/tag_index.py +0 -0
  108. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/temporal.py +0 -0
  109. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/topic_candidates.py +0 -0
  110. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/trace.py +0 -0
  111. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/underflow.py +0 -0
  112. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/user_level_l0.py +0 -0
  113. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/balanced_catalog.py +0 -0
  114. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/base.py +0 -0
  115. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/reader.py +0 -0
  116. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/reader_hybrid.py +0 -0
  117. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/reader_legacy.py +0 -0
  118. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/registry.py +0 -0
  119. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/specific_supplement.py +0 -0
  120. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/system2_tools.py +0 -0
  121. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/system2_writer.py +0 -0
  122. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/user_profile_scope.py +0 -0
  123. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/writer.py +0 -0
  124. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/retrieval.py +0 -0
  125. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/runtime.py +0 -0
  126. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/server.py +0 -0
  127. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/snapshot.py +0 -0
  128. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/structure.py +0 -0
  129. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/clone_references.py +0 -0
  130. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/keyword_ties.py +0 -0
  131. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/lang_detect.py +0 -0
  132. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/log_setup.py +0 -0
  133. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/metrics.py +0 -0
  134. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/model_call_context.py +0 -0
  135. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/source_filter.py +0 -0
  136. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/time_window.py +0 -0
  137. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/trace_file_writer.py +0 -0
  138. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/tracer.py +0 -0
  139. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory.egg-info/SOURCES.txt +0 -0
  140. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory.egg-info/dependency_links.txt +0 -0
  141. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory.egg-info/requires.txt +0 -0
  142. {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory.egg-info/top_level.txt +0 -0
  143. {hy_memory-2.0.7 → hy_memory-2.0.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hy-memory
3
- Version: 2.0.7
3
+ Version: 2.0.8
4
4
  Summary: HY Memory - Industrial-grade dual-system cognitive memory framework
5
5
  Author: alvinfei
6
6
  License-Expression: Apache-2.0
@@ -1177,7 +1177,7 @@ class Extractor:
1177
1177
  if not tc_id:
1178
1178
  logger.error(
1179
1179
  f"[extractor] tool_call_id is empty! "
1180
- f"raw tc: {json.dumps(tc_raw, ensure_ascii=False, default=str)[:500]}"
1180
+ f"raw_keys={list(tc_raw.keys())}"
1181
1181
  )
1182
1182
  try:
1183
1183
  t_res = await tool_registry.dispatch(
@@ -1311,11 +1311,12 @@ class Extractor:
1311
1311
  if parsed is None:
1312
1312
  if not all_tool_calls:
1313
1313
  logger.warning(
1314
- f"[extractor] JSON parse failed: {raw_text[:200]}"
1314
+ f"[extractor] JSON parse failed ({len(raw_text)} chars)"
1315
1315
  )
1316
+ logger.debug(f"[extractor] unparsable output: {raw_text[:200]!r}")
1316
1317
  return ExtractResult(
1317
1318
  success=False,
1318
- error=f"JSON parse failed: {raw_text[:200]}",
1319
+ error=f"JSON parse failed ({len(raw_text)} chars)",
1319
1320
  error_code="JSON_PARSE_FAILED",
1320
1321
  raw_response=raw_text,
1321
1322
  tokens_used=total_tokens,
@@ -129,8 +129,7 @@ def _parse_tool_calls_list(raw_calls: list, _json) -> List[Dict[str, Any]]:
129
129
  tc_id = tc.get("id") or ""
130
130
  if not tc_id:
131
131
  logger.error(
132
- f"[llm] tool_call missing 'id'! raw: "
133
- f"{_json.dumps(tc, ensure_ascii=False, default=str)[:500]}"
132
+ f"[llm] tool_call missing 'id'! keys={list(tc.keys())}"
134
133
  )
135
134
 
136
135
  # 保留原始 tc 完整结构,只规范化 arguments
@@ -234,7 +234,7 @@ class MemAgent:
234
234
  f"identity={len(identity)} facts={len(facts)} "
235
235
  f"basic_info={basic_field_count}fields"
236
236
  )
237
- logger.info(f"MemAgent: extracted_info={extract_result.info}")
237
+ logger.debug(f"MemAgent: extracted_info={extract_result.info}")
238
238
 
239
239
  # 3. 冲突检测(如果有已有记忆)
240
240
  if mode == ProcessMode.FULL and existing_memories:
@@ -50,7 +50,7 @@ def is_canonical_state_node(node: Any) -> bool:
50
50
 
51
51
 
52
52
  def _log_ops_detail(ops: List["ReconcileOp"]) -> None:
53
- """以单条 INFO(JSON list)输出 reconcile 的逐条结果,便于线上 debug。"""
53
+ """以单条 DEBUG(JSON list)输出 reconcile 的逐条结果;含记忆正文,不进 INFO。"""
54
54
  if not ops:
55
55
  return
56
56
  view = [
@@ -68,7 +68,7 @@ def _log_ops_detail(ops: List["ReconcileOp"]) -> None:
68
68
  }
69
69
  for o in ops
70
70
  ]
71
- logger.info(
71
+ logger.debug(
72
72
  f"[reconciler] ops detail: {json.dumps(view, ensure_ascii=False, default=str)}"
73
73
  )
74
74
 
@@ -1514,9 +1514,10 @@ class MemoryReconciler:
1514
1514
  logger.warning(
1515
1515
  f"[reconciler] RECONCILE JSON parse failed "
1516
1516
  f"(attempt {_attempt+1}/{_MAX_PARSE_ATTEMPTS}), "
1517
- f"retrying: raw="
1518
- f"{raw_response[:200] if _log_llm_content_enabled() else '<redacted>'}"
1517
+ f"retrying: raw({len(raw_response or '')} chars)"
1519
1518
  )
1519
+ if _log_llm_content_enabled():
1520
+ logger.debug(f"[reconciler] unparsable raw={raw_response[:200]!r}")
1520
1521
 
1521
1522
  recon_elapsed = (_time.perf_counter() - t_recon) * 1000
1522
1523
 
@@ -1528,8 +1529,8 @@ class MemoryReconciler:
1528
1529
  error_code = "LLM_UNAVAILABLE"
1529
1530
  elif raw_response.strip():
1530
1531
  error_msg = (
1531
- f"RECONCILE JSON parse failed after {_n_attempts} attempts: "
1532
- f"{raw_response[:200] if _log_llm_content_enabled() else '<redacted>'}"
1532
+ f"RECONCILE JSON parse failed after {_n_attempts} attempts "
1533
+ f"({len(raw_response)} chars)"
1533
1534
  )
1534
1535
  error_code = "JSON_PARSE_FAILED"
1535
1536
  else:
@@ -1806,7 +1807,8 @@ class MemoryReconciler:
1806
1807
  except Exception as _re:
1807
1808
  logger.debug(f"[reconciler] json_repair failed: {_re}")
1808
1809
  if data is None:
1809
- logger.warning(f"[reconciler] failed to parse ops JSON: {text[:200]}")
1810
+ logger.warning(f"[reconciler] failed to parse ops JSON ({len(text)} chars)")
1811
+ logger.debug(f"[reconciler] unparsable ops output: {text[:200]!r}")
1810
1812
  return []
1811
1813
 
1812
1814
  if not isinstance(data, list):
@@ -2018,11 +2020,11 @@ class MemoryReconciler:
2018
2020
  if clash is not None:
2019
2021
  logger.warning(
2020
2022
  f"[reconciler] memory_id {clash} already touched by '{touched[clash]}', "
2021
- f"current op '{op.op} ({op.reason[:60]})' dropped"
2023
+ f"current op '{op.op}' dropped"
2022
2024
  )
2023
2025
  continue
2024
2026
  for t in target_ids:
2025
- touched[t] = f"{op.op}({op.reason[:40]})"
2027
+ touched[t] = op.op
2026
2028
  validated.append(op)
2027
2029
 
2028
2030
  return validated
@@ -211,7 +211,9 @@ def parse_tool_calls_from_json(raw: Any) -> List[ToolCall]:
211
211
  try:
212
212
  args = json.loads(args_raw) if args_raw.strip() else {}
213
213
  except json.JSONDecodeError:
214
- logger.warning(f"[tools] failed to parse arguments as JSON: {args_raw[:120]}")
214
+ logger.warning(
215
+ f"[tools] failed to parse arguments as JSON ({len(args_raw)} chars)"
216
+ )
215
217
  args = {}
216
218
  elif isinstance(args_raw, dict):
217
219
  args = args_raw
@@ -744,7 +744,7 @@ class HyMemoryClient:
744
744
  def _install_pipeline_observability_hooks(self) -> None:
745
745
  """
746
746
  包装 cache.store_pipeline_log:
747
- - Log:JSONL 文件,始终写入(环节级,不依赖 Trace 开关)
747
+ - Log:JSONL 文件,由 MEMORY_PIPELINE_LOG_ENABLED 控制(默认开启)
748
748
  - Trace:SQLite pipeline_logs,由 MEMORY_PIPELINE_TRACE_ENABLED 控制(Inspector)
749
749
  - 落盘 fire-and-forget,不阻塞 search/write 热路径
750
750
  """
@@ -958,7 +958,7 @@ class HyMemoryClient:
958
958
  parsed_tool_calls = json.loads(raw_tool_calls) if raw_tool_calls else []
959
959
  except Exception:
960
960
  logger.warning(
961
- "tool_calls 是非法 JSON 字符串,已忽略: %r", raw_tool_calls[:200]
961
+ "tool_calls 是非法 JSON 字符串,已忽略 (%d chars)", len(raw_tool_calls)
962
962
  )
963
963
  parsed_tool_calls = []
964
964
  raw_tool_calls = parsed_tool_calls if isinstance(parsed_tool_calls, list) else []
@@ -3008,13 +3008,13 @@ class HyMemoryClient:
3008
3008
  f"[add] writing {preview} user={uid} "
3009
3009
  f"agent_id={agent_id} session_id={session_id} mode={self._mode}"
3010
3010
  )
3011
- logger.info(f"[add] messages detail: {[m.to_dict() for m in chat_messages]}")
3011
+ logger.debug(f"[add] messages detail: {[m.to_dict() for m in chat_messages]}")
3012
3012
  else:
3013
3013
  logger.info(
3014
3014
  f"[add] writing text({len(content)} chars) user={uid} "
3015
- f"agent_id={agent_id} session_id={session_id} mode={self._mode} "
3016
- f"content={content}"
3015
+ f"agent_id={agent_id} session_id={session_id} mode={self._mode}"
3017
3016
  )
3017
+ logger.debug(f"[add] text content={content!r}")
3018
3018
 
3019
3019
  # mode → agent_mode 映射
3020
3020
  # lite: 不调 LLM agent
@@ -4034,14 +4034,14 @@ class HyMemoryClient:
4034
4034
  decided_variant = HyMemoryClient._scene_variant(scene)
4035
4035
  logger.info(
4036
4036
  f"[search] scene explicit={decided_scene} (no LLM judge); "
4037
- f"target={effective_target_query!r}"
4037
+ f"target({len(effective_target_query or '')} chars)"
4038
4038
  )
4039
4039
  elif not agentic_enabled and not queries_supplied:
4040
4040
  # 默认 light search 仅指 query="...":固定 normal,绝不调用 LLM。
4041
4041
  decided_scene = "normal"
4042
4042
  logger.info(
4043
4043
  f"[search] search_mode=default single-query scene=normal (no LLM); "
4044
- f"target={target_query!r}"
4044
+ f"target({len(target_query or '')} chars)"
4045
4045
  )
4046
4046
  elif agentic_enabled:
4047
4047
  # agentic:单/多 query 都强制一次 LLM 完成三分类 + rewrite + 时间解析。
@@ -4076,14 +4076,18 @@ class HyMemoryClient:
4076
4076
  rewrite_created_after = ar.get("created_after")
4077
4077
  logger.info(
4078
4078
  f"[search] agentic scene={decided_scene} via rewrite-LLM "
4079
- f"(classify={want_classify}); orig={target_query!r} "
4080
- f"effective={effective_target_query!r} rewrite_used={rewrite_used} "
4079
+ f"(classify={want_classify}); rewrite_used={rewrite_used} "
4081
4080
  f"created_after={rewrite_created_after}"
4082
4081
  )
4082
+ logger.debug(
4083
+ f"[search] agentic rewrite: orig={target_query!r} "
4084
+ f"effective={effective_target_query!r}"
4085
+ )
4083
4086
  else:
4084
4087
  decided_scene = "normal"
4085
4088
  logger.info(
4086
- f"[search] agentic rewrite-LLM fallback=normal; target={target_query!r}"
4089
+ f"[search] agentic rewrite-LLM fallback=normal; "
4090
+ f"target({len(target_query or '')} chars)"
4087
4091
  )
4088
4092
  elif rewrite_enabled and not _too_long:
4089
4093
  # default + queries= 的原逻辑:配置开启时一次 LLM 做 rewrite/time,
@@ -4162,7 +4166,7 @@ class HyMemoryClient:
4162
4166
  decided_scene = "normal"
4163
4167
  logger.info(
4164
4168
  f"[search] default multi-query features disabled; no LLM; "
4165
- f"target={target_query!r}"
4169
+ f"target({len(target_query or '')} chars)"
4166
4170
  )
4167
4171
 
4168
4172
  # ────────────────────────────────────────────────────────────
@@ -4215,7 +4219,7 @@ class HyMemoryClient:
4215
4219
  else:
4216
4220
  logger.info(
4217
4221
  f"[search] routing to PRODUCTIVITY path variant={decided_variant}: "
4218
- f"target={effective_target_query!r} "
4222
+ f"target({len(effective_target_query or '')} chars) "
4219
4223
  f"workspace_id={workspace_id!r} branch={branch!r}"
4220
4224
  )
4221
4225
  return await self._search_coding(
@@ -4243,9 +4247,11 @@ class HyMemoryClient:
4243
4247
  # Chat 召回链路(原有逻辑)
4244
4248
  # ────────────────────────────────────────────────────────────
4245
4249
  logger.info(
4246
- f"[search] query='{effective_target_query}' user_ids={effective_user_ids} "
4250
+ f"[search] query({len(effective_target_query or '')} chars) "
4251
+ f"user_ids={effective_user_ids} "
4247
4252
  f"agent_ids={effective_agent_ids} session_ids={effective_session_ids} limit={limit}"
4248
4253
  )
4254
+ logger.debug(f"[search] query={effective_target_query!r}")
4249
4255
 
4250
4256
  # rewrite 抽出的时间下界与用户显式 created_after 取更晚者(更严格)
4251
4257
  effective_created_after = created_after
@@ -1166,7 +1166,10 @@ class CodingMemoryExtractor:
1166
1166
  obj = None
1167
1167
 
1168
1168
  if not isinstance(obj, dict):
1169
- logger.warning(f"[coding-extract] failed to parse output: {text!r}")
1169
+ logger.warning(
1170
+ f"[coding-extract] failed to parse output ({len(text or '')} chars)"
1171
+ )
1172
+ logger.debug(f"[coding-extract] unparsable output: {text!r}")
1170
1173
  return [], []
1171
1174
 
1172
1175
  # "memories" 是双层化之前的 key,老 prompt 缓存 / 旧模型可能还这么回
@@ -570,9 +570,12 @@ class WorkingFactReconciler:
570
570
  kept.append(fact)
571
571
  if blocked:
572
572
  logger.info(
573
- f"[fact-reconcile] subject gate blocked {len(blocked)} candidate(s) "
574
- f"for subject={draft.subject!r}: "
575
- + ", ".join(f"{f.fact_id}({f.subject!r})" for f in blocked)
573
+ f"[fact-reconcile] subject gate blocked {len(blocked)} candidate(s): "
574
+ + ", ".join(f.fact_id for f in blocked)
575
+ )
576
+ logger.debug(
577
+ f"[fact-reconcile] subject gate: draft subject={draft.subject!r} "
578
+ f"blocked subjects={[f.subject for f in blocked]!r}"
576
579
  )
577
580
  result.append(kept)
578
581
  return result
@@ -745,7 +748,10 @@ class WorkingFactReconciler:
745
748
  obj = None
746
749
 
747
750
  if not isinstance(obj, dict):
748
- logger.warning(f"[fact-reconcile] failed to parse ops output: {text!r}")
751
+ logger.warning(
752
+ f"[fact-reconcile] failed to parse ops output ({len(text or '')} chars)"
753
+ )
754
+ logger.debug(f"[fact-reconcile] unparsable ops output: {text!r}")
749
755
  return {}
750
756
  ops_raw = obj.get("ops")
751
757
  if not isinstance(ops_raw, list):
@@ -835,8 +841,11 @@ class WorkingFactReconciler:
835
841
  # 别的主体的状态不是。闸门关掉时这道复查一并跳过,否则闸门就没关干净。
836
842
  if self.subject_gate and _subject_conflict(draft.subject, old.subject):
837
843
  logger.warning(
838
- f"[fact-reconcile] refused cross-subject SUPERSEDE: "
839
- f"{old.fact_id}({old.subject!r}) by draft subject {draft.subject!r}"
844
+ f"[fact-reconcile] refused cross-subject SUPERSEDE: {old.fact_id}"
845
+ )
846
+ logger.debug(
847
+ f"[fact-reconcile] cross-subject: old subject={old.subject!r} "
848
+ f"draft subject={draft.subject!r}"
840
849
  )
841
850
  continue
842
851
  try:
@@ -195,8 +195,10 @@ async def classify_messages_scene(
195
195
  result = _parse_scene_json(resp.content)
196
196
  if result is None:
197
197
  logger.warning(
198
- f"[coding-judge] failed to parse classify_messages output: {resp.content!r}; defaulting to chat"
198
+ f"[coding-judge] failed to parse classify_messages output "
199
+ f"({len(resp.content or '')} chars); defaulting to chat"
199
200
  )
201
+ logger.debug(f"[coding-judge] unparsable classify_messages output: {resp.content!r}")
200
202
  await _store_coding_log(
201
203
  cache, request_id=request_id, user_id=user_id, agent_id=agent_id,
202
204
  session_id=session_id, step="CODING_JUDGE", prompt=prompt,
@@ -207,9 +209,8 @@ async def classify_messages_scene(
207
209
  return "chat"
208
210
  scene = result["scene"]
209
211
  parsed = {**result, "is_coding": scene != "chat"}
210
- logger.info(
211
- f"[coding-judge] scene={scene} reason={result.get('reason', '')!r}"
212
- )
212
+ logger.info(f"[coding-judge] scene={scene}")
213
+ logger.debug(f"[coding-judge] scene={scene} reason={result.get('reason', '')!r}")
213
214
  await _store_coding_log(
214
215
  cache, request_id=request_id, user_id=user_id, agent_id=agent_id,
215
216
  session_id=session_id, step="CODING_JUDGE", prompt=prompt,
@@ -295,8 +296,10 @@ async def classify_queries_scene(
295
296
  result = _parse_scene_json(resp.content)
296
297
  if result is None:
297
298
  logger.warning(
298
- f"[coding-judge] failed to parse classify_queries output: {resp.content!r}; defaulting to chat"
299
+ f"[coding-judge] failed to parse classify_queries output "
300
+ f"({len(resp.content or '')} chars); defaulting to chat"
299
301
  )
302
+ logger.debug(f"[coding-judge] unparsable classify_queries output: {resp.content!r}")
300
303
  return "chat"
301
304
  scene = result["scene"]
302
305
  logger.info(f"[coding-judge] queries scene={scene}")
@@ -422,15 +425,18 @@ async def classify_and_rewrite_queries(
422
425
  scene = _parse_scene_label(head)
423
426
  if scene is None:
424
427
  logger.warning(
425
- f"[coding-judge] classify_and_rewrite unrecognized head={head!r}; falling back to chat"
428
+ f"[coding-judge] classify_and_rewrite unrecognized head "
429
+ f"({len(head)} chars); falling back to chat"
426
430
  )
431
+ logger.debug(f"[coding-judge] classify_and_rewrite unrecognized head={head!r}")
427
432
  return {"scene": "chat", "is_coding": False, "rewrite_query": rewrite, "ok": False}
428
433
  is_coding = scene != "chat"
429
434
 
430
435
  logger.info(
431
436
  f"[coding-judge] classify_and_rewrite scene={scene} "
432
- f"rewrite={rewrite!r}"
437
+ f"rewrite({len(rewrite)} chars)"
433
438
  )
439
+ logger.debug(f"[coding-judge] classify_and_rewrite rewrite={rewrite!r}")
434
440
  return {"scene": scene, "is_coding": is_coding, "rewrite_query": rewrite, "ok": True}
435
441
 
436
442
 
@@ -569,8 +575,9 @@ async def analyze_queries(
569
575
  parsed_scene = _parse_scene_label(head)
570
576
  if parsed_scene is None:
571
577
  logger.warning(
572
- f"[coding-judge] analyze_queries unrecognized head={head!r}; ok=False"
578
+ f"[coding-judge] analyze_queries unrecognized head ({len(head)} chars); ok=False"
573
579
  )
580
+ logger.debug(f"[coding-judge] analyze_queries unrecognized head={head!r}")
574
581
  return {**fallback, "since_raw": ""}
575
582
  scene = parsed_scene
576
583
  idx = 1
@@ -589,9 +596,10 @@ async def analyze_queries(
589
596
  created_after = _since_to_created_after(since_raw, current_time)
590
597
 
591
598
  logger.info(
592
- f"[coding-judge] analyze scene={scene} rewrite={rewrite!r} "
599
+ f"[coding-judge] analyze scene={scene} rewrite({len(rewrite)} chars) "
593
600
  f"since={since_raw!r} created_after={created_after}"
594
601
  )
602
+ logger.debug(f"[coding-judge] analyze rewrite={rewrite!r}")
595
603
  return {
596
604
  "scene": scene, "is_coding": scene != "chat", "rewrite_query": rewrite,
597
605
  "created_after": created_after, "since_raw": since_raw, "ok": True,
@@ -614,7 +622,10 @@ def _since_to_created_after(since_raw: str, current_time: str) -> Optional[float
614
622
  return dt.timestamp()
615
623
  except (ValueError, TypeError):
616
624
  continue
617
- logger.warning(f"[coding-judge] analyze_queries unparsable SINCE={since_raw!r}")
625
+ logger.warning(
626
+ f"[coding-judge] analyze_queries unparsable SINCE ({len(since_raw)} chars)"
627
+ )
628
+ logger.debug(f"[coding-judge] analyze_queries unparsable SINCE={since_raw!r}")
618
629
  return None
619
630
 
620
631
 
@@ -388,7 +388,10 @@ class CodingMemoryReconciler:
388
388
  obj = None
389
389
 
390
390
  if not obj or not isinstance(obj, dict):
391
- logger.warning(f"[coding-reconcile] failed to parse ops output: {text!r}")
391
+ logger.warning(
392
+ f"[coding-reconcile] failed to parse ops output ({len(text or '')} chars)"
393
+ )
394
+ logger.debug(f"[coding-reconcile] unparsable ops output: {text!r}")
392
395
  return []
393
396
  ops_raw = obj.get("ops") or []
394
397
  if not isinstance(ops_raw, list):
@@ -441,10 +444,10 @@ class CodingMemoryReconciler:
441
444
  for op in ops:
442
445
  if op.action == "ADD":
443
446
  if op.draft_idx is None or not (0 <= op.draft_idx < len(drafts)):
444
- logger.warning(f"[coding-reconcile] drop invalid ADD: {op.to_dict()}")
447
+ logger.warning(f"[coding-reconcile] drop invalid ADD: {_op_ref(op)}")
445
448
  continue
446
449
  if op.draft_idx in consumed:
447
- logger.warning(f"[coding-reconcile] drop ADD on consumed draft: {op.to_dict()}")
450
+ logger.warning(f"[coding-reconcile] drop ADD on consumed draft: {_op_ref(op)}")
448
451
  continue
449
452
  d = drafts[op.draft_idx]
450
453
  memory = self._draft_to_new_memory(d)
@@ -458,18 +461,18 @@ class CodingMemoryReconciler:
458
461
 
459
462
  elif op.action == "UPDATE":
460
463
  if op.draft_idx is None or not (0 <= op.draft_idx < len(drafts)):
461
- logger.warning(f"[coding-reconcile] drop invalid UPDATE: {op.to_dict()}")
464
+ logger.warning(f"[coding-reconcile] drop invalid UPDATE: {_op_ref(op)}")
462
465
  continue
463
466
  if not op.target_experience_id:
464
- logger.warning(f"[coding-reconcile] drop UPDATE missing target: {op.to_dict()}")
467
+ logger.warning(f"[coding-reconcile] drop UPDATE missing target: {_op_ref(op)}")
465
468
  continue
466
469
  if not self._target_was_offered(op, candidates_per_draft):
467
470
  logger.warning(
468
- f"[coding-reconcile] drop UPDATE outside authority scope: {op.to_dict()}"
471
+ f"[coding-reconcile] drop UPDATE outside authority scope: {_op_ref(op)}"
469
472
  )
470
473
  continue
471
474
  if op.draft_idx in consumed:
472
- logger.warning(f"[coding-reconcile] drop UPDATE on consumed draft: {op.to_dict()}")
475
+ logger.warning(f"[coding-reconcile] drop UPDATE on consumed draft: {_op_ref(op)}")
473
476
  continue
474
477
  d = drafts[op.draft_idx]
475
478
  memory = self._draft_to_new_memory(d, experience_id=op.target_experience_id)
@@ -488,17 +491,17 @@ class CodingMemoryReconciler:
488
491
 
489
492
  elif op.action == "DELETE":
490
493
  if not op.target_experience_id:
491
- logger.warning(f"[coding-reconcile] drop DELETE missing target: {op.to_dict()}")
494
+ logger.warning(f"[coding-reconcile] drop DELETE missing target: {_op_ref(op)}")
492
495
  continue
493
496
  if not self._target_was_offered(op, candidates_per_draft):
494
497
  logger.warning(
495
- f"[coding-reconcile] drop DELETE outside authority scope: {op.to_dict()}"
498
+ f"[coding-reconcile] drop DELETE outside authority scope: {_op_ref(op)}"
496
499
  )
497
500
  continue
498
501
  # DELETE 需要知道 user_id;通过 draft_idx(如有)或第一个 draft 推断
499
502
  user_id = self._infer_user_id(op, drafts)
500
503
  if not user_id:
501
- logger.warning(f"[coding-reconcile] drop DELETE no user_id context: {op.to_dict()}")
504
+ logger.warning(f"[coding-reconcile] drop DELETE no user_id context: {_op_ref(op)}")
502
505
  continue
503
506
  try:
504
507
  ok = await self.store.delete(op.target_experience_id, user_id=user_id)
@@ -585,6 +588,14 @@ class CodingMemoryReconciler:
585
588
  # Helpers
586
589
  # ================================================================
587
590
 
591
+ def _op_ref(op: ReconcileOp) -> str:
592
+ """op 的日志引用:只带动作与编号,不带 LLM 写的 reason。"""
593
+ return (
594
+ f"action={op.action} draft_idx={op.draft_idx} "
595
+ f"target={op.target_experience_id}"
596
+ )
597
+
598
+
588
599
  def _truncate(s: str, n: int) -> str:
589
600
  if not s:
590
601
  return ""
@@ -605,7 +605,11 @@ class CodingMemoryStore:
605
605
  await self._upsert_vec_keys(memory)
606
606
  logger.info(
607
607
  f"[coding-store] inserted experience_id={memory.experience_id} "
608
- f"task={memory.task!r} keys={1 + len(memory.search_keys)}"
608
+ f"keys={1 + len(memory.search_keys)}"
609
+ )
610
+ logger.debug(
611
+ f"[coding-store] inserted experience_id={memory.experience_id} "
612
+ f"task={memory.task!r}"
609
613
  )
610
614
 
611
615
  async def update(self, memory: CodingMemory) -> None:
@@ -1002,7 +1006,8 @@ class CodingMemoryStore:
1002
1006
  fact.created_at = datetime.utcnow()
1003
1007
  await _run_in_pool(self._upsert_fact_sql_sync, fact)
1004
1008
  await self._upsert_fact_vec(fact)
1005
- logger.info(
1009
+ logger.info(f"[coding-store] inserted fact_id={fact.fact_id}")
1010
+ logger.debug(
1006
1011
  f"[coding-store] inserted fact_id={fact.fact_id} "
1007
1012
  f"statement={fact.statement!r}"
1008
1013
  )
@@ -80,7 +80,7 @@ def _count_tokens(text: str) -> int:
80
80
  try:
81
81
  import tiktoken
82
82
 
83
- return len(tiktoken.get_encoding("cl100k_base").encode(text))
83
+ return len(tiktoken.get_encoding("cl100k_base").encode(text, disallowed_special=()))
84
84
  except Exception:
85
85
  return len(text) // _CHARS_PER_TOKEN_FALLBACK + 1
86
86
 
@@ -98,7 +98,8 @@ def _truncate_for_embedding(text: str, limit: int = _EMBED_MAX_TOKENS) -> str:
98
98
  import tiktoken
99
99
 
100
100
  enc = tiktoken.get_encoding("cl100k_base")
101
- ids = enc.encode(text)
101
+ # Input is ordinary user text, not a tokenizer control-token stream.
102
+ ids = enc.encode(text, disallowed_special=())
102
103
  if len(ids) <= limit:
103
104
  return text
104
105
  dropped = len(ids) - limit
@@ -753,16 +754,22 @@ class EmbedService:
753
754
  """
754
755
  打一条 INFO 级 embedding 请求日志(含 request_id / provider / model / api / 入参 / 出参)。
755
756
 
756
- 出参只含 vector 维度,不含向量数值。request_id 同时写入 message,
757
+ 出参只含 vector 维度,不含向量数值。入参的 input 是待向量化的原文
758
+ (对话 / 记忆 / 查询),INFO 只记每条的字符数(input_chars),原文仅在
759
+ DEBUG 单独输出。request_id 同时写入 message,
757
760
  并依赖 log formatter 的 %(request_id)s(setup_logging 后生效)。
758
761
  """
762
+ texts = request_payload.get("input")
763
+ info_request = {k: v for k, v in request_payload.items() if k != "input"}
764
+ if isinstance(texts, list):
765
+ info_request["input_chars"] = [len(str(t)) for t in texts]
759
766
  payload = {
760
767
  "request_id": get_request_id(),
761
768
  "status": status,
762
769
  "provider": self._embed_config.provider,
763
770
  "model": self._embed_config.model,
764
771
  "api": api_url,
765
- "request": request_payload,
772
+ "request": info_request,
766
773
  "response": response_payload,
767
774
  }
768
775
  try:
@@ -770,6 +777,10 @@ class EmbedService:
770
777
  except Exception:
771
778
  body = str(payload)
772
779
  logger.info(f"[embed] {body}")
780
+ if texts is not None and logger.isEnabledFor(logging.DEBUG):
781
+ logger.debug(
782
+ f"[embed] input: {json.dumps(texts, ensure_ascii=False, default=str)}"
783
+ )
773
784
 
774
785
  async def _embed_openai(
775
786
  self, texts: List[str]
@@ -115,11 +115,16 @@ class ReconcileHybridRetriever:
115
115
  fused_max = max(fused_all) if fused_all else 0.0
116
116
 
117
117
  base = (
118
- f"[reconcile-hybrid][{req}] pool={pool_size} q_terms={q_terms!r} "
118
+ f"[reconcile-hybrid][{req}] pool={pool_size} q_terms={len(q_terms)} "
119
119
  f"raw_bm25 max={raw_max:.4f} nonzero={raw_nonzero}/{len(raw_bm25)} "
120
120
  f"bm25_norm max={norm_max:.4f} nonzero={norm_nonzero}/{len(bm25_norms)} "
121
121
  f"sem_max={sem_max:.4f} fused_max={fused_max:.4f} "
122
122
  f"kept={kept}/{len(fused_all)} min_score={self._cfg.min_score} "
123
+ f"query({len(query_text or '')} chars)"
124
+ )
125
+ # query 是待对账的新记忆原文,词项与预览只进 DEBUG
126
+ logger.debug(
127
+ f"[reconcile-hybrid][{req}] q_terms={q_terms!r} "
123
128
  f"query={q_preview!r} lemmatized={lem_preview!r}"
124
129
  )
125
130
 
@@ -692,9 +692,10 @@ class CrossDomainSweeper:
692
692
  )
693
693
 
694
694
  logger.info(
695
- f"[sweeper] Created L6 core {core_id[:12]}: '{core_content[:60]}' "
695
+ f"[sweeper] Created L6 core {core_id[:12]} "
696
696
  f"from {len(cluster_ids)} basics, evidence_inherited={len(inherited_evidence_ids)}"
697
697
  )
698
+ logger.debug(f"[sweeper] L6 core {core_id[:12]} content={core_content[:60]!r}")
698
699
  return 1, 0, [core_id]
699
700
 
700
701
  except Exception as e:
@@ -3587,8 +3587,9 @@ class BaseReadPipeline(ReadPipeline):
3587
3587
  f"hidden_l0={len(hidden_l0_hits)} fwd_l6={len(forward_l6)} rev_l6={len(reverse_l6)} "
3588
3588
  f"fused_l6={len(fused_l6)} graph_intent={len(graph_intention_hits)} "
3589
3589
  f"returned={len(final_results)} "
3590
- f"query='{request.query[:80]}'"
3590
+ f"query({len(request.query or '')} chars)"
3591
3591
  )
3592
+ logger.debug(f"[read/{self.VERSION}] query={request.query[:80]!r}")
3592
3593
 
3593
3594
  response.success = True
3594
3595
 
@@ -2516,7 +2516,8 @@ def _parse_operations_json(text: str) -> Optional[List[Dict[str, Any]]]:
2516
2516
  return result
2517
2517
  return None
2518
2518
  except json.JSONDecodeError:
2519
- logger.warning(f"[S2-agent] JSON parse failed: {json_str[:200]}")
2519
+ logger.warning(f"[S2-agent] JSON parse failed ({len(json_str)} chars)")
2520
+ logger.debug(f"[S2-agent] unparsable output: {json_str[:200]!r}")
2520
2521
  return None
2521
2522
 
2522
2523
 
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import concurrent.futures
7
+ import contextvars
7
8
  import functools
8
9
  from concurrent.futures import Executor
9
10
  from typing import Any, Callable, Optional
@@ -39,7 +40,9 @@ async def run_in_executor_guarded(
39
40
 
40
41
  async def to_thread_guarded(func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
41
42
  """Guarded equivalent of ``asyncio.to_thread`` using the default pool."""
42
- return await run_in_executor_guarded(None, func, *args, **kwargs)
43
+ context = contextvars.copy_context()
44
+ call = functools.partial(func, *args, **kwargs)
45
+ return await run_in_executor_guarded(None, context.run, call)
43
46
 
44
47
 
45
48
  def concurrent_result_guarded(
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """
3
- Pipeline Log Writer — 环节级 JSONL 日志(始终开启,与 SQLite Trace 分离)。
3
+ Pipeline Log Writer — 环节级 JSONL 日志(默认开启,MEMORY_PIPELINE_LOG_ENABLED=false 关闭;
4
+ 与 SQLite Trace 分离)。
4
5
 
5
6
  目录: {log_dir}/{subdir}/{date}.log
6
7
  每行: request_id, namespace, user_id, agent_id, step, prompt, content, parsed,