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.
- {hy_memory-2.0.7/hy_memory.egg-info → hy_memory-2.0.8}/PKG-INFO +1 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/extractor.py +4 -3
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/llm_provider.py +1 -2
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/mem_agent.py +1 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/reconciler.py +11 -9
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/tools/base.py +3 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/client.py +19 -13
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/extractor.py +4 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/fact_reconciler.py +15 -6
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/judge.py +21 -10
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/reconciler.py +21 -10
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/store.py +7 -2
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/embed_service.py +15 -4
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/reconcile_retrieval.py +6 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/cross_domain_sweeper.py +2 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/reader_base.py +2 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/system2_agent.py +2 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/async_executor.py +4 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/pipeline_log_writer.py +2 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/pipeline_observability.py +71 -30
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/token_count.py +2 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8/hy_memory.egg-info}/PKG-INFO +1 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/pyproject.toml +1 -1
- {hy_memory-2.0.7 → hy_memory-2.0.8}/LICENSE +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/MANIFEST.in +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/README.md +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/abstractor.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/emotion_analyzer.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/episode_summary.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/intention_detector.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/prompts_zh.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/reflector.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/tools/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/agent/tools/basic_profile.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/chain_store.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/lexical.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/preproc.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/prompts_zh.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/types.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/coding/writer.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/config.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/merger.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/scorer.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/core/workload.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/cache.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/cache_base.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/cache_mysql.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/cache_sqlite.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/graph_store.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/graph_store_base.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/graph_store_kuzu.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/graph_store_neo4j.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/history_store.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/kv_store.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/rdb.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store_base.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store_chroma.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store_faiss.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/data/vector_store_qdrant.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/embedding.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/inspector.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/metrics.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/models/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/models/memory.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/models/requests.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_graph/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_graph/projector.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_graph/rerank.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_graph/topic_summary.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/__init__.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/bm25.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/bm25_fastembed.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/candidate_guards.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/config.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/date_view.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/dedup.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/diversity.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/entities.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/entity_store.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/evolution.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/hierarchy_bundle.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/intent.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/intention.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/lemmatize.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/local_rerank.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/memory_format.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/multi_seed_bundle.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/ner.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/profile_evidence.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/profile_owner_gate.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/query_expand.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/record_date_view.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/retrieval_clauses.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/rrf.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/scoring.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/series_tail_guard.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/session_episode.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/strength.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/supplemental_propositions.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/tag_index.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/temporal.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/topic_candidates.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/trace.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/underflow.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/_retrieval/user_level_l0.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/balanced_catalog.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/base.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/reader.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/reader_hybrid.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/reader_legacy.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/registry.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/specific_supplement.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/system2_tools.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/system2_writer.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/user_profile_scope.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/pipelines/writer.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/retrieval.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/runtime.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/server.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/snapshot.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/structure.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/clone_references.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/keyword_ties.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/lang_detect.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/log_setup.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/metrics.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/model_call_context.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/source_filter.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/time_window.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/trace_file_writer.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory/utils/tracer.py +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory.egg-info/SOURCES.txt +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory.egg-info/dependency_links.txt +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory.egg-info/requires.txt +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/hy_memory.egg-info/top_level.txt +0 -0
- {hy_memory-2.0.7 → hy_memory-2.0.8}/setup.cfg +0 -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"
|
|
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
|
|
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
|
|
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'!
|
|
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.
|
|
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
|
-
"""以单条
|
|
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.
|
|
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
|
|
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
|
|
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}
|
|
2023
|
+
f"current op '{op.op}' dropped"
|
|
2022
2024
|
)
|
|
2023
2025
|
continue
|
|
2024
2026
|
for t in target_ids:
|
|
2025
|
-
touched[t] =
|
|
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(
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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});
|
|
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;
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
575
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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"
|
|
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
|
-
|
|
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
|
|
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":
|
|
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
|
|
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]}
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|