MaiBot 1.3.8__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.
Files changed (716) hide show
  1. maibot-1.3.8.dist-info/METADATA +245 -0
  2. maibot-1.3.8.dist-info/RECORD +716 -0
  3. maibot-1.3.8.dist-info/WHEEL +5 -0
  4. maibot-1.3.8.dist-info/entry_points.txt +2 -0
  5. maibot-1.3.8.dist-info/licenses/LICENSE +674 -0
  6. maibot-1.3.8.dist-info/top_level.txt +1 -0
  7. src/A_memorix/LICENSE +661 -0
  8. src/A_memorix/LICENSE-MAIBOT-GPL.md +22 -0
  9. src/A_memorix/__init__.py +5 -0
  10. src/A_memorix/config_schema.json +1640 -0
  11. src/A_memorix/core/__init__.py +83 -0
  12. src/A_memorix/core/embedding/__init__.py +17 -0
  13. src/A_memorix/core/embedding/api_adapter.py +650 -0
  14. src/A_memorix/core/embedding/manager.py +506 -0
  15. src/A_memorix/core/embedding/presets.py +68 -0
  16. src/A_memorix/core/image/__init__.py +6 -0
  17. src/A_memorix/core/image/asset_store.py +141 -0
  18. src/A_memorix/core/image/component_paths.py +36 -0
  19. src/A_memorix/core/image/embedder.py +129 -0
  20. src/A_memorix/core/image/fingerprints.py +36 -0
  21. src/A_memorix/core/image/runtime.py +900 -0
  22. src/A_memorix/core/retrieval/__init__.py +66 -0
  23. src/A_memorix/core/retrieval/dual_path.py +3272 -0
  24. src/A_memorix/core/retrieval/graph_relation_recall.py +285 -0
  25. src/A_memorix/core/retrieval/pagerank.py +471 -0
  26. src/A_memorix/core/retrieval/posterior_graph.py +787 -0
  27. src/A_memorix/core/retrieval/score_calibration.py +180 -0
  28. src/A_memorix/core/retrieval/sparse_bm25.py +717 -0
  29. src/A_memorix/core/retrieval/threshold.py +404 -0
  30. src/A_memorix/core/runtime/__init__.py +26 -0
  31. src/A_memorix/core/runtime/admin_contracts.py +293 -0
  32. src/A_memorix/core/runtime/kernel_compat.py +269 -0
  33. src/A_memorix/core/runtime/lifecycle_orchestrator.py +368 -0
  34. src/A_memorix/core/runtime/models.py +27 -0
  35. src/A_memorix/core/runtime/runtime_facade.py +141 -0
  36. src/A_memorix/core/runtime/runtime_writer_lock.py +105 -0
  37. src/A_memorix/core/runtime/sdk_memory_kernel.py +1907 -0
  38. src/A_memorix/core/runtime/search_runtime_initializer.py +290 -0
  39. src/A_memorix/core/runtime/services/__init__.py +63 -0
  40. src/A_memorix/core/runtime/services/background_task_service.py +477 -0
  41. src/A_memorix/core/runtime/services/base.py +28 -0
  42. src/A_memorix/core/runtime/services/bundle_admin_service.py +2200 -0
  43. src/A_memorix/core/runtime/services/chat_filter_service.py +88 -0
  44. src/A_memorix/core/runtime/services/correction_admin_service.py +1274 -0
  45. src/A_memorix/core/runtime/services/delete_admin_service.py +2018 -0
  46. src/A_memorix/core/runtime/services/dual_vector_migration_service.py +109 -0
  47. src/A_memorix/core/runtime/services/dual_vector_state_service.py +503 -0
  48. src/A_memorix/core/runtime/services/embedding_state_service.py +515 -0
  49. src/A_memorix/core/runtime/services/episode_admin_service.py +104 -0
  50. src/A_memorix/core/runtime/services/fact_admin_service.py +199 -0
  51. src/A_memorix/core/runtime/services/feedback_correction_service.py +1523 -0
  52. src/A_memorix/core/runtime/services/graph_admin_service.py +1521 -0
  53. src/A_memorix/core/runtime/services/image_service.py +158 -0
  54. src/A_memorix/core/runtime/services/import_tuning_admin_service.py +158 -0
  55. src/A_memorix/core/runtime/services/ingest_service.py +787 -0
  56. src/A_memorix/core/runtime/services/memory_maintenance_service.py +404 -0
  57. src/A_memorix/core/runtime/services/memory_search_service.py +373 -0
  58. src/A_memorix/core/runtime/services/profile_admin_service.py +646 -0
  59. src/A_memorix/core/runtime/services/request_dedup_service.py +34 -0
  60. src/A_memorix/core/runtime/services/runtime_config_service.py +94 -0
  61. src/A_memorix/core/runtime/services/runtime_dependency_service.py +105 -0
  62. src/A_memorix/core/runtime/services/runtime_lifecycle_service.py +409 -0
  63. src/A_memorix/core/runtime/services/search_hit_processing_service.py +891 -0
  64. src/A_memorix/core/runtime/services/source_admin_service.py +52 -0
  65. src/A_memorix/core/runtime/services/stats_service.py +35 -0
  66. src/A_memorix/core/runtime/services/summary_service.py +50 -0
  67. src/A_memorix/core/runtime/services/v5_admin_service.py +326 -0
  68. src/A_memorix/core/runtime/services/vector_delete_service.py +30 -0
  69. src/A_memorix/core/runtime/services/vector_rebuild_checkpoint.py +65 -0
  70. src/A_memorix/core/runtime/services/vector_recovery_service.py +460 -0
  71. src/A_memorix/core/runtime/services/vector_runtime_service.py +1417 -0
  72. src/A_memorix/core/runtime/services/vector_space_service.py +251 -0
  73. src/A_memorix/core/storage/__init__.py +62 -0
  74. src/A_memorix/core/storage/format_migration.py +676 -0
  75. src/A_memorix/core/storage/graph_store.py +1733 -0
  76. src/A_memorix/core/storage/knowledge_types.py +183 -0
  77. src/A_memorix/core/storage/metadata_cleanup.py +913 -0
  78. src/A_memorix/core/storage/metadata_episode.py +1659 -0
  79. src/A_memorix/core/storage/metadata_fact.py +1410 -0
  80. src/A_memorix/core/storage/metadata_feedback.py +745 -0
  81. src/A_memorix/core/storage/metadata_fts.py +937 -0
  82. src/A_memorix/core/storage/metadata_image.py +1076 -0
  83. src/A_memorix/core/storage/metadata_profile.py +782 -0
  84. src/A_memorix/core/storage/metadata_schema.py +2503 -0
  85. src/A_memorix/core/storage/metadata_store.py +4565 -0
  86. src/A_memorix/core/storage/sqlite_connection.py +176 -0
  87. src/A_memorix/core/storage/tokenizer_runtime.py +9 -0
  88. src/A_memorix/core/storage/transaction.py +42 -0
  89. src/A_memorix/core/storage/type_detection.py +137 -0
  90. src/A_memorix/core/storage/value_coercion.py +10 -0
  91. src/A_memorix/core/storage/vector_store.py +2851 -0
  92. src/A_memorix/core/strategies/__init__.py +0 -0
  93. src/A_memorix/core/strategies/base.py +92 -0
  94. src/A_memorix/core/strategies/chat_log.py +80 -0
  95. src/A_memorix/core/strategies/factual.py +104 -0
  96. src/A_memorix/core/strategies/narrative.py +148 -0
  97. src/A_memorix/core/strategies/quote.py +47 -0
  98. src/A_memorix/core/utils/__init__.py +33 -0
  99. src/A_memorix/core/utils/aggregate_query_service.py +359 -0
  100. src/A_memorix/core/utils/episode_retrieval_service.py +222 -0
  101. src/A_memorix/core/utils/episode_segmentation_service.py +322 -0
  102. src/A_memorix/core/utils/episode_service.py +646 -0
  103. src/A_memorix/core/utils/feedback_policy.py +149 -0
  104. src/A_memorix/core/utils/hash.py +128 -0
  105. src/A_memorix/core/utils/import_payloads.py +222 -0
  106. src/A_memorix/core/utils/io.py +85 -0
  107. src/A_memorix/core/utils/matcher.py +175 -0
  108. src/A_memorix/core/utils/memory_lifecycle_policy.py +336 -0
  109. src/A_memorix/core/utils/metadata.py +11 -0
  110. src/A_memorix/core/utils/model_routing.py +239 -0
  111. src/A_memorix/core/utils/monitor.py +188 -0
  112. src/A_memorix/core/utils/path_fallback_service.py +182 -0
  113. src/A_memorix/core/utils/person_profile_service.py +1277 -0
  114. src/A_memorix/core/utils/plugin_id_policy.py +26 -0
  115. src/A_memorix/core/utils/profile_evidence.py +25 -0
  116. src/A_memorix/core/utils/profile_policy.py +45 -0
  117. src/A_memorix/core/utils/profile_text.py +204 -0
  118. src/A_memorix/core/utils/quantization.py +343 -0
  119. src/A_memorix/core/utils/relation_query.py +121 -0
  120. src/A_memorix/core/utils/relation_write_service.py +504 -0
  121. src/A_memorix/core/utils/retrieval_tuning_manager.py +2369 -0
  122. src/A_memorix/core/utils/runtime_payloads.py +132 -0
  123. src/A_memorix/core/utils/runtime_self_check.py +303 -0
  124. src/A_memorix/core/utils/search_execution_service.py +445 -0
  125. src/A_memorix/core/utils/search_postprocess.py +91 -0
  126. src/A_memorix/core/utils/summary_importer.py +991 -0
  127. src/A_memorix/core/utils/time_parser.py +156 -0
  128. src/A_memorix/core/utils/web_import_manager.py +4511 -0
  129. src/A_memorix/host_service.py +1018 -0
  130. src/A_memorix/paths.py +58 -0
  131. src/A_memorix/plugin.py +321 -0
  132. src/A_memorix/runtime_registry.py +30 -0
  133. src/A_memorix/scripts/_bootstrap.py +22 -0
  134. src/A_memorix/scripts/audit_vector_consistency.py +205 -0
  135. src/A_memorix/scripts/backfill_relation_vectors.py +265 -0
  136. src/A_memorix/scripts/backfill_temporal_metadata.py +67 -0
  137. src/A_memorix/scripts/convert_lpmm.py +537 -0
  138. src/A_memorix/scripts/import_lpmm_json.py +189 -0
  139. src/A_memorix/scripts/migrate_chat_history.py +101 -0
  140. src/A_memorix/scripts/migrate_maibot_memory.py +1955 -0
  141. src/A_memorix/scripts/migrate_person_memory_points.py +113 -0
  142. src/A_memorix/scripts/process_knowledge.py +831 -0
  143. src/A_memorix/scripts/rebuild_episodes.py +207 -0
  144. src/A_memorix/scripts/release_vnext_migrate.py +875 -0
  145. src/A_memorix/scripts/runtime_self_check.py +144 -0
  146. src/__init__.py +14 -0
  147. src/_resources/EULA.md +135 -0
  148. src/_resources/PRIVACY.md +60 -0
  149. src/_resources/changelogs/changelog.md +918 -0
  150. src/_resources/locales/en-US/config.json +39 -0
  151. src/_resources/locales/en-US/core.json +8 -0
  152. src/_resources/locales/en-US/prompts.json +8 -0
  153. src/_resources/locales/en-US/startup.json +98 -0
  154. src/_resources/locales/ja-JP/config.json +39 -0
  155. src/_resources/locales/ja-JP/core.json +8 -0
  156. src/_resources/locales/ja-JP/prompts.json +8 -0
  157. src/_resources/locales/ja-JP/startup.json +98 -0
  158. src/_resources/locales/ko/config.json +39 -0
  159. src/_resources/locales/ko/core.json +8 -0
  160. src/_resources/locales/ko/prompts.json +8 -0
  161. src/_resources/locales/ko/startup.json +98 -0
  162. src/_resources/locales/zh-CN/config.json +39 -0
  163. src/_resources/locales/zh-CN/core.json +8 -0
  164. src/_resources/locales/zh-CN/prompts.json +8 -0
  165. src/_resources/locales/zh-CN/startup.json +98 -0
  166. src/_resources/prompts/en-US/.meta.toml +164 -0
  167. src/_resources/prompts/en-US/a_memorix_chat_summary.prompt +45 -0
  168. src/_resources/prompts/en-US/emoji_content_analysis.prompt +3 -0
  169. src/_resources/prompts/en-US/emoji_content_filtration.prompt +6 -0
  170. src/_resources/prompts/en-US/emoji_replace.prompt +12 -0
  171. src/_resources/prompts/en-US/emoji_selection.prompt +6 -0
  172. src/_resources/prompts/en-US/expression_evaluation.prompt +14 -0
  173. src/_resources/prompts/en-US/heuristic_memory_impression.prompt +15 -0
  174. src/_resources/prompts/en-US/image_description.prompt +1 -0
  175. src/_resources/prompts/en-US/jargon_compare_inference.prompt +14 -0
  176. src/_resources/prompts/en-US/jargon_inference_content_only.prompt +11 -0
  177. src/_resources/prompts/en-US/jargon_inference_with_context.prompt +19 -0
  178. src/_resources/prompts/en-US/learn_jargon.prompt +31 -0
  179. src/_resources/prompts/en-US/learn_style.prompt +28 -0
  180. src/_resources/prompts/en-US/maisaka_chat.prompt +28 -0
  181. src/_resources/prompts/en-US/maisaka_chat_focus.prompt +39 -0
  182. src/_resources/prompts/en-US/maisaka_chat_optimized.prompt +19 -0
  183. src/_resources/prompts/en-US/maisaka_replyer.prompt +10 -0
  184. src/_resources/prompts/en-US/memory_fuzzy_modify_plan.prompt +45 -0
  185. src/_resources/prompts/en-US/mid_term_memory_summary.prompt +10 -0
  186. src/_resources/prompts/en-US/reply_attachments.prompt +3 -0
  187. src/_resources/prompts/en-US/retro_private_replyer.prompt +13 -0
  188. src/_resources/prompts/en-US/retro_private_replyer_self.prompt +13 -0
  189. src/_resources/prompts/en-US/retro_replyer.prompt +17 -0
  190. src/_resources/prompts/en-US/retro_replyer_light.prompt +17 -0
  191. src/_resources/prompts/en-US/webui_ai_search.prompt +24 -0
  192. src/_resources/prompts/en-US/webui_ai_search_correction.prompt +1 -0
  193. src/_resources/prompts/en-US/webui_ai_search_evidence.prompt +3 -0
  194. src/_resources/prompts/en-US/webui_ai_search_final.prompt +1 -0
  195. src/_resources/prompts/ja-JP/.meta.toml +164 -0
  196. src/_resources/prompts/ja-JP/a_memorix_chat_summary.prompt +45 -0
  197. src/_resources/prompts/ja-JP/emoji_content_analysis.prompt +3 -0
  198. src/_resources/prompts/ja-JP/emoji_content_filtration.prompt +6 -0
  199. src/_resources/prompts/ja-JP/emoji_replace.prompt +12 -0
  200. src/_resources/prompts/ja-JP/emoji_selection.prompt +6 -0
  201. src/_resources/prompts/ja-JP/expression_evaluation.prompt +14 -0
  202. src/_resources/prompts/ja-JP/heuristic_memory_impression.prompt +15 -0
  203. src/_resources/prompts/ja-JP/image_description.prompt +1 -0
  204. src/_resources/prompts/ja-JP/jargon_compare_inference.prompt +14 -0
  205. src/_resources/prompts/ja-JP/jargon_inference_content_only.prompt +11 -0
  206. src/_resources/prompts/ja-JP/jargon_inference_with_context.prompt +19 -0
  207. src/_resources/prompts/ja-JP/learn_jargon.prompt +31 -0
  208. src/_resources/prompts/ja-JP/learn_style.prompt +28 -0
  209. src/_resources/prompts/ja-JP/maisaka_chat.prompt +28 -0
  210. src/_resources/prompts/ja-JP/maisaka_chat_focus.prompt +39 -0
  211. src/_resources/prompts/ja-JP/maisaka_chat_optimized.prompt +19 -0
  212. src/_resources/prompts/ja-JP/maisaka_replyer.prompt +10 -0
  213. src/_resources/prompts/ja-JP/memory_fuzzy_modify_plan.prompt +45 -0
  214. src/_resources/prompts/ja-JP/mid_term_memory_summary.prompt +10 -0
  215. src/_resources/prompts/ja-JP/reply_attachments.prompt +3 -0
  216. src/_resources/prompts/ja-JP/retro_private_replyer.prompt +13 -0
  217. src/_resources/prompts/ja-JP/retro_private_replyer_self.prompt +13 -0
  218. src/_resources/prompts/ja-JP/retro_replyer.prompt +17 -0
  219. src/_resources/prompts/ja-JP/retro_replyer_light.prompt +17 -0
  220. src/_resources/prompts/ja-JP/webui_ai_search.prompt +24 -0
  221. src/_resources/prompts/ja-JP/webui_ai_search_correction.prompt +1 -0
  222. src/_resources/prompts/ja-JP/webui_ai_search_evidence.prompt +3 -0
  223. src/_resources/prompts/ja-JP/webui_ai_search_final.prompt +1 -0
  224. src/_resources/prompts/ko/.meta.toml +4 -0
  225. src/_resources/prompts/ko/reply_attachments.prompt +3 -0
  226. src/_resources/prompts/zh-CN/.meta.toml +159 -0
  227. src/_resources/prompts/zh-CN/a_memorix_chat_summary.prompt +59 -0
  228. src/_resources/prompts/zh-CN/emoji_content_analysis.prompt +3 -0
  229. src/_resources/prompts/zh-CN/emoji_content_filtration.prompt +6 -0
  230. src/_resources/prompts/zh-CN/emoji_replace.prompt +12 -0
  231. src/_resources/prompts/zh-CN/emoji_selection.prompt +6 -0
  232. src/_resources/prompts/zh-CN/expression_evaluation.prompt +14 -0
  233. src/_resources/prompts/zh-CN/heuristic_memory_impression.prompt +15 -0
  234. src/_resources/prompts/zh-CN/image_description.prompt +1 -0
  235. src/_resources/prompts/zh-CN/jargon_compare_inference.prompt +14 -0
  236. src/_resources/prompts/zh-CN/jargon_inference_content_only.prompt +11 -0
  237. src/_resources/prompts/zh-CN/jargon_inference_with_context.prompt +19 -0
  238. src/_resources/prompts/zh-CN/learn_jargon.prompt +31 -0
  239. src/_resources/prompts/zh-CN/learn_style.prompt +28 -0
  240. src/_resources/prompts/zh-CN/maisaka_chat.prompt +28 -0
  241. src/_resources/prompts/zh-CN/maisaka_chat_focus.prompt +39 -0
  242. src/_resources/prompts/zh-CN/maisaka_chat_optimized.prompt +19 -0
  243. src/_resources/prompts/zh-CN/maisaka_replyer.prompt +6 -0
  244. src/_resources/prompts/zh-CN/memory_fuzzy_modify_plan.prompt +45 -0
  245. src/_resources/prompts/zh-CN/mid_term_memory_summary.prompt +10 -0
  246. src/_resources/prompts/zh-CN/reply_attachments.prompt +3 -0
  247. src/_resources/prompts/zh-CN/retro_private_replyer.prompt +13 -0
  248. src/_resources/prompts/zh-CN/retro_private_replyer_self.prompt +13 -0
  249. src/_resources/prompts/zh-CN/retro_replyer.prompt +17 -0
  250. src/_resources/prompts/zh-CN/retro_replyer_light.prompt +17 -0
  251. src/_resources/prompts/zh-CN/webui_ai_search.prompt +24 -0
  252. src/_resources/prompts/zh-CN/webui_ai_search_correction.prompt +1 -0
  253. src/_resources/prompts/zh-CN/webui_ai_search_evidence.prompt +3 -0
  254. src/_resources/prompts/zh-CN/webui_ai_search_final.prompt +1 -0
  255. src/_resources/pyproject.toml +156 -0
  256. src/chat/__init__.py +13 -0
  257. src/chat/heart_flow/heartFC_utils.py +29 -0
  258. src/chat/heart_flow/heartflow_manager.py +138 -0
  259. src/chat/heart_flow/heartflow_message_processor.py +104 -0
  260. src/chat/image_system/image_cache_cleanup.py +300 -0
  261. src/chat/image_system/image_manager.py +516 -0
  262. src/chat/message_receive/__init__.py +5 -0
  263. src/chat/message_receive/bot.py +893 -0
  264. src/chat/message_receive/chat_manager.py +540 -0
  265. src/chat/message_receive/image_receive_compressor.py +135 -0
  266. src/chat/message_receive/message.py +546 -0
  267. src/chat/message_receive/uni_message_sender.py +332 -0
  268. src/chat/replyer/expression_vector_index.py +3176 -0
  269. src/chat/replyer/maisaka_expression_selector.py +678 -0
  270. src/chat/replyer/maisaka_generator.py +25 -0
  271. src/chat/replyer/maisaka_generator_base.py +1457 -0
  272. src/chat/replyer/replyer_manager.py +62 -0
  273. src/chat/replyer/retro_prompt.py +512 -0
  274. src/chat/utils/llm_sentence_splitter.py +151 -0
  275. src/chat/utils/statistic.py +3170 -0
  276. src/chat/utils/typo_generator.py +495 -0
  277. src/chat/utils/utils.py +922 -0
  278. src/cli/__init__.py +3 -0
  279. src/cli/bot_console.py +281 -0
  280. src/cli/console.py +17 -0
  281. src/cli/input_reader.py +55 -0
  282. src/cli/maisaka_cli.py +122 -0
  283. src/cli/maisaka_cli_sender.py +28 -0
  284. src/cli/tool.py +62 -0
  285. src/common/__init__.py +1 -0
  286. src/common/data_models/__init__.py +28 -0
  287. src/common/data_models/chat_session_data_model.py +73 -0
  288. src/common/data_models/chat_target_info_data_model.py +73 -0
  289. src/common/data_models/embedding_service_data_models.py +31 -0
  290. src/common/data_models/expression_data_model.py +83 -0
  291. src/common/data_models/image_data_model.py +247 -0
  292. src/common/data_models/jargon_data_model.py +101 -0
  293. src/common/data_models/llm_data_model.py +23 -0
  294. src/common/data_models/llm_service_data_models.py +298 -0
  295. src/common/data_models/mai_message_data_model.py +226 -0
  296. src/common/data_models/message_component_data_model.py +560 -0
  297. src/common/data_models/person_info_data_model.py +177 -0
  298. src/common/data_models/reply_generation_data_models.py +275 -0
  299. src/common/data_models/tool_record_data_model.py +51 -0
  300. src/common/database/__init__.py +0 -0
  301. src/common/database/database.py +240 -0
  302. src/common/database/database_model.py +477 -0
  303. src/common/database/migrations/__init__.py +161 -0
  304. src/common/database/migrations/behavior_generic_tags.py +532 -0
  305. src/common/database/migrations/bootstrap.py +171 -0
  306. src/common/database/migrations/builtin.py +1966 -0
  307. src/common/database/migrations/exceptions.py +33 -0
  308. src/common/database/migrations/frozen_v2_schema.py +282 -0
  309. src/common/database/migrations/legacy_v1_to_v2.py +1300 -0
  310. src/common/database/migrations/manager.py +209 -0
  311. src/common/database/migrations/models.py +305 -0
  312. src/common/database/migrations/planner.py +108 -0
  313. src/common/database/migrations/progress.py +319 -0
  314. src/common/database/migrations/registry.py +98 -0
  315. src/common/database/migrations/resolver.py +135 -0
  316. src/common/database/migrations/schema.py +98 -0
  317. src/common/database/migrations/v10_to_v11.py +60 -0
  318. src/common/database/migrations/v11_to_v12.py +92 -0
  319. src/common/database/migrations/v12_to_v13.py +42 -0
  320. src/common/database/migrations/v13_to_v14.py +55 -0
  321. src/common/database/migrations/v14_to_v15.py +44 -0
  322. src/common/database/migrations/v15_to_v16.py +39 -0
  323. src/common/database/migrations/v16_to_v17.py +66 -0
  324. src/common/database/migrations/v17_to_v18.py +64 -0
  325. src/common/database/migrations/v18_to_v19.py +355 -0
  326. src/common/database/migrations/v19_to_v20.py +169 -0
  327. src/common/database/migrations/v20_to_v21.py +149 -0
  328. src/common/database/migrations/v21_to_v22.py +343 -0
  329. src/common/database/migrations/v22_to_v23.py +114 -0
  330. src/common/database/migrations/v23_to_v24.py +465 -0
  331. src/common/database/migrations/v24_to_v25.py +96 -0
  332. src/common/database/migrations/v25_to_v26.py +32 -0
  333. src/common/database/migrations/v26_to_v27.py +86 -0
  334. src/common/database/migrations/v27_to_v28.py +185 -0
  335. src/common/database/migrations/v28_to_v29.py +80 -0
  336. src/common/database/migrations/v29_to_v30.py +217 -0
  337. src/common/database/migrations/v2_to_v3.py +269 -0
  338. src/common/database/migrations/v30_to_v31.py +323 -0
  339. src/common/database/migrations/v31_to_v32.py +250 -0
  340. src/common/database/migrations/v32_to_v33.py +84 -0
  341. src/common/database/migrations/v33_to_v34.py +119 -0
  342. src/common/database/migrations/v34_to_v35.py +68 -0
  343. src/common/database/migrations/v35_to_v36.py +35 -0
  344. src/common/database/migrations/v36_to_v37.py +54 -0
  345. src/common/database/migrations/v37_to_v38.py +95 -0
  346. src/common/database/migrations/v38_to_v39.py +85 -0
  347. src/common/database/migrations/v39_to_v40.py +53 -0
  348. src/common/database/migrations/v3_to_v4.py +155 -0
  349. src/common/database/migrations/v40_to_v41.py +23 -0
  350. src/common/database/migrations/v41_to_v42.py +44 -0
  351. src/common/database/migrations/v42_to_v43.py +29 -0
  352. src/common/database/migrations/v4_to_v5.py +77 -0
  353. src/common/database/migrations/v5_to_v6.py +51 -0
  354. src/common/database/migrations/v6_to_v7.py +164 -0
  355. src/common/database/migrations/v7_to_v8.py +73 -0
  356. src/common/database/migrations/v8_to_v9.py +313 -0
  357. src/common/database/migrations/v9_to_v10.py +53 -0
  358. src/common/database/migrations/version_store.py +57 -0
  359. src/common/embedding_identity.py +43 -0
  360. src/common/event_loop_watchdog.py +72 -0
  361. src/common/i18n/__init__.py +70 -0
  362. src/common/i18n/exceptions.py +18 -0
  363. src/common/i18n/formatting.py +29 -0
  364. src/common/i18n/loaders.py +162 -0
  365. src/common/i18n/manager.py +201 -0
  366. src/common/logger.py +1009 -0
  367. src/common/logger_color_and_mapping.py +430 -0
  368. src/common/message_repository.py +287 -0
  369. src/common/message_server/__init__.py +19 -0
  370. src/common/message_server/api.py +190 -0
  371. src/common/message_server/server.py +130 -0
  372. src/common/operation_timing.py +52 -0
  373. src/common/paths.py +32 -0
  374. src/common/process_runner.py +142 -0
  375. src/common/prompt_i18n.py +416 -0
  376. src/common/remote.py +288 -0
  377. src/common/reply_effect_fingerprint.py +119 -0
  378. src/common/reply_effect_record_codec.py +40 -0
  379. src/common/runtime_loop.py +31 -0
  380. src/common/self_update.py +580 -0
  381. src/common/shutdown.py +55 -0
  382. src/common/update_notice.py +351 -0
  383. src/common/utils/image_path.py +39 -0
  384. src/common/utils/math_utils.py +122 -0
  385. src/common/utils/port_checker.py +97 -0
  386. src/common/utils/system_utils.py +11 -0
  387. src/common/utils/utils_action.py +32 -0
  388. src/common/utils/utils_config.py +613 -0
  389. src/common/utils/utils_file.py +56 -0
  390. src/common/utils/utils_image.py +257 -0
  391. src/common/utils/utils_message.py +871 -0
  392. src/common/utils/utils_person.py +55 -0
  393. src/common/utils/utils_session.py +42 -0
  394. src/common/utils/utils_voice.py +42 -0
  395. src/common/version.py +33 -0
  396. src/config/__init__.py +0 -0
  397. src/config/config.py +927 -0
  398. src/config/config_base.py +308 -0
  399. src/config/config_upgrade_hooks.py +675 -0
  400. src/config/config_utils.py +166 -0
  401. src/config/default_model_config.py +131 -0
  402. src/config/file_watcher.py +238 -0
  403. src/config/legacy_migration.py +424 -0
  404. src/config/legacy_upgrade_confirmation.py +225 -0
  405. src/config/model_configs.py +548 -0
  406. src/config/official_configs.py +6166 -0
  407. src/config/startup_bindings.py +201 -0
  408. src/core/__init__.py +6 -0
  409. src/core/announcement_manager.py +120 -0
  410. src/core/config_types.py +273 -0
  411. src/core/event_bus.py +225 -0
  412. src/core/local_operator.py +98 -0
  413. src/core/tooling.py +420 -0
  414. src/core/types.py +423 -0
  415. src/emoji_system/emoji_cache_cleanup.py +326 -0
  416. src/emoji_system/emoji_manager.py +1314 -0
  417. src/emoji_system/maisaka_tool.py +286 -0
  418. src/learners/expression_learner.py +927 -0
  419. src/learners/expression_review_store.py +216 -0
  420. src/learners/expression_style_utils.py +32 -0
  421. src/learners/expression_utils.py +299 -0
  422. src/learners/jargon_learner.py +873 -0
  423. src/learners/jargon_miner.py +885 -0
  424. src/learners/weighted_sampling.py +75 -0
  425. src/llm_models/LICENSE +21 -0
  426. src/llm_models/__init__.py +0 -0
  427. src/llm_models/exceptions.py +140 -0
  428. src/llm_models/generation_diagnostics.py +155 -0
  429. src/llm_models/image_normalizer.py +128 -0
  430. src/llm_models/model_client/__init__.py +29 -0
  431. src/llm_models/model_client/adapter_base.py +362 -0
  432. src/llm_models/model_client/base_client.py +678 -0
  433. src/llm_models/model_client/embedding_rate_limit.py +55 -0
  434. src/llm_models/model_client/gemini_client.py +1379 -0
  435. src/llm_models/model_client/image_embedding_protocols.py +397 -0
  436. src/llm_models/model_client/openai_client.py +2200 -0
  437. src/llm_models/model_client/openai_responses_client.py +817 -0
  438. src/llm_models/model_client/plugin_client.py +307 -0
  439. src/llm_models/openai_compat.py +165 -0
  440. src/llm_models/payload_content/__init__.py +51 -0
  441. src/llm_models/payload_content/context_item.py +699 -0
  442. src/llm_models/payload_content/context_protocol.py +243 -0
  443. src/llm_models/payload_content/native_tool.py +18 -0
  444. src/llm_models/payload_content/resp_format.py +239 -0
  445. src/llm_models/payload_content/tool_option.py +547 -0
  446. src/llm_models/request_snapshot.py +1311 -0
  447. src/llm_models/utils.py +276 -0
  448. src/llm_models/utils_model.py +1604 -0
  449. src/main.py +300 -0
  450. src/maisaka/attention_drift.py +116 -0
  451. src/maisaka/builtin_tool/__init__.py +236 -0
  452. src/maisaka/builtin_tool/context.py +530 -0
  453. src/maisaka/builtin_tool/fetch_history.py +62 -0
  454. src/maisaka/builtin_tool/provider.py +74 -0
  455. src/maisaka/builtin_tool/query_image_memory.py +103 -0
  456. src/maisaka/builtin_tool/query_memory.py +335 -0
  457. src/maisaka/builtin_tool/query_person_profile.py +152 -0
  458. src/maisaka/builtin_tool/read_tool_result.py +142 -0
  459. src/maisaka/builtin_tool/reply.py +695 -0
  460. src/maisaka/builtin_tool/send_emoji.py +543 -0
  461. src/maisaka/builtin_tool/show_emoji_list.py +76 -0
  462. src/maisaka/builtin_tool/subagent.py +549 -0
  463. src/maisaka/builtin_tool/switch_chat.py +78 -0
  464. src/maisaka/builtin_tool/tool_search.py +92 -0
  465. src/maisaka/builtin_tool/view_forward_message.py +153 -0
  466. src/maisaka/builtin_tool/wait.py +77 -0
  467. src/maisaka/chat_loop_service.py +1579 -0
  468. src/maisaka/context/__init__.py +1 -0
  469. src/maisaka/context/clear_context.py +42 -0
  470. src/maisaka/context/emoji_candidates.py +36 -0
  471. src/maisaka/context/history.py +384 -0
  472. src/maisaka/context/image_attachment.py +52 -0
  473. src/maisaka/context/message_adapter.py +126 -0
  474. src/maisaka/context/message_id_alias.py +52 -0
  475. src/maisaka/context/messages.py +825 -0
  476. src/maisaka/context/planner_messages.py +199 -0
  477. src/maisaka/context/post_processor.py +204 -0
  478. src/maisaka/context/usage.py +141 -0
  479. src/maisaka/display/__init__.py +25 -0
  480. src/maisaka/display/display_utils.py +144 -0
  481. src/maisaka/display/preview_path_utils.py +64 -0
  482. src/maisaka/display/prompt_cli_renderer.py +1110 -0
  483. src/maisaka/display/prompt_preview_logger.py +603 -0
  484. src/maisaka/display/runtime_mixin.py +709 -0
  485. src/maisaka/display/stage_status_board.py +129 -0
  486. src/maisaka/focus/__init__.py +10 -0
  487. src/maisaka/focus/manager.py +442 -0
  488. src/maisaka/focus/runtime_mixin.py +753 -0
  489. src/maisaka/jargon_context_matcher.py +363 -0
  490. src/maisaka/memory/__init__.py +1 -0
  491. src/maisaka/memory/heuristic_injector.py +393 -0
  492. src/maisaka/memory/image_injector.py +81 -0
  493. src/maisaka/memory/mid_term.py +995 -0
  494. src/maisaka/memory/person_profile.py +280 -0
  495. src/maisaka/mode_policy.py +28 -0
  496. src/maisaka/monitor/__init__.py +1 -0
  497. src/maisaka/monitor/event_store.py +351 -0
  498. src/maisaka/monitor/events.py +795 -0
  499. src/maisaka/monitor/message_payload.py +172 -0
  500. src/maisaka/monitor/planner_delta.py +77 -0
  501. src/maisaka/monitor/tool_media.py +58 -0
  502. src/maisaka/planner_reply_guard.py +69 -0
  503. src/maisaka/reasoning_engine.py +2186 -0
  504. src/maisaka/reply_effect/__init__.py +18 -0
  505. src/maisaka/reply_effect/image_utils.py +100 -0
  506. src/maisaka/reply_effect/judge.py +382 -0
  507. src/maisaka/reply_effect/models.py +185 -0
  508. src/maisaka/reply_effect/path_utils.py +24 -0
  509. src/maisaka/reply_effect/quote_utils.py +32 -0
  510. src/maisaka/reply_effect/scoring.py +205 -0
  511. src/maisaka/reply_effect/storage.py +380 -0
  512. src/maisaka/reply_effect/tracker.py +568 -0
  513. src/maisaka/runtime.py +2218 -0
  514. src/maisaka/subagent_tasks.py +171 -0
  515. src/maisaka/tool_result_condenser.py +143 -0
  516. src/maisaka/tool_result_store.py +57 -0
  517. src/maisaka/turn_trigger/__init__.py +9 -0
  518. src/maisaka/turn_trigger/dynamic_gate.py +211 -0
  519. src/maisaka/turn_trigger/gates.py +252 -0
  520. src/maisaka/turn_trigger/idle_backoff.py +93 -0
  521. src/maisaka/turn_trigger/reply_likelihood.py +88 -0
  522. src/maisaka/turn_trigger/scheduler.py +113 -0
  523. src/maisaka/utils/__init__.py +1 -0
  524. src/maisaka/utils/tool_post_execution.py +76 -0
  525. src/maisaka/utils/tool_record_payload.py +155 -0
  526. src/maisaka/visual/__init__.py +1 -0
  527. src/maisaka/visual/chat_history_refresher.py +303 -0
  528. src/maisaka/visual/message_limiter.py +69 -0
  529. src/maisaka/visual/mode_utils.py +33 -0
  530. src/manager/async_task_manager.py +193 -0
  531. src/manager/local_store_manager.py +126 -0
  532. src/mcp_module/__init__.py +9 -0
  533. src/mcp_module/config.py +174 -0
  534. src/mcp_module/connection.py +726 -0
  535. src/mcp_module/hooks.py +20 -0
  536. src/mcp_module/host_llm_bridge.py +654 -0
  537. src/mcp_module/manager.py +680 -0
  538. src/mcp_module/models.py +437 -0
  539. src/mcp_module/provider.py +62 -0
  540. src/mcp_module/service.py +308 -0
  541. src/mcp_module/stdio_filter.py +177 -0
  542. src/person_info/__init__.py +0 -0
  543. src/person_info/person_info.py +600 -0
  544. src/platform_io/__init__.py +39 -0
  545. src/platform_io/adapter_policy.py +839 -0
  546. src/platform_io/avatar.py +197 -0
  547. src/platform_io/dedupe.py +133 -0
  548. src/platform_io/drivers/__init__.py +11 -0
  549. src/platform_io/drivers/base.py +104 -0
  550. src/platform_io/drivers/legacy_driver.py +92 -0
  551. src/platform_io/drivers/plugin_driver.py +213 -0
  552. src/platform_io/manager.py +716 -0
  553. src/platform_io/outbound_tracker.py +286 -0
  554. src/platform_io/registry.py +70 -0
  555. src/platform_io/route_key_factory.py +174 -0
  556. src/platform_io/routing.py +141 -0
  557. src/platform_io/types.py +265 -0
  558. src/plugin_runtime/__init__.py +60 -0
  559. src/plugin_runtime/avatar_provider.py +64 -0
  560. src/plugin_runtime/capabilities/__init__.py +11 -0
  561. src/plugin_runtime/capabilities/components.py +977 -0
  562. src/plugin_runtime/capabilities/core.py +868 -0
  563. src/plugin_runtime/capabilities/data.py +1415 -0
  564. src/plugin_runtime/capabilities/registry.py +116 -0
  565. src/plugin_runtime/capabilities/render.py +131 -0
  566. src/plugin_runtime/compat_policy.py +47 -0
  567. src/plugin_runtime/component_query.py +1119 -0
  568. src/plugin_runtime/dependency_pipeline.py +547 -0
  569. src/plugin_runtime/docker_layout_migration.py +275 -0
  570. src/plugin_runtime/hook_catalog.py +52 -0
  571. src/plugin_runtime/hook_payloads.py +197 -0
  572. src/plugin_runtime/hook_schema_utils.py +31 -0
  573. src/plugin_runtime/host/__init__.py +1 -0
  574. src/plugin_runtime/host/api_registry.py +353 -0
  575. src/plugin_runtime/host/authorization.py +67 -0
  576. src/plugin_runtime/host/capability_service.py +91 -0
  577. src/plugin_runtime/host/circuit_breaker.py +243 -0
  578. src/plugin_runtime/host/component_registry.py +1336 -0
  579. src/plugin_runtime/host/component_timeout.py +32 -0
  580. src/plugin_runtime/host/event_dispatcher.py +228 -0
  581. src/plugin_runtime/host/hook_dispatcher.py +711 -0
  582. src/plugin_runtime/host/hook_spec_registry.py +190 -0
  583. src/plugin_runtime/host/logger_bridge.py +64 -0
  584. src/plugin_runtime/host/message_gateway.py +116 -0
  585. src/plugin_runtime/host/message_utils.py +572 -0
  586. src/plugin_runtime/host/reply_extension_schema.py +65 -0
  587. src/plugin_runtime/host/reply_extensions.py +204 -0
  588. src/plugin_runtime/host/rpc_server.py +509 -0
  589. src/plugin_runtime/host/supervisor.py +2269 -0
  590. src/plugin_runtime/integration.py +2063 -0
  591. src/plugin_runtime/local_sdk.py +147 -0
  592. src/plugin_runtime/protocol/__init__.py +1 -0
  593. src/plugin_runtime/protocol/codec.py +71 -0
  594. src/plugin_runtime/protocol/envelope.py +600 -0
  595. src/plugin_runtime/protocol/errors.py +77 -0
  596. src/plugin_runtime/runner/__init__.py +1 -0
  597. src/plugin_runtime/runner/log_handler.py +239 -0
  598. src/plugin_runtime/runner/manifest_validator.py +1517 -0
  599. src/plugin_runtime/runner/plugin_loader.py +711 -0
  600. src/plugin_runtime/runner/plugin_paths.py +59 -0
  601. src/plugin_runtime/runner/rpc_client.py +336 -0
  602. src/plugin_runtime/runner/runner_main.py +2670 -0
  603. src/plugin_runtime/tool_provider.py +58 -0
  604. src/plugin_runtime/transport/__init__.py +1 -0
  605. src/plugin_runtime/transport/base.py +116 -0
  606. src/plugin_runtime/transport/factory.py +57 -0
  607. src/plugin_runtime/transport/named_pipe.py +206 -0
  608. src/plugin_runtime/transport/tcp.py +62 -0
  609. src/plugin_runtime/transport/uds.py +133 -0
  610. src/plugin_runtime/update_compatibility_notice.py +303 -0
  611. src/plugin_runtime/upload_store.py +173 -0
  612. src/plugin_runtime/webui_schema.py +571 -0
  613. src/plugins/__init__.py +1 -0
  614. src/plugins/built_in/plugin_management/_manifest.json +48 -0
  615. src/plugins/built_in/plugin_management/plugin.py +665 -0
  616. src/prompt/prompt_manager.py +440 -0
  617. src/runtime.py +688 -0
  618. src/services/__init__.py +7 -0
  619. src/services/bot_account_service.py +327 -0
  620. src/services/database_service.py +203 -0
  621. src/services/embedding_service.py +271 -0
  622. src/services/expression_vector_service.py +106 -0
  623. src/services/html_render_service.py +945 -0
  624. src/services/image_path_maintenance_service.py +454 -0
  625. src/services/image_writeback_journal.py +98 -0
  626. src/services/llm_cache_stats.py +1521 -0
  627. src/services/llm_service.py +799 -0
  628. src/services/memory_flow_service.py +1019 -0
  629. src/services/memory_service.py +528 -0
  630. src/services/message_service.py +295 -0
  631. src/services/message_word_frequency_service.py +595 -0
  632. src/services/news_service.py +123 -0
  633. src/services/person_fact_reverification.py +90 -0
  634. src/services/person_fact_verifier.py +79 -0
  635. src/services/send_service.py +1483 -0
  636. src/services/service_task_resolver.py +59 -0
  637. src/services/statistics_aggregation_service.py +349 -0
  638. src/services/statistics_service.py +850 -0
  639. src/services/telemetry_stats_service.py +294 -0
  640. src/services/tool_record_cleanup_service.py +205 -0
  641. src/webui/app.py +374 -0
  642. src/webui/config_schema.py +481 -0
  643. src/webui/core/__init__.py +32 -0
  644. src/webui/core/auth.py +153 -0
  645. src/webui/core/rate_limiter.py +247 -0
  646. src/webui/core/security.py +361 -0
  647. src/webui/dependencies.py +70 -0
  648. src/webui/logs_ws.py +195 -0
  649. src/webui/middleware/__init__.py +17 -0
  650. src/webui/middleware/anti_crawler.py +824 -0
  651. src/webui/middleware/compression.py +88 -0
  652. src/webui/routers/__init__.py +33 -0
  653. src/webui/routers/avatar.py +250 -0
  654. src/webui/routers/bot_accounts.py +103 -0
  655. src/webui/routers/chat/__init__.py +18 -0
  656. src/webui/routers/chat/routes.py +1715 -0
  657. src/webui/routers/chat/serializers.py +248 -0
  658. src/webui/routers/chat/service.py +1489 -0
  659. src/webui/routers/config.py +1964 -0
  660. src/webui/routers/data_transfer.py +769 -0
  661. src/webui/routers/emoji/__init__.py +3 -0
  662. src/webui/routers/emoji/routes.py +1230 -0
  663. src/webui/routers/emoji/schemas.py +182 -0
  664. src/webui/routers/emoji/support.py +143 -0
  665. src/webui/routers/expression.py +2298 -0
  666. src/webui/routers/jargon.py +1319 -0
  667. src/webui/routers/mcp.py +99 -0
  668. src/webui/routers/memory.py +5589 -0
  669. src/webui/routers/model.py +910 -0
  670. src/webui/routers/news.py +23 -0
  671. src/webui/routers/person.py +427 -0
  672. src/webui/routers/plugin/__init__.py +31 -0
  673. src/webui/routers/plugin/catalog.py +218 -0
  674. src/webui/routers/plugin/config_routes.py +592 -0
  675. src/webui/routers/plugin/icon_routes.py +71 -0
  676. src/webui/routers/plugin/management.py +908 -0
  677. src/webui/routers/plugin/marketplace.py +116 -0
  678. src/webui/routers/plugin/progress.py +172 -0
  679. src/webui/routers/plugin/release_install.py +313 -0
  680. src/webui/routers/plugin/releases.py +181 -0
  681. src/webui/routers/plugin/runtime_routes.py +243 -0
  682. src/webui/routers/plugin/schemas.py +131 -0
  683. src/webui/routers/plugin/stats_proxy.py +127 -0
  684. src/webui/routers/plugin/support.py +366 -0
  685. src/webui/routers/plugin/webui_extensions.py +346 -0
  686. src/webui/routers/plugin/zip_install.py +125 -0
  687. src/webui/routers/reasoning_process.py +2326 -0
  688. src/webui/routers/reply_effects.py +926 -0
  689. src/webui/routers/search.py +249 -0
  690. src/webui/routers/self_update.py +194 -0
  691. src/webui/routers/statistics.py +61 -0
  692. src/webui/routers/system.py +1790 -0
  693. src/webui/routers/user_emoji.py +168 -0
  694. src/webui/routers/websocket/__init__.py +7 -0
  695. src/webui/routers/websocket/auth.py +104 -0
  696. src/webui/routers/websocket/manager.py +373 -0
  697. src/webui/routers/websocket/plugin_runtime.py +54 -0
  698. src/webui/routers/websocket/unified.py +761 -0
  699. src/webui/routes.py +416 -0
  700. src/webui/schemas/__init__.py +18 -0
  701. src/webui/schemas/news.py +24 -0
  702. src/webui/schemas/statistics.py +173 -0
  703. src/webui/services/__init__.py +1 -0
  704. src/webui/services/ai_search_agent.py +509 -0
  705. src/webui/services/ai_search_documents.py +329 -0
  706. src/webui/services/ai_search_grounding.py +103 -0
  707. src/webui/services/ai_search_local_config.py +129 -0
  708. src/webui/services/ai_search_models.py +98 -0
  709. src/webui/services/git_mirror_service.py +1024 -0
  710. src/webui/services/plugin_market_service.py +56 -0
  711. src/webui/utils/__init__.py +1 -0
  712. src/webui/utils/http_client.py +27 -0
  713. src/webui/utils/network_security.py +133 -0
  714. src/webui/utils/toml_utils.py +124 -0
  715. src/webui/version_compatibility.py +180 -0
  716. src/webui/webui_server.py +422 -0
@@ -0,0 +1,831 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 知识库自动导入脚本 (Strategy-Aware Version)
4
+
5
+ 功能:
6
+ 1. 扫描 A_Memorix 数据目录 imports/source/raw 下的 .txt 文件
7
+ 2. 检查 imports/manifest.json 确认是否已导入
8
+ 3. 使用 Strategy 模式处理文件 (Narrative/Factual/Quote)
9
+ 4. 将生成的数据直接存入 VectorStore/GraphStore/MetadataStore
10
+ 5. 更新 manifest
11
+ """
12
+
13
+ from datetime import datetime
14
+ from pathlib import Path
15
+ from typing import Any, Dict, Optional
16
+ import argparse
17
+ import asyncio
18
+ import hashlib
19
+ import json
20
+ import os
21
+ import sys
22
+ import time
23
+
24
+ from rich.console import Console
25
+ from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential
26
+ import tomlkit
27
+
28
+ from _bootstrap import DEFAULT_CONFIG_PATH, DEFAULT_DATA_DIR, resolve_repo_path
29
+
30
+ console = Console()
31
+
32
+
33
+ class LLMGenerationError(Exception):
34
+ pass
35
+
36
+
37
+ def _build_arg_parser() -> argparse.ArgumentParser:
38
+ parser = argparse.ArgumentParser(description="A_Memorix Knowledge Importer (Strategy-Aware)")
39
+ parser.add_argument("--data-dir", default=str(DEFAULT_DATA_DIR), help="A_Memorix 数据目录")
40
+ parser.add_argument("--force", action="store_true", help="Force re-import")
41
+ parser.add_argument("--clear-manifest", action="store_true", help="Clear manifest")
42
+ parser.add_argument(
43
+ "--type",
44
+ "-t",
45
+ default="auto",
46
+ help="Target import strategy override (auto/narrative/factual/quote)",
47
+ )
48
+ parser.add_argument("--concurrency", "-c", type=int, default=5)
49
+ parser.add_argument(
50
+ "--chat-log",
51
+ action="store_true",
52
+ help="聊天记录导入模式:强制 narrative 策略,并使用 LLM 语义抽取 event_time/event_time_range",
53
+ )
54
+ parser.add_argument(
55
+ "--chat-reference-time",
56
+ default=None,
57
+ help="chat_log 模式的相对时间参考点(如 2026/02/12 10:30);不传则使用当前本地时间",
58
+ )
59
+ return parser
60
+
61
+
62
+ # --help/-h 快速路径:避免加载较重的宿主和插件运行时
63
+ if any(arg in {"-h", "--help"} for arg in sys.argv[1:]):
64
+ _build_arg_parser().print_help()
65
+ sys.exit(0)
66
+
67
+
68
+ try:
69
+ import A_memorix.core as core_module
70
+ import A_memorix.core.storage as storage_module
71
+ from src.common.logger import get_logger
72
+ from src.services import llm_service as llm_api
73
+ from src.config.config import global_config
74
+
75
+ VectorStore = core_module.VectorStore
76
+ GraphStore = core_module.GraphStore
77
+ MetadataStore = core_module.MetadataStore
78
+ ImportStrategy = core_module.ImportStrategy
79
+ create_embedding_api_adapter = core_module.create_embedding_api_adapter
80
+ RelationWriteService = getattr(core_module, "RelationWriteService", None)
81
+
82
+ looks_like_quote_text = storage_module.looks_like_quote_text
83
+ parse_import_strategy = storage_module.parse_import_strategy
84
+ resolve_stored_knowledge_type = storage_module.resolve_stored_knowledge_type
85
+ select_import_strategy = storage_module.select_import_strategy
86
+
87
+ from A_memorix.core.utils.import_payloads import (
88
+ ImportPayloadValidationError,
89
+ is_probable_hash_token,
90
+ normalize_entity_import_item,
91
+ normalize_paragraph_import_item,
92
+ normalize_relation_import_item,
93
+ )
94
+ from A_memorix.core.utils.model_routing import (
95
+ ResolvedLLMModel,
96
+ generate_with_resolved_model,
97
+ get_text_generation_model_tasks,
98
+ pick_text_generation_task,
99
+ resolve_text_generation_model_selector,
100
+ )
101
+ from A_memorix.core.utils.time_parser import normalize_time_meta
102
+ from A_memorix.core.strategies.base import BaseStrategy, ProcessedChunk, KnowledgeType as StratKnowledgeType
103
+ from A_memorix.core.strategies.narrative import NarrativeStrategy
104
+ from A_memorix.core.strategies.factual import FactualStrategy
105
+ from A_memorix.core.strategies.quote import QuoteStrategy
106
+
107
+ except ImportError as e:
108
+ print(f"❌ 无法导入模块: {e}")
109
+ import traceback
110
+
111
+ traceback.print_exc()
112
+ sys.exit(1)
113
+
114
+ logger = get_logger("A_Memorix.AutoImport")
115
+
116
+
117
+ def _log_before_retry(retry_state) -> None:
118
+ """使用项目统一日志风格记录重试信息。"""
119
+ exc = None
120
+ if getattr(retry_state, "outcome", None) is not None and retry_state.outcome.failed:
121
+ exc = retry_state.outcome.exception()
122
+ next_sleep = getattr(getattr(retry_state, "next_action", None), "sleep", None)
123
+ logger.warning(
124
+ f"LLM 调用即将重试: attempt={getattr(retry_state, 'attempt_number', '?')} next_sleep={next_sleep} error={exc}"
125
+ )
126
+
127
+
128
+ class AutoImporter:
129
+ def __init__(
130
+ self,
131
+ force: bool = False,
132
+ clear_manifest: bool = False,
133
+ target_type: str = "auto",
134
+ concurrency: int = 5,
135
+ chat_log: bool = False,
136
+ chat_reference_time: Optional[str] = None,
137
+ data_dir: Optional[Path] = None,
138
+ ):
139
+ self.vector_store: Optional[VectorStore] = None
140
+ self.graph_store: Optional[GraphStore] = None
141
+ self.metadata_store: Optional[MetadataStore] = None
142
+ self.embedding_manager = None
143
+ self.relation_write_service = None
144
+ self.plugin_config = {}
145
+ self.manifest = {}
146
+ self.data_dir = resolve_repo_path(data_dir, fallback=DEFAULT_DATA_DIR)
147
+ self.import_dir = self.data_dir / "imports"
148
+ self.raw_dir = self.import_dir / "source" / "raw"
149
+ self.processed_dir = self.import_dir / "processed"
150
+ self.manifest_path = self.import_dir / "manifest.json"
151
+ self.force = force
152
+ self.clear_manifest = clear_manifest
153
+ self.chat_log = chat_log
154
+ parsed_target_type = parse_import_strategy(target_type, default=ImportStrategy.AUTO)
155
+ self.target_type = ImportStrategy.NARRATIVE.value if chat_log else parsed_target_type.value
156
+ self.chat_reference_dt = self._parse_reference_time(chat_reference_time)
157
+ if self.chat_log and parsed_target_type not in {ImportStrategy.AUTO, ImportStrategy.NARRATIVE}:
158
+ logger.warning(f"chat_log 模式已启用,target_type={target_type} 将被覆盖为 narrative")
159
+ self.concurrency_limit = concurrency
160
+ self.semaphore = None
161
+ self.storage_lock = None
162
+
163
+ async def initialize(self):
164
+ logger.info(f"正在初始化... (并发数: {self.concurrency_limit})")
165
+ self.semaphore = asyncio.Semaphore(self.concurrency_limit)
166
+ self.storage_lock = asyncio.Lock()
167
+
168
+ self.raw_dir.mkdir(parents=True, exist_ok=True)
169
+ self.processed_dir.mkdir(parents=True, exist_ok=True)
170
+ legacy_manifest_path = self.data_dir / "import_manifest.json"
171
+ if legacy_manifest_path.is_file() and not self.manifest_path.exists():
172
+ try:
173
+ os.replace(legacy_manifest_path, self.manifest_path)
174
+ logger.info(f"已迁移旧导入清单: {legacy_manifest_path} -> {self.manifest_path}")
175
+ except OSError as exc:
176
+ logger.warning(f"旧导入清单迁移失败,保留原文件继续执行: {legacy_manifest_path}, error={exc}")
177
+
178
+ if self.clear_manifest:
179
+ logger.info("🧹 清理 Mainfest")
180
+ self.manifest = {}
181
+ self._save_manifest()
182
+ elif self.manifest_path.exists():
183
+ try:
184
+ with open(self.manifest_path, "r", encoding="utf-8") as f:
185
+ self.manifest = json.load(f)
186
+ except Exception:
187
+ self.manifest = {}
188
+
189
+ config_path = DEFAULT_CONFIG_PATH
190
+ try:
191
+ with open(config_path, "r", encoding="utf-8") as f:
192
+ self.plugin_config = tomlkit.load(f)
193
+ except Exception as e:
194
+ logger.error(f"加载 A_Memorix 配置失败: {e}")
195
+ return False
196
+
197
+ try:
198
+ await self._init_stores()
199
+ except Exception as e:
200
+ logger.error(f"初始化存储失败: {e}")
201
+ return False
202
+
203
+ return True
204
+
205
+ async def _init_stores(self):
206
+ # 使用与主运行时一致的存储初始化流程。
207
+ self.embedding_manager = create_embedding_api_adapter(
208
+ batch_size=self.plugin_config.get("embedding", {}).get("batch_size", 32),
209
+ default_dimension=self.plugin_config.get("embedding", {}).get("dimension", 384),
210
+ model_name=self.plugin_config.get("embedding", {}).get("model_name", "auto"),
211
+ dimension_request_mode=self.plugin_config.get("embedding", {}).get("dimension_request_mode", "explicit"),
212
+ retry_config=self.plugin_config.get("embedding", {}).get("retry", {}),
213
+ )
214
+ try:
215
+ dim = await self.embedding_manager._detect_dimension()
216
+ except Exception:
217
+ dim = self.embedding_manager.default_dimension
218
+
219
+ q_type_str = str(self.plugin_config.get("embedding", {}).get("quantization_type", "int8") or "int8").lower()
220
+ # QuantizationType 通过 storage_module 延迟获取,避免脚本启动阶段提前导入。
221
+ QuantizationType = storage_module.QuantizationType
222
+ if q_type_str != "int8":
223
+ raise ValueError(
224
+ "embedding.quantization_type 在 vNext 仅允许 int8(SQ8)。"
225
+ " 请先执行 scripts/release_vnext_migrate.py migrate。"
226
+ )
227
+
228
+ self.vector_store = VectorStore(
229
+ dimension=dim, quantization_type=QuantizationType.INT8, data_dir=self.data_dir / "vectors"
230
+ )
231
+
232
+ SparseMatrixFormat = storage_module.SparseMatrixFormat
233
+ m_fmt_str = self.plugin_config.get("graph", {}).get("sparse_matrix_format", "csr")
234
+ m_map = {"csr": SparseMatrixFormat.CSR, "csc": SparseMatrixFormat.CSC}
235
+
236
+ self.graph_store = GraphStore(
237
+ matrix_format=m_map.get(m_fmt_str, SparseMatrixFormat.CSR), data_dir=self.data_dir / "graph"
238
+ )
239
+
240
+ self.metadata_store = MetadataStore(data_dir=self.data_dir / "metadata")
241
+ self.metadata_store.connect()
242
+
243
+ if RelationWriteService is not None:
244
+ self.relation_write_service = RelationWriteService(
245
+ metadata_store=self.metadata_store,
246
+ graph_store=self.graph_store,
247
+ vector_store=self.vector_store,
248
+ embedding_manager=self.embedding_manager,
249
+ )
250
+
251
+ if self.vector_store.has_data():
252
+ self.vector_store.load()
253
+ if self.graph_store.has_data():
254
+ self.graph_store.load()
255
+
256
+ def _should_write_relation_vectors(self) -> bool:
257
+ retrieval_cfg = self.plugin_config.get("retrieval", {})
258
+ if not isinstance(retrieval_cfg, dict):
259
+ return False
260
+ rv_cfg = retrieval_cfg.get("relation_vectorization", {})
261
+ if not isinstance(rv_cfg, dict):
262
+ return False
263
+ return bool(rv_cfg.get("enabled", False)) and bool(rv_cfg.get("write_on_import", True))
264
+
265
+ def load_file(self, file_path: Path) -> str:
266
+ with open(file_path, "r", encoding="utf-8") as f:
267
+ return f.read()
268
+
269
+ def get_file_hash(self, content: str) -> str:
270
+ return hashlib.md5(content.encode("utf-8")).hexdigest()
271
+
272
+ def _parse_reference_time(self, value: Optional[str]) -> datetime:
273
+ """解析 chat_log 模式的参考时间(用于相对时间语义解析)。"""
274
+ if not value:
275
+ return datetime.now()
276
+ formats = [
277
+ "%Y/%m/%d %H:%M:%S",
278
+ "%Y/%m/%d %H:%M",
279
+ "%Y-%m-%d %H:%M:%S",
280
+ "%Y-%m-%d %H:%M",
281
+ "%Y/%m/%d",
282
+ "%Y-%m-%d",
283
+ ]
284
+ text = str(value).strip()
285
+ for fmt in formats:
286
+ try:
287
+ return datetime.strptime(text, fmt)
288
+ except ValueError:
289
+ continue
290
+ logger.warning(f"无法解析 chat_reference_time={value},将回退为当前本地时间")
291
+ return datetime.now()
292
+
293
+ async def _extract_chat_time_meta_with_llm(
294
+ self,
295
+ text: str,
296
+ *,
297
+ resolved_model: Any,
298
+ ) -> Optional[Dict[str, Any]]:
299
+ """
300
+ 使用 LLM 从聊天文本语义中抽取时间信息。
301
+ 支持将相对时间表达转换为绝对时间。
302
+ """
303
+ if not text.strip():
304
+ return None
305
+
306
+ reference_now = self.chat_reference_dt.strftime("%Y/%m/%d %H:%M")
307
+ prompt = f"""You are a time extraction engine for chat logs.
308
+ Extract temporal information from the following chat paragraph.
309
+
310
+ Rules:
311
+ 1. Use semantic understanding, not regex matching.
312
+ 2. Convert relative expressions (e.g., yesterday evening, last Friday morning) to absolute local datetime using reference_now.
313
+ 3. If a time span exists, return event_time_start/event_time_end.
314
+ 4. If only one point in time exists, return event_time.
315
+ 5. If no reliable time can be inferred, return all time fields as null.
316
+ 6. Output ONLY valid JSON. No markdown, no explanation.
317
+
318
+ reference_now: {reference_now}
319
+ timezone: local system timezone
320
+
321
+ Allowed output formats for time values:
322
+ - "YYYY/MM/DD"
323
+ - "YYYY/MM/DD HH:mm"
324
+
325
+ JSON schema:
326
+ {{
327
+ "event_time": null,
328
+ "event_time_start": null,
329
+ "event_time_end": null,
330
+ "time_range": null,
331
+ "time_granularity": "day",
332
+ "time_confidence": 0.0
333
+ }}
334
+
335
+ Chat paragraph:
336
+ \"\"\"{text}\"\"\"
337
+ """
338
+ try:
339
+ result = await self._llm_call(prompt, resolved_model)
340
+ except Exception as e:
341
+ logger.warning(f"chat_log 时间语义抽取失败: {e}")
342
+ return None
343
+
344
+ if not isinstance(result, dict):
345
+ return None
346
+
347
+ raw_time_meta = {
348
+ "event_time": result.get("event_time"),
349
+ "event_time_start": result.get("event_time_start"),
350
+ "event_time_end": result.get("event_time_end"),
351
+ "time_range": result.get("time_range"),
352
+ "time_granularity": result.get("time_granularity"),
353
+ "time_confidence": result.get("time_confidence"),
354
+ }
355
+ try:
356
+ normalized = normalize_time_meta(raw_time_meta)
357
+ except Exception as e:
358
+ logger.warning(f"chat_log 时间语义抽取结果不可用,已忽略: {e}")
359
+ return None
360
+
361
+ has_effective_time = any(key in normalized for key in ("event_time", "event_time_start", "event_time_end"))
362
+ if not has_effective_time:
363
+ return None
364
+
365
+ return normalized
366
+
367
+ def _determine_strategy(self, filename: str, content: str) -> BaseStrategy:
368
+ """第一层:全局导入策略路由。"""
369
+ strategy = select_import_strategy(
370
+ content,
371
+ override=self.target_type,
372
+ chat_log=self.chat_log,
373
+ )
374
+ if self.chat_log:
375
+ logger.info(f"chat_log 模式: {filename} 强制使用 NarrativeStrategy")
376
+ elif strategy == ImportStrategy.QUOTE:
377
+ logger.info(f"Auto-detected Quote/Lyric type for {filename}")
378
+
379
+ if strategy == ImportStrategy.FACTUAL:
380
+ return FactualStrategy(filename)
381
+ if strategy == ImportStrategy.QUOTE:
382
+ return QuoteStrategy(filename)
383
+ return NarrativeStrategy(filename)
384
+
385
+ def _chunk_rescue(self, chunk: ProcessedChunk, filename: str) -> Optional[BaseStrategy]:
386
+ """第二层:分块级策略修正。"""
387
+ # Quote 分块已经满足目标格式,不再重复修正。
388
+ if chunk.type == StratKnowledgeType.QUOTE:
389
+ return None
390
+
391
+ if looks_like_quote_text(chunk.chunk.text):
392
+ logger.info(f" > Rescuing chunk {chunk.chunk.index} as Quote")
393
+ return QuoteStrategy(filename)
394
+
395
+ return None
396
+
397
+ async def process_and_import(self):
398
+ if not await self.initialize():
399
+ return
400
+
401
+ files = list(self.raw_dir.glob("*.txt"))
402
+ logger.info(f"扫描到 {len(files)} 个文件 in {self.raw_dir}")
403
+
404
+ if not files:
405
+ return
406
+
407
+ tasks = []
408
+ for file_path in files:
409
+ tasks.append(asyncio.create_task(self._process_single_file(file_path)))
410
+
411
+ results = await asyncio.gather(*tasks, return_exceptions=True)
412
+
413
+ success_count = sum(1 for r in results if r is True)
414
+ logger.info(f"本次主处理完成,共成功处理 {success_count}/{len(files)} 个文件")
415
+
416
+ if self.vector_store:
417
+ self.vector_store.save()
418
+ if self.graph_store:
419
+ self.graph_store.save()
420
+
421
+ async def _process_single_file(self, file_path: Path) -> bool:
422
+ filename = file_path.name
423
+ async with self.semaphore:
424
+ try:
425
+ content = self.load_file(file_path)
426
+ file_hash = self.get_file_hash(content)
427
+
428
+ if not self.force and filename in self.manifest:
429
+ record = self.manifest[filename]
430
+ if record.get("hash") == file_hash and record.get("imported"):
431
+ logger.info(f"跳过已导入文件: {filename}")
432
+ return False
433
+
434
+ logger.info(f">>> 开始处理: {filename}")
435
+
436
+ # 1. 选择导入策略
437
+ strategy = self._determine_strategy(filename, content)
438
+ logger.info(f" 策略: {strategy.__class__.__name__}")
439
+
440
+ # 2. 按策略分块
441
+ initial_chunks = strategy.split(content)
442
+ logger.info(f" 初步分块: {len(initial_chunks)}")
443
+
444
+ processed_data = {"paragraphs": [], "entities": [], "relations": []}
445
+
446
+ # 3. 逐块抽取
447
+ resolved_model = await self._select_model()
448
+
449
+ for i, chunk in enumerate(initial_chunks):
450
+ current_strategy = strategy
451
+ # 第二层:分块级策略修正
452
+ rescue_strategy = self._chunk_rescue(chunk, filename)
453
+ if rescue_strategy:
454
+ # 修正后不再重新切分,直接把当前分块作为完整 Quote 交给新策略。
455
+ chunk.type = StratKnowledgeType.QUOTE
456
+ chunk.flags.verbatim = True
457
+ chunk.flags.requires_llm = False # Quote 通常不需要 LLM
458
+ current_strategy = rescue_strategy
459
+
460
+ # 抽取内容
461
+ if chunk.flags.requires_llm:
462
+ result_chunk = await current_strategy.extract(
463
+ chunk,
464
+ lambda p: self._llm_call(p, resolved_model),
465
+ )
466
+ else:
467
+ # QuoteStrategy 通常透传文本,并保留统一抽取接口。
468
+ result_chunk = await current_strategy.extract(chunk)
469
+
470
+ time_meta = None
471
+ if self.chat_log:
472
+ time_meta = await self._extract_chat_time_meta_with_llm(
473
+ result_chunk.chunk.text,
474
+ resolved_model=resolved_model,
475
+ )
476
+
477
+ # 归一化结果
478
+ self._normalize_and_aggregate(
479
+ result_chunk,
480
+ processed_data,
481
+ time_meta=time_meta,
482
+ )
483
+
484
+ logger.info(f" 已处理块 {i + 1}/{len(initial_chunks)}")
485
+
486
+ # 4. 保存 JSON
487
+ json_path = self.processed_dir / f"{file_path.stem}.json"
488
+ with open(json_path, "w", encoding="utf-8") as f:
489
+ json.dump(processed_data, f, ensure_ascii=False, indent=2)
490
+
491
+ # 5. 导入数据库
492
+ async with self.storage_lock:
493
+ await self._import_to_db(processed_data)
494
+
495
+ self.manifest[filename] = {"hash": file_hash, "timestamp": time.time(), "imported": True}
496
+ self._save_manifest()
497
+ self.vector_store.save()
498
+ self.graph_store.save()
499
+ logger.info(f"✅ 文件 {filename} 处理并导入完成")
500
+ return True
501
+
502
+ except Exception as e:
503
+ logger.error(f"❌ 处理失败 {filename}: {e}")
504
+ import traceback
505
+
506
+ traceback.print_exc()
507
+ return False
508
+
509
+ def _normalize_and_aggregate(
510
+ self,
511
+ chunk: ProcessedChunk,
512
+ all_data: Dict,
513
+ time_meta: Optional[Dict[str, Any]] = None,
514
+ ):
515
+ """将策略特有结果转换为存储层统一格式。"""
516
+ # 通用字段
517
+ para_item = {
518
+ "content": chunk.chunk.text,
519
+ "source": chunk.source.file,
520
+ "knowledge_type": resolve_stored_knowledge_type(
521
+ chunk.type.value,
522
+ content=chunk.chunk.text,
523
+ ).value,
524
+ "entities": [],
525
+ "relations": [],
526
+ }
527
+
528
+ data = chunk.data
529
+
530
+ # 1. 事实三元组(Factual)
531
+ if "triples" in data:
532
+ for t in data["triples"]:
533
+ para_item["relations"].append(
534
+ {"subject": t.get("subject"), "predicate": t.get("predicate"), "object": t.get("object")}
535
+ )
536
+ # 自动收集三元组中的实体。
537
+ para_item["entities"].extend([t.get("subject"), t.get("object")])
538
+
539
+ # 2. 事件与关系(Narrative)
540
+ if "events" in data:
541
+ # 当前将事件作为实体写入,保留既有检索语义。
542
+ para_item["entities"].extend(data["events"])
543
+
544
+ if "relations" in data: # Narrative 同时输出关系列表
545
+ para_item["relations"].extend(data["relations"])
546
+ for r in data["relations"]:
547
+ para_item["entities"].extend([r.get("subject"), r.get("object")])
548
+
549
+ # 3. 逐字实体(Quote)
550
+ if "verbatim_entities" in data:
551
+ para_item["entities"].extend(data["verbatim_entities"])
552
+
553
+ # 在每个段落内去重。
554
+ para_item["entities"] = list(set([e for e in para_item["entities"] if e]))
555
+
556
+ if time_meta:
557
+ para_item["time_meta"] = time_meta
558
+
559
+ all_data["paragraphs"].append(para_item)
560
+ all_data["entities"].extend(para_item["entities"])
561
+ if "relations" in para_item:
562
+ all_data["relations"].extend(para_item["relations"])
563
+
564
+ @retry(
565
+ retry=retry_if_exception_type((LLMGenerationError, json.JSONDecodeError)),
566
+ stop=stop_after_attempt(3),
567
+ wait=wait_exponential(multiplier=1, min=2, max=10),
568
+ before_sleep=_log_before_retry,
569
+ )
570
+ async def _llm_call(self, prompt: str, resolved_model: Any) -> Dict:
571
+ """统一的 LLM 调用入口。"""
572
+ result = await generate_with_resolved_model(
573
+ resolved_model,
574
+ request_type="Script.ProcessKnowledge",
575
+ prompt=prompt,
576
+ temperature=getattr(resolved_model.task_config, "temperature", None),
577
+ max_tokens=getattr(resolved_model.task_config, "max_tokens", None),
578
+ )
579
+ success = bool(result.success)
580
+ response = str(result.completion.response or "")
581
+ if success:
582
+ txt = response.strip()
583
+ if "```" in txt:
584
+ txt = txt.split("```json")[-1].split("```")[0].strip()
585
+ try:
586
+ return json.loads(txt)
587
+ except json.JSONDecodeError:
588
+ # 回退解析:截取首个左花括号到最后一个右花括号。
589
+ start = txt.find("{")
590
+ end = txt.rfind("}")
591
+ if start != -1 and end != -1:
592
+ return json.loads(txt[start : end + 1])
593
+ raise
594
+ else:
595
+ raise LLMGenerationError("LLM generation failed")
596
+
597
+ async def _select_model(self) -> "ResolvedLLMModel":
598
+ models = get_text_generation_model_tasks(llm_api)
599
+ if not models:
600
+ raise ValueError("No LLM models")
601
+
602
+ config_model = str(self.plugin_config.get("advanced", {}).get("extraction_model", "auto") or "auto").strip()
603
+ if config_model != "auto":
604
+ task_name, task_config, selected_model_name = resolve_text_generation_model_selector(models, config_model)
605
+ if task_name and task_config:
606
+ return ResolvedLLMModel(
607
+ task_name=task_name,
608
+ task_config=task_config,
609
+ selected_model_name=selected_model_name,
610
+ )
611
+ logger.warning(f"advanced.extraction_model={config_model!r} 不可用于文本生成,已回退自动选择")
612
+
613
+ task_name, task_config = pick_text_generation_task(
614
+ models,
615
+ preferred=("memory", "utils", "lpmm_entity_extract", "lpmm_rdf_build", "replyer", "planner"),
616
+ )
617
+ if task_name and task_config:
618
+ return ResolvedLLMModel(task_name=task_name, task_config=task_config)
619
+ raise ValueError("No LLM models")
620
+
621
+ # 复用现有写入方法。
622
+ async def _add_entity_with_vector(self, name: str, source_paragraph: Optional[str] = None) -> str:
623
+ # 最后一道守卫:防止旁路把 hash 写入实体名
624
+ entity_name = str(name or "").strip()
625
+ if not entity_name:
626
+ return ""
627
+ if is_probable_hash_token(entity_name):
628
+ logger.warning(f"脚本导入跳过疑似哈希实体: {entity_name[:32]}")
629
+ return ""
630
+
631
+ hash_value = self.metadata_store.add_entity(entity_name, source_paragraph=source_paragraph)
632
+ self.graph_store.add_nodes([entity_name])
633
+ try:
634
+ emb = await self.embedding_manager.encode(entity_name)
635
+ self.vector_store.add(emb.reshape(1, -1), [hash_value])
636
+ except (RuntimeError, ValueError) as exc:
637
+ logger.warning(f"实体向量写入失败: entity={entity_name}, hash={hash_value}, error={exc}")
638
+ return hash_value
639
+
640
+ async def import_json_data(self, data: Dict, filename: str = "script_import", progress_callback=None):
641
+ """预处理 JSON 载荷的公开导入入口。"""
642
+ if not self.storage_lock:
643
+ raise RuntimeError("Importer is not initialized. Call initialize() first.")
644
+
645
+ async with self.storage_lock:
646
+ await self._import_to_db(data, progress_callback=progress_callback)
647
+ self.manifest[filename] = {
648
+ "hash": self.get_file_hash(json.dumps(data, ensure_ascii=False, sort_keys=True)),
649
+ "timestamp": time.time(),
650
+ "imported": True,
651
+ }
652
+ self._save_manifest()
653
+ self.vector_store.save()
654
+ self.graph_store.save()
655
+
656
+ async def _import_to_db(self, data: Dict, progress_callback=None):
657
+ # 沿用统一导入逻辑,并逐项记录非法数据。
658
+ warning_count = 0
659
+
660
+ def append_warning(message: str) -> None:
661
+ nonlocal warning_count
662
+ warning_count += 1
663
+ logger.warning(message)
664
+
665
+ with self.graph_store.batch_update():
666
+ for paragraph_index, item in enumerate(data.get("paragraphs", [])):
667
+ try:
668
+ paragraph = normalize_paragraph_import_item(
669
+ item,
670
+ default_source="script",
671
+ )
672
+ except ImportPayloadValidationError as exc:
673
+ append_warning(f"脚本导入跳过段落[{paragraph_index}]:{exc} (code={exc.code})")
674
+ if progress_callback:
675
+ progress_callback(1)
676
+ continue
677
+
678
+ content = paragraph["content"]
679
+ if is_probable_hash_token(content):
680
+ append_warning(f"脚本导入跳过段落[{paragraph_index}]:段落内容疑似哈希值")
681
+ if progress_callback:
682
+ progress_callback(1)
683
+ continue
684
+
685
+ source = paragraph["source"]
686
+ k_type_val = paragraph["knowledge_type"]
687
+
688
+ h_val = self.metadata_store.add_paragraph(
689
+ content=content,
690
+ source=source,
691
+ knowledge_type=k_type_val,
692
+ time_meta=paragraph["time_meta"],
693
+ )
694
+
695
+ if h_val not in self.vector_store:
696
+ try:
697
+ emb = await self.embedding_manager.encode(content)
698
+ self.vector_store.add(emb.reshape(1, -1), [h_val])
699
+ except Exception as e:
700
+ logger.error(f" Vector fail: {e}")
701
+
702
+ para_entities = paragraph["entities"]
703
+ for entity in para_entities:
704
+ name = normalize_entity_import_item(entity)
705
+ if not name:
706
+ append_warning(f"脚本导入跳过段落[{paragraph_index}]中的实体:无效名称或疑似哈希值")
707
+ continue
708
+ await self._add_entity_with_vector(name, source_paragraph=h_val)
709
+
710
+ para_relations = paragraph["relations"]
711
+ for rel in para_relations:
712
+ normalized_relation = normalize_relation_import_item(rel)
713
+ if normalized_relation is None:
714
+ append_warning(f"脚本导入跳过段落[{paragraph_index}]中的关系:字段无效或疑似哈希值")
715
+ continue
716
+
717
+ s = normalized_relation["subject"]
718
+ p = normalized_relation["predicate"]
719
+ o = normalized_relation["object"]
720
+ await self._add_entity_with_vector(s, source_paragraph=h_val)
721
+ await self._add_entity_with_vector(o, source_paragraph=h_val)
722
+
723
+ confidence_value = rel.get("confidence", 1.0) if isinstance(rel, dict) else 1.0
724
+ confidence = float(1.0 if confidence_value is None else confidence_value)
725
+ rel_meta = rel.get("metadata", {}) if isinstance(rel, dict) else {}
726
+ write_vector = self._should_write_relation_vectors()
727
+ if self.relation_write_service is not None:
728
+ await self.relation_write_service.upsert_relation_with_vector(
729
+ subject=s,
730
+ predicate=p,
731
+ obj=o,
732
+ confidence=confidence,
733
+ source_paragraph=h_val,
734
+ metadata=rel_meta if isinstance(rel_meta, dict) else {},
735
+ write_vector=write_vector,
736
+ )
737
+ else:
738
+ rel_hash = self.metadata_store.add_relation(
739
+ s,
740
+ p,
741
+ o,
742
+ confidence=confidence,
743
+ source_paragraph=h_val,
744
+ metadata=rel_meta if isinstance(rel_meta, dict) else {},
745
+ )
746
+ self.graph_store.add_edges([(s, o)], relation_hashes=[rel_hash])
747
+
748
+ if progress_callback:
749
+ progress_callback(1)
750
+
751
+ for entity_index, raw_entity in enumerate(data.get("entities", []) or []):
752
+ entity_name = normalize_entity_import_item(raw_entity)
753
+ if not entity_name:
754
+ append_warning(f"脚本导入跳过顶层实体[{entity_index}]:无效名称或疑似哈希值")
755
+ continue
756
+ await self._add_entity_with_vector(entity_name)
757
+
758
+ for relation_index, raw_relation in enumerate(data.get("relations", []) or []):
759
+ relation = normalize_relation_import_item(raw_relation)
760
+ if relation is None:
761
+ append_warning(f"脚本导入跳过顶层关系[{relation_index}]:字段无效或疑似哈希值")
762
+ continue
763
+
764
+ subject = relation["subject"]
765
+ predicate = relation["predicate"]
766
+ obj = relation["object"]
767
+ await self._add_entity_with_vector(subject)
768
+ await self._add_entity_with_vector(obj)
769
+
770
+ confidence_value = raw_relation.get("confidence", 1.0) if isinstance(raw_relation, dict) else 1.0
771
+ confidence = float(1.0 if confidence_value is None else confidence_value)
772
+ rel_meta = raw_relation.get("metadata", {}) if isinstance(raw_relation, dict) else {}
773
+ write_vector = self._should_write_relation_vectors()
774
+ if self.relation_write_service is not None:
775
+ await self.relation_write_service.upsert_relation_with_vector(
776
+ subject=subject,
777
+ predicate=predicate,
778
+ obj=obj,
779
+ confidence=confidence,
780
+ source_paragraph="",
781
+ metadata=rel_meta if isinstance(rel_meta, dict) else {},
782
+ write_vector=write_vector,
783
+ )
784
+ else:
785
+ rel_hash = self.metadata_store.add_relation(
786
+ subject,
787
+ predicate,
788
+ obj,
789
+ confidence=confidence,
790
+ source_paragraph="",
791
+ metadata=rel_meta if isinstance(rel_meta, dict) else {},
792
+ )
793
+ self.graph_store.add_edges([(subject, obj)], relation_hashes=[rel_hash])
794
+
795
+ if warning_count > 0:
796
+ logger.warning(f"脚本导入完成,跳过异常项 {warning_count} 条")
797
+
798
+ async def close(self):
799
+ if self.metadata_store:
800
+ self.metadata_store.close()
801
+
802
+ def _save_manifest(self):
803
+ self.manifest_path.parent.mkdir(parents=True, exist_ok=True)
804
+ with open(self.manifest_path, "w", encoding="utf-8") as f:
805
+ json.dump(self.manifest, f, ensure_ascii=False, indent=2)
806
+
807
+
808
+ async def main():
809
+ parser = _build_arg_parser()
810
+ args = parser.parse_args()
811
+
812
+ if not global_config:
813
+ return
814
+
815
+ importer = AutoImporter(
816
+ force=args.force,
817
+ clear_manifest=args.clear_manifest,
818
+ target_type=args.type,
819
+ concurrency=args.concurrency,
820
+ chat_log=args.chat_log,
821
+ chat_reference_time=args.chat_reference_time,
822
+ data_dir=Path(args.data_dir),
823
+ )
824
+ await importer.process_and_import()
825
+ await importer.close()
826
+
827
+
828
+ if __name__ == "__main__":
829
+ if sys.platform == "win32":
830
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
831
+ asyncio.run(main())