MemoryOS 2.0.3__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 (315) hide show
  1. memoryos-2.0.3.dist-info/METADATA +418 -0
  2. memoryos-2.0.3.dist-info/RECORD +315 -0
  3. memoryos-2.0.3.dist-info/WHEEL +4 -0
  4. memoryos-2.0.3.dist-info/entry_points.txt +3 -0
  5. memoryos-2.0.3.dist-info/licenses/LICENSE +201 -0
  6. memos/__init__.py +20 -0
  7. memos/api/client.py +571 -0
  8. memos/api/config.py +1018 -0
  9. memos/api/context/dependencies.py +50 -0
  10. memos/api/exceptions.py +53 -0
  11. memos/api/handlers/__init__.py +62 -0
  12. memos/api/handlers/add_handler.py +158 -0
  13. memos/api/handlers/base_handler.py +194 -0
  14. memos/api/handlers/chat_handler.py +1401 -0
  15. memos/api/handlers/component_init.py +388 -0
  16. memos/api/handlers/config_builders.py +190 -0
  17. memos/api/handlers/feedback_handler.py +93 -0
  18. memos/api/handlers/formatters_handler.py +237 -0
  19. memos/api/handlers/memory_handler.py +316 -0
  20. memos/api/handlers/scheduler_handler.py +497 -0
  21. memos/api/handlers/search_handler.py +222 -0
  22. memos/api/handlers/suggestion_handler.py +117 -0
  23. memos/api/mcp_serve.py +614 -0
  24. memos/api/middleware/request_context.py +101 -0
  25. memos/api/product_api.py +38 -0
  26. memos/api/product_models.py +1206 -0
  27. memos/api/routers/__init__.py +1 -0
  28. memos/api/routers/product_router.py +477 -0
  29. memos/api/routers/server_router.py +394 -0
  30. memos/api/server_api.py +44 -0
  31. memos/api/start_api.py +433 -0
  32. memos/chunkers/__init__.py +4 -0
  33. memos/chunkers/base.py +24 -0
  34. memos/chunkers/charactertext_chunker.py +41 -0
  35. memos/chunkers/factory.py +24 -0
  36. memos/chunkers/markdown_chunker.py +62 -0
  37. memos/chunkers/sentence_chunker.py +54 -0
  38. memos/chunkers/simple_chunker.py +50 -0
  39. memos/cli.py +113 -0
  40. memos/configs/__init__.py +0 -0
  41. memos/configs/base.py +82 -0
  42. memos/configs/chunker.py +59 -0
  43. memos/configs/embedder.py +88 -0
  44. memos/configs/graph_db.py +236 -0
  45. memos/configs/internet_retriever.py +100 -0
  46. memos/configs/llm.py +151 -0
  47. memos/configs/mem_agent.py +54 -0
  48. memos/configs/mem_chat.py +81 -0
  49. memos/configs/mem_cube.py +105 -0
  50. memos/configs/mem_os.py +83 -0
  51. memos/configs/mem_reader.py +91 -0
  52. memos/configs/mem_scheduler.py +385 -0
  53. memos/configs/mem_user.py +70 -0
  54. memos/configs/memory.py +324 -0
  55. memos/configs/parser.py +38 -0
  56. memos/configs/reranker.py +18 -0
  57. memos/configs/utils.py +8 -0
  58. memos/configs/vec_db.py +80 -0
  59. memos/context/context.py +355 -0
  60. memos/dependency.py +52 -0
  61. memos/deprecation.py +262 -0
  62. memos/embedders/__init__.py +0 -0
  63. memos/embedders/ark.py +95 -0
  64. memos/embedders/base.py +106 -0
  65. memos/embedders/factory.py +29 -0
  66. memos/embedders/ollama.py +77 -0
  67. memos/embedders/sentence_transformer.py +49 -0
  68. memos/embedders/universal_api.py +51 -0
  69. memos/exceptions.py +30 -0
  70. memos/graph_dbs/__init__.py +0 -0
  71. memos/graph_dbs/base.py +274 -0
  72. memos/graph_dbs/factory.py +27 -0
  73. memos/graph_dbs/item.py +46 -0
  74. memos/graph_dbs/nebular.py +1794 -0
  75. memos/graph_dbs/neo4j.py +1942 -0
  76. memos/graph_dbs/neo4j_community.py +1058 -0
  77. memos/graph_dbs/polardb.py +5446 -0
  78. memos/hello_world.py +97 -0
  79. memos/llms/__init__.py +0 -0
  80. memos/llms/base.py +25 -0
  81. memos/llms/deepseek.py +13 -0
  82. memos/llms/factory.py +38 -0
  83. memos/llms/hf.py +443 -0
  84. memos/llms/hf_singleton.py +114 -0
  85. memos/llms/ollama.py +135 -0
  86. memos/llms/openai.py +222 -0
  87. memos/llms/openai_new.py +198 -0
  88. memos/llms/qwen.py +13 -0
  89. memos/llms/utils.py +14 -0
  90. memos/llms/vllm.py +218 -0
  91. memos/log.py +237 -0
  92. memos/mem_agent/base.py +19 -0
  93. memos/mem_agent/deepsearch_agent.py +391 -0
  94. memos/mem_agent/factory.py +36 -0
  95. memos/mem_chat/__init__.py +0 -0
  96. memos/mem_chat/base.py +30 -0
  97. memos/mem_chat/factory.py +21 -0
  98. memos/mem_chat/simple.py +200 -0
  99. memos/mem_cube/__init__.py +0 -0
  100. memos/mem_cube/base.py +30 -0
  101. memos/mem_cube/general.py +240 -0
  102. memos/mem_cube/navie.py +172 -0
  103. memos/mem_cube/utils.py +169 -0
  104. memos/mem_feedback/base.py +15 -0
  105. memos/mem_feedback/feedback.py +1192 -0
  106. memos/mem_feedback/simple_feedback.py +40 -0
  107. memos/mem_feedback/utils.py +230 -0
  108. memos/mem_os/client.py +5 -0
  109. memos/mem_os/core.py +1203 -0
  110. memos/mem_os/main.py +582 -0
  111. memos/mem_os/product.py +1608 -0
  112. memos/mem_os/product_server.py +455 -0
  113. memos/mem_os/utils/default_config.py +359 -0
  114. memos/mem_os/utils/format_utils.py +1403 -0
  115. memos/mem_os/utils/reference_utils.py +162 -0
  116. memos/mem_reader/__init__.py +0 -0
  117. memos/mem_reader/base.py +47 -0
  118. memos/mem_reader/factory.py +53 -0
  119. memos/mem_reader/memory.py +298 -0
  120. memos/mem_reader/multi_modal_struct.py +965 -0
  121. memos/mem_reader/read_multi_modal/__init__.py +43 -0
  122. memos/mem_reader/read_multi_modal/assistant_parser.py +311 -0
  123. memos/mem_reader/read_multi_modal/base.py +273 -0
  124. memos/mem_reader/read_multi_modal/file_content_parser.py +826 -0
  125. memos/mem_reader/read_multi_modal/image_parser.py +359 -0
  126. memos/mem_reader/read_multi_modal/multi_modal_parser.py +252 -0
  127. memos/mem_reader/read_multi_modal/string_parser.py +139 -0
  128. memos/mem_reader/read_multi_modal/system_parser.py +327 -0
  129. memos/mem_reader/read_multi_modal/text_content_parser.py +131 -0
  130. memos/mem_reader/read_multi_modal/tool_parser.py +210 -0
  131. memos/mem_reader/read_multi_modal/user_parser.py +218 -0
  132. memos/mem_reader/read_multi_modal/utils.py +358 -0
  133. memos/mem_reader/simple_struct.py +912 -0
  134. memos/mem_reader/strategy_struct.py +163 -0
  135. memos/mem_reader/utils.py +157 -0
  136. memos/mem_scheduler/__init__.py +0 -0
  137. memos/mem_scheduler/analyzer/__init__.py +0 -0
  138. memos/mem_scheduler/analyzer/api_analyzer.py +714 -0
  139. memos/mem_scheduler/analyzer/eval_analyzer.py +219 -0
  140. memos/mem_scheduler/analyzer/mos_for_test_scheduler.py +571 -0
  141. memos/mem_scheduler/analyzer/scheduler_for_eval.py +280 -0
  142. memos/mem_scheduler/base_scheduler.py +1319 -0
  143. memos/mem_scheduler/general_modules/__init__.py +0 -0
  144. memos/mem_scheduler/general_modules/api_misc.py +137 -0
  145. memos/mem_scheduler/general_modules/base.py +80 -0
  146. memos/mem_scheduler/general_modules/init_components_for_scheduler.py +425 -0
  147. memos/mem_scheduler/general_modules/misc.py +313 -0
  148. memos/mem_scheduler/general_modules/scheduler_logger.py +389 -0
  149. memos/mem_scheduler/general_modules/task_threads.py +315 -0
  150. memos/mem_scheduler/general_scheduler.py +1495 -0
  151. memos/mem_scheduler/memory_manage_modules/__init__.py +5 -0
  152. memos/mem_scheduler/memory_manage_modules/memory_filter.py +306 -0
  153. memos/mem_scheduler/memory_manage_modules/retriever.py +547 -0
  154. memos/mem_scheduler/monitors/__init__.py +0 -0
  155. memos/mem_scheduler/monitors/dispatcher_monitor.py +366 -0
  156. memos/mem_scheduler/monitors/general_monitor.py +394 -0
  157. memos/mem_scheduler/monitors/task_schedule_monitor.py +254 -0
  158. memos/mem_scheduler/optimized_scheduler.py +410 -0
  159. memos/mem_scheduler/orm_modules/__init__.py +0 -0
  160. memos/mem_scheduler/orm_modules/api_redis_model.py +518 -0
  161. memos/mem_scheduler/orm_modules/base_model.py +729 -0
  162. memos/mem_scheduler/orm_modules/monitor_models.py +261 -0
  163. memos/mem_scheduler/orm_modules/redis_model.py +699 -0
  164. memos/mem_scheduler/scheduler_factory.py +23 -0
  165. memos/mem_scheduler/schemas/__init__.py +0 -0
  166. memos/mem_scheduler/schemas/analyzer_schemas.py +52 -0
  167. memos/mem_scheduler/schemas/api_schemas.py +233 -0
  168. memos/mem_scheduler/schemas/general_schemas.py +55 -0
  169. memos/mem_scheduler/schemas/message_schemas.py +173 -0
  170. memos/mem_scheduler/schemas/monitor_schemas.py +406 -0
  171. memos/mem_scheduler/schemas/task_schemas.py +132 -0
  172. memos/mem_scheduler/task_schedule_modules/__init__.py +0 -0
  173. memos/mem_scheduler/task_schedule_modules/dispatcher.py +740 -0
  174. memos/mem_scheduler/task_schedule_modules/local_queue.py +247 -0
  175. memos/mem_scheduler/task_schedule_modules/orchestrator.py +74 -0
  176. memos/mem_scheduler/task_schedule_modules/redis_queue.py +1385 -0
  177. memos/mem_scheduler/task_schedule_modules/task_queue.py +162 -0
  178. memos/mem_scheduler/utils/__init__.py +0 -0
  179. memos/mem_scheduler/utils/api_utils.py +77 -0
  180. memos/mem_scheduler/utils/config_utils.py +100 -0
  181. memos/mem_scheduler/utils/db_utils.py +50 -0
  182. memos/mem_scheduler/utils/filter_utils.py +176 -0
  183. memos/mem_scheduler/utils/metrics.py +125 -0
  184. memos/mem_scheduler/utils/misc_utils.py +290 -0
  185. memos/mem_scheduler/utils/monitor_event_utils.py +67 -0
  186. memos/mem_scheduler/utils/status_tracker.py +229 -0
  187. memos/mem_scheduler/webservice_modules/__init__.py +0 -0
  188. memos/mem_scheduler/webservice_modules/rabbitmq_service.py +485 -0
  189. memos/mem_scheduler/webservice_modules/redis_service.py +380 -0
  190. memos/mem_user/factory.py +94 -0
  191. memos/mem_user/mysql_persistent_user_manager.py +271 -0
  192. memos/mem_user/mysql_user_manager.py +502 -0
  193. memos/mem_user/persistent_factory.py +98 -0
  194. memos/mem_user/persistent_user_manager.py +260 -0
  195. memos/mem_user/redis_persistent_user_manager.py +225 -0
  196. memos/mem_user/user_manager.py +488 -0
  197. memos/memories/__init__.py +0 -0
  198. memos/memories/activation/__init__.py +0 -0
  199. memos/memories/activation/base.py +42 -0
  200. memos/memories/activation/item.py +56 -0
  201. memos/memories/activation/kv.py +292 -0
  202. memos/memories/activation/vllmkv.py +219 -0
  203. memos/memories/base.py +19 -0
  204. memos/memories/factory.py +42 -0
  205. memos/memories/parametric/__init__.py +0 -0
  206. memos/memories/parametric/base.py +19 -0
  207. memos/memories/parametric/item.py +11 -0
  208. memos/memories/parametric/lora.py +41 -0
  209. memos/memories/textual/__init__.py +0 -0
  210. memos/memories/textual/base.py +92 -0
  211. memos/memories/textual/general.py +236 -0
  212. memos/memories/textual/item.py +304 -0
  213. memos/memories/textual/naive.py +187 -0
  214. memos/memories/textual/prefer_text_memory/__init__.py +0 -0
  215. memos/memories/textual/prefer_text_memory/adder.py +504 -0
  216. memos/memories/textual/prefer_text_memory/config.py +106 -0
  217. memos/memories/textual/prefer_text_memory/extractor.py +221 -0
  218. memos/memories/textual/prefer_text_memory/factory.py +85 -0
  219. memos/memories/textual/prefer_text_memory/retrievers.py +177 -0
  220. memos/memories/textual/prefer_text_memory/spliter.py +132 -0
  221. memos/memories/textual/prefer_text_memory/utils.py +93 -0
  222. memos/memories/textual/preference.py +344 -0
  223. memos/memories/textual/simple_preference.py +161 -0
  224. memos/memories/textual/simple_tree.py +69 -0
  225. memos/memories/textual/tree.py +459 -0
  226. memos/memories/textual/tree_text_memory/__init__.py +0 -0
  227. memos/memories/textual/tree_text_memory/organize/__init__.py +0 -0
  228. memos/memories/textual/tree_text_memory/organize/handler.py +184 -0
  229. memos/memories/textual/tree_text_memory/organize/manager.py +518 -0
  230. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +238 -0
  231. memos/memories/textual/tree_text_memory/organize/reorganizer.py +622 -0
  232. memos/memories/textual/tree_text_memory/retrieve/__init__.py +0 -0
  233. memos/memories/textual/tree_text_memory/retrieve/advanced_searcher.py +364 -0
  234. memos/memories/textual/tree_text_memory/retrieve/bm25_util.py +186 -0
  235. memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +419 -0
  236. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +270 -0
  237. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +102 -0
  238. memos/memories/textual/tree_text_memory/retrieve/reasoner.py +61 -0
  239. memos/memories/textual/tree_text_memory/retrieve/recall.py +497 -0
  240. memos/memories/textual/tree_text_memory/retrieve/reranker.py +111 -0
  241. memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +16 -0
  242. memos/memories/textual/tree_text_memory/retrieve/retrieve_utils.py +472 -0
  243. memos/memories/textual/tree_text_memory/retrieve/searcher.py +848 -0
  244. memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +135 -0
  245. memos/memories/textual/tree_text_memory/retrieve/utils.py +54 -0
  246. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +387 -0
  247. memos/memos_tools/dinding_report_bot.py +453 -0
  248. memos/memos_tools/lockfree_dict.py +120 -0
  249. memos/memos_tools/notification_service.py +44 -0
  250. memos/memos_tools/notification_utils.py +142 -0
  251. memos/memos_tools/singleton.py +174 -0
  252. memos/memos_tools/thread_safe_dict.py +310 -0
  253. memos/memos_tools/thread_safe_dict_segment.py +382 -0
  254. memos/multi_mem_cube/__init__.py +0 -0
  255. memos/multi_mem_cube/composite_cube.py +86 -0
  256. memos/multi_mem_cube/single_cube.py +874 -0
  257. memos/multi_mem_cube/views.py +54 -0
  258. memos/parsers/__init__.py +0 -0
  259. memos/parsers/base.py +15 -0
  260. memos/parsers/factory.py +21 -0
  261. memos/parsers/markitdown.py +28 -0
  262. memos/reranker/__init__.py +4 -0
  263. memos/reranker/base.py +25 -0
  264. memos/reranker/concat.py +103 -0
  265. memos/reranker/cosine_local.py +102 -0
  266. memos/reranker/factory.py +72 -0
  267. memos/reranker/http_bge.py +324 -0
  268. memos/reranker/http_bge_strategy.py +327 -0
  269. memos/reranker/noop.py +19 -0
  270. memos/reranker/strategies/__init__.py +4 -0
  271. memos/reranker/strategies/base.py +61 -0
  272. memos/reranker/strategies/concat_background.py +94 -0
  273. memos/reranker/strategies/concat_docsource.py +110 -0
  274. memos/reranker/strategies/dialogue_common.py +109 -0
  275. memos/reranker/strategies/factory.py +31 -0
  276. memos/reranker/strategies/single_turn.py +107 -0
  277. memos/reranker/strategies/singleturn_outmem.py +98 -0
  278. memos/settings.py +10 -0
  279. memos/templates/__init__.py +0 -0
  280. memos/templates/advanced_search_prompts.py +211 -0
  281. memos/templates/cloud_service_prompt.py +107 -0
  282. memos/templates/instruction_completion.py +66 -0
  283. memos/templates/mem_agent_prompts.py +85 -0
  284. memos/templates/mem_feedback_prompts.py +822 -0
  285. memos/templates/mem_reader_prompts.py +1096 -0
  286. memos/templates/mem_reader_strategy_prompts.py +238 -0
  287. memos/templates/mem_scheduler_prompts.py +626 -0
  288. memos/templates/mem_search_prompts.py +93 -0
  289. memos/templates/mos_prompts.py +403 -0
  290. memos/templates/prefer_complete_prompt.py +735 -0
  291. memos/templates/tool_mem_prompts.py +139 -0
  292. memos/templates/tree_reorganize_prompts.py +230 -0
  293. memos/types/__init__.py +34 -0
  294. memos/types/general_types.py +151 -0
  295. memos/types/openai_chat_completion_types/__init__.py +15 -0
  296. memos/types/openai_chat_completion_types/chat_completion_assistant_message_param.py +56 -0
  297. memos/types/openai_chat_completion_types/chat_completion_content_part_image_param.py +27 -0
  298. memos/types/openai_chat_completion_types/chat_completion_content_part_input_audio_param.py +23 -0
  299. memos/types/openai_chat_completion_types/chat_completion_content_part_param.py +43 -0
  300. memos/types/openai_chat_completion_types/chat_completion_content_part_refusal_param.py +16 -0
  301. memos/types/openai_chat_completion_types/chat_completion_content_part_text_param.py +16 -0
  302. memos/types/openai_chat_completion_types/chat_completion_message_custom_tool_call_param.py +27 -0
  303. memos/types/openai_chat_completion_types/chat_completion_message_function_tool_call_param.py +32 -0
  304. memos/types/openai_chat_completion_types/chat_completion_message_param.py +18 -0
  305. memos/types/openai_chat_completion_types/chat_completion_message_tool_call_union_param.py +15 -0
  306. memos/types/openai_chat_completion_types/chat_completion_system_message_param.py +36 -0
  307. memos/types/openai_chat_completion_types/chat_completion_tool_message_param.py +30 -0
  308. memos/types/openai_chat_completion_types/chat_completion_user_message_param.py +34 -0
  309. memos/utils.py +123 -0
  310. memos/vec_dbs/__init__.py +0 -0
  311. memos/vec_dbs/base.py +117 -0
  312. memos/vec_dbs/factory.py +23 -0
  313. memos/vec_dbs/item.py +50 -0
  314. memos/vec_dbs/milvus.py +654 -0
  315. memos/vec_dbs/qdrant.py +355 -0
@@ -0,0 +1,714 @@
1
+ """
2
+ API Analyzer for Scheduler
3
+
4
+ This module provides the APIAnalyzerForScheduler class that handles API requests
5
+ for search and add operations with reusable instance variables.
6
+ """
7
+
8
+ import http.client
9
+ import json
10
+
11
+ from typing import Any
12
+ from urllib.parse import urlparse
13
+
14
+ import requests
15
+
16
+ from memos.api.product_models import APIADDRequest, APISearchRequest
17
+ from memos.api.routers.server_router import add_memories, search_memories
18
+ from memos.log import get_logger
19
+ from memos.types import MessageDict, SearchMode, UserContext
20
+
21
+
22
+ logger = get_logger(__name__)
23
+
24
+
25
+ class APIAnalyzerForScheduler:
26
+ """
27
+ API Analyzer class for scheduler operations.
28
+
29
+ This class provides methods to interact with APIs for search and add operations,
30
+ with reusable instance variables for better performance and configuration management.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ base_url: str = "http://127.0.0.1:8002",
36
+ default_headers: dict[str, str] | None = None,
37
+ timeout: int = 30,
38
+ ):
39
+ """
40
+ Initialize the APIAnalyzerForScheduler.
41
+
42
+ Args:
43
+ base_url: Base URL for API requests
44
+ default_headers: Default headers to use for all requests
45
+ timeout: Request timeout in seconds
46
+ """
47
+ self.base_url = base_url.rstrip("/")
48
+ self.timeout = timeout
49
+
50
+ # Default headers
51
+ self.default_headers = default_headers or {"Content-Type": "application/json"}
52
+
53
+ # Parse URL for http.client usage
54
+ parsed_url = urlparse(self.base_url)
55
+ self.host = parsed_url.hostname
56
+ self.port = parsed_url.port or 8002
57
+ self.is_https = parsed_url.scheme == "https"
58
+
59
+ # Reusable connection for http.client
60
+ self._connection = None
61
+
62
+ # Attributes
63
+ self.user_id = "test_user_id"
64
+ self.mem_cube_id = "test_mem_cube_id"
65
+
66
+ logger.info(f"APIAnalyzerForScheduler initialized with base_url: {self.base_url}")
67
+
68
+ def _get_connection(self) -> http.client.HTTPConnection | http.client.HTTPSConnection:
69
+ """
70
+ Get or create a reusable HTTP connection.
71
+
72
+ Returns:
73
+ HTTP connection object
74
+ """
75
+ if self._connection is None:
76
+ if self.is_https:
77
+ self._connection = http.client.HTTPSConnection(self.host, self.port)
78
+ else:
79
+ self._connection = http.client.HTTPConnection(self.host, self.port)
80
+ return self._connection
81
+
82
+ def _close_connection(self):
83
+ """Close the HTTP connection if it exists."""
84
+ if self._connection:
85
+ self._connection.close()
86
+ self._connection = None
87
+
88
+ def search(
89
+ self, user_id: str, mem_cube_id: str, query: str, top_k: int = 50, use_requests: bool = True
90
+ ) -> dict[str, Any]:
91
+ """
92
+ Search for memories using the product/search API endpoint.
93
+
94
+ Args:
95
+ user_id: User identifier
96
+ mem_cube_id: Memory cube identifier
97
+ query: Search query string
98
+ top_k: Number of top_k results to return
99
+ use_requests: Whether to use requests library (True) or http.client (False)
100
+
101
+ Returns:
102
+ Dictionary containing the API response
103
+ """
104
+ payload = {"user_id": user_id, "mem_cube_id": mem_cube_id, "query": query, "top_k": top_k}
105
+
106
+ try:
107
+ if use_requests:
108
+ return self._search_with_requests(payload)
109
+ else:
110
+ return self._search_with_http_client(payload)
111
+ except Exception as e:
112
+ logger.error(f"Error in search operation: {e}")
113
+ return {"error": str(e), "success": False}
114
+
115
+ def _search_with_requests(self, payload: dict[str, Any]) -> dict[str, Any]:
116
+ """
117
+ Perform search using requests library.
118
+
119
+ Args:
120
+ payload: Request payload
121
+
122
+ Returns:
123
+ Dictionary containing the API response
124
+ """
125
+ url = f"{self.base_url}/product/search"
126
+
127
+ response = requests.post(
128
+ url, headers=self.default_headers, data=json.dumps(payload), timeout=self.timeout
129
+ )
130
+
131
+ logger.info(f"Search request to {url} completed with status: {response.status_code}")
132
+
133
+ try:
134
+ return {
135
+ "success": True,
136
+ "status_code": response.status_code,
137
+ "data": response.json() if response.content else {},
138
+ "text": response.text,
139
+ }
140
+ except json.JSONDecodeError:
141
+ return {
142
+ "success": True,
143
+ "status_code": response.status_code,
144
+ "data": {},
145
+ "text": response.text,
146
+ }
147
+
148
+ def _search_with_http_client(self, payload: dict[str, Any]) -> dict[str, Any]:
149
+ """
150
+ Perform search using http.client.
151
+
152
+ Args:
153
+ payload: Request payload
154
+
155
+ Returns:
156
+ Dictionary containing the API response
157
+ """
158
+ conn = self._get_connection()
159
+
160
+ try:
161
+ conn.request("POST", "/product/search", json.dumps(payload), self.default_headers)
162
+
163
+ response = conn.getresponse()
164
+ data = response.read()
165
+ response_text = data.decode("utf-8")
166
+
167
+ logger.info(f"Search request completed with status: {response.status}")
168
+
169
+ try:
170
+ response_data = json.loads(response_text) if response_text else {}
171
+ except json.JSONDecodeError:
172
+ response_data = {}
173
+
174
+ return {
175
+ "success": True,
176
+ "status_code": response.status,
177
+ "data": response_data,
178
+ "text": response_text,
179
+ }
180
+ except Exception as e:
181
+ logger.error(f"Error in http.client search: {e}")
182
+ return {"error": str(e), "success": False}
183
+
184
+ def add(
185
+ self, messages: list, user_id: str, mem_cube_id: str, use_requests: bool = True
186
+ ) -> dict[str, Any]:
187
+ """
188
+ Add memories using the product/add API endpoint.
189
+
190
+ Args:
191
+ messages: List of message objects with role and content
192
+ user_id: User identifier
193
+ mem_cube_id: Memory cube identifier
194
+ use_requests: Whether to use requests library (True) or http.client (False)
195
+
196
+ Returns:
197
+ Dictionary containing the API response
198
+ """
199
+ payload = {"messages": messages, "user_id": user_id, "mem_cube_id": mem_cube_id}
200
+
201
+ try:
202
+ if use_requests:
203
+ return self._add_with_requests(payload)
204
+ else:
205
+ return self._add_with_http_client(payload)
206
+ except Exception as e:
207
+ logger.error(f"Error in add operation: {e}")
208
+ return {"error": str(e), "success": False}
209
+
210
+ def _add_with_requests(self, payload: dict[str, Any]) -> dict[str, Any]:
211
+ """
212
+ Perform add using requests library.
213
+
214
+ Args:
215
+ payload: Request payload
216
+
217
+ Returns:
218
+ Dictionary containing the API response
219
+ """
220
+ url = f"{self.base_url}/product/add"
221
+
222
+ response = requests.post(
223
+ url, headers=self.default_headers, data=json.dumps(payload), timeout=self.timeout
224
+ )
225
+
226
+ logger.info(f"Add request to {url} completed with status: {response.status_code}")
227
+
228
+ try:
229
+ return {
230
+ "success": True,
231
+ "status_code": response.status_code,
232
+ "data": response.json() if response.content else {},
233
+ "text": response.text,
234
+ }
235
+ except json.JSONDecodeError:
236
+ return {
237
+ "success": True,
238
+ "status_code": response.status_code,
239
+ "data": {},
240
+ "text": response.text,
241
+ }
242
+
243
+ def _add_with_http_client(self, payload: dict[str, Any]) -> dict[str, Any]:
244
+ """
245
+ Perform add using http.client.
246
+
247
+ Args:
248
+ payload: Request payload
249
+
250
+ Returns:
251
+ Dictionary containing the API response
252
+ """
253
+ conn = self._get_connection()
254
+
255
+ try:
256
+ conn.request("POST", "/product/add", json.dumps(payload), self.default_headers)
257
+
258
+ response = conn.getresponse()
259
+ data = response.read()
260
+ response_text = data.decode("utf-8")
261
+
262
+ logger.info(f"Add request completed with status: {response.status}")
263
+
264
+ try:
265
+ response_data = json.loads(response_text) if response_text else {}
266
+ except json.JSONDecodeError:
267
+ response_data = {}
268
+
269
+ return {
270
+ "success": True,
271
+ "status_code": response.status,
272
+ "data": response_data,
273
+ "text": response_text,
274
+ }
275
+ except Exception as e:
276
+ logger.error(f"Error in http.client add: {e}")
277
+ return {"error": str(e), "success": False}
278
+
279
+ def update_base_url(self, new_base_url: str):
280
+ """
281
+ Update the base URL and reinitialize connection parameters.
282
+
283
+ Args:
284
+ new_base_url: New base URL for API requests
285
+ """
286
+ self._close_connection()
287
+ self.base_url = new_base_url.rstrip("/")
288
+
289
+ # Re-parse URL
290
+ parsed_url = urlparse(self.base_url)
291
+ self.host = parsed_url.hostname
292
+ self.port = parsed_url.port or (443 if parsed_url.scheme == "https" else 80)
293
+ self.is_https = parsed_url.scheme == "https"
294
+
295
+ logger.info(f"Base URL updated to: {self.base_url}")
296
+
297
+ def update_headers(self, headers: dict[str, str]):
298
+ """
299
+ Update default headers.
300
+
301
+ Args:
302
+ headers: New headers to merge with existing ones
303
+ """
304
+ self.default_headers.update(headers)
305
+ logger.info("Headers updated")
306
+
307
+ def __del__(self):
308
+ """Cleanup method to close connection when object is destroyed."""
309
+ self._close_connection()
310
+
311
+ def analyze_service(self):
312
+ # Example add operation
313
+ messages = [
314
+ {"role": "user", "content": "Where should I go for New Year's Eve in Shanghai?"},
315
+ {
316
+ "role": "assistant",
317
+ "content": "You could head to the Bund for the countdown, attend a rooftop party, or enjoy the fireworks at Disneyland Shanghai.",
318
+ },
319
+ ]
320
+
321
+ add_result = self.add(
322
+ messages=messages, user_id="test_user_id", mem_cube_id="test_mem_cube_id"
323
+ )
324
+ print("Add result:", add_result)
325
+
326
+ # Example search operation
327
+ search_result = self.search(
328
+ user_id="test_user_id",
329
+ mem_cube_id="test_mem_cube_id",
330
+ query="What are some good places to celebrate New Year's Eve in Shanghai?",
331
+ top_k=50,
332
+ )
333
+ print("Search result:", search_result)
334
+
335
+ def analyze_features(self):
336
+ try:
337
+ # Test basic search functionality
338
+ search_result = self.search(
339
+ user_id="test_user_id",
340
+ mem_cube_id="test_mem_cube_id",
341
+ query="What are some good places to celebrate New Year's Eve in Shanghai?",
342
+ top_k=50,
343
+ )
344
+ print("Search result:", search_result)
345
+ except Exception as e:
346
+ logger.error(f"Feature analysis failed: {e}")
347
+
348
+
349
+ class DirectSearchMemoriesAnalyzer:
350
+ """
351
+ Direct analyzer for testing search_memories function
352
+ Used for debugging and analyzing search_memories function behavior without starting a full API server
353
+ """
354
+
355
+ def __init__(self):
356
+ """Initialize the analyzer"""
357
+ # Import necessary modules
358
+ self.APISearchRequest = APISearchRequest
359
+ self.APIADDRequest = APIADDRequest
360
+ self.search_memories = search_memories
361
+ self.add_memories = add_memories
362
+ self.UserContext = UserContext
363
+ self.MessageDict = MessageDict
364
+
365
+ # Initialize conversation history for continuous conversation support
366
+ self.conversation_history = []
367
+ self.current_session_id = None
368
+ self.current_user_id = None
369
+ self.current_mem_cube_id = None
370
+
371
+ logger.info("DirectSearchMemoriesAnalyzer initialized successfully")
372
+
373
+ def start_conversation(self, user_id="test_user", mem_cube_id="test_cube", session_id=None):
374
+ """
375
+ Start a new conversation session for continuous dialogue.
376
+
377
+ Args:
378
+ user_id: User ID for the conversation
379
+ mem_cube_id: Memory cube ID for the conversation
380
+ session_id: Session ID for the conversation (auto-generated if None)
381
+ """
382
+ self.current_user_id = user_id
383
+ self.current_mem_cube_id = mem_cube_id
384
+ self.current_session_id = (
385
+ session_id or f"session_{hash(user_id + mem_cube_id)}_{len(self.conversation_history)}"
386
+ )
387
+ self.conversation_history = []
388
+
389
+ logger.info(f"Started conversation session: {self.current_session_id}")
390
+ print(f"šŸš€ Started new conversation session: {self.current_session_id}")
391
+ print(f" User ID: {self.current_user_id}")
392
+ print(f" Mem Cube ID: {self.current_mem_cube_id}")
393
+
394
+ def add_to_conversation(self, user_message, assistant_message=None):
395
+ """
396
+ Add messages to the current conversation and store them in memory.
397
+
398
+ Args:
399
+ user_message: User's message content
400
+ assistant_message: Assistant's response (optional)
401
+
402
+ Returns:
403
+ Result from add_memories function
404
+ """
405
+ if not self.current_session_id:
406
+ raise ValueError("No active conversation session. Call start_conversation() first.")
407
+
408
+ # Prepare messages for adding to memory
409
+ messages = [{"role": "user", "content": user_message}]
410
+ if assistant_message:
411
+ messages.append({"role": "assistant", "content": assistant_message})
412
+
413
+ # Add to conversation history
414
+ self.conversation_history.extend(messages)
415
+
416
+ # Create add request
417
+ add_req = self.create_test_add_request(
418
+ user_id=self.current_user_id,
419
+ mem_cube_id=self.current_mem_cube_id,
420
+ messages=messages,
421
+ session_id=self.current_session_id,
422
+ )
423
+
424
+ print(f"šŸ’¬ Adding to conversation (Session: {self.current_session_id}):")
425
+ print(f" User: {user_message}")
426
+ if assistant_message:
427
+ print(f" Assistant: {assistant_message}")
428
+
429
+ # Add to memory
430
+ result = self.add_memories(add_req)
431
+ print(" āœ… Added to memory successfully")
432
+
433
+ return result
434
+
435
+ def search_in_conversation(self, query, mode="fast", top_k=10, include_history=True):
436
+ """
437
+ Search memories within the current conversation context.
438
+
439
+ Args:
440
+ query: Search query
441
+ mode: Search mode ("fast", "fine", or "mixture")
442
+ top_k: Number of results to return
443
+ include_history: Whether to include conversation history in the search
444
+
445
+ Returns:
446
+ Search results
447
+ """
448
+ if not self.current_session_id:
449
+ raise ValueError("No active conversation session. Call start_conversation() first.")
450
+
451
+ # Prepare chat history if requested
452
+ chat_history = self.conversation_history if include_history else None
453
+
454
+ # Create search request
455
+ search_req = self.create_test_search_request(
456
+ query=query,
457
+ user_id=self.current_user_id,
458
+ mem_cube_id=self.current_mem_cube_id,
459
+ mode=mode,
460
+ top_k=top_k,
461
+ chat_history=chat_history,
462
+ session_id=self.current_session_id,
463
+ )
464
+
465
+ print(f"šŸ” Searching in conversation (Session: {self.current_session_id}):")
466
+ print(f" Query: {query}")
467
+ print(f" Mode: {mode}")
468
+ print(f" Top K: {top_k}")
469
+ print(f" Include History: {include_history}")
470
+ print(f" History Length: {len(self.conversation_history) if chat_history else 0}")
471
+
472
+ # Perform search
473
+ result = self.search_memories(search_req)
474
+
475
+ print(" āœ… Search completed")
476
+ if hasattr(result, "data") and result.data:
477
+ total_memories = sum(
478
+ len(mem_list) for mem_list in result.data.values() if isinstance(mem_list, list)
479
+ )
480
+ print(f" šŸ“Š Found {total_memories} total memories")
481
+
482
+ return result
483
+
484
+ def test_continuous_conversation(self, mode=SearchMode.MIXTURE):
485
+ """Test continuous conversation functionality"""
486
+ print("=" * 80)
487
+ print("Testing Continuous Conversation Functionality")
488
+ print("=" * 80)
489
+
490
+ try:
491
+ # Start a conversation
492
+ self.start_conversation(user_id="conv_test_user", mem_cube_id="conv_test_cube")
493
+
494
+ # Prepare all conversation messages for batch addition
495
+ all_messages = [
496
+ {
497
+ "role": "user",
498
+ "content": "I'm planning a trip to Shanghai for New Year's Eve. What are some good places to visit?",
499
+ },
500
+ {
501
+ "role": "assistant",
502
+ "content": "Shanghai has many great places for New Year's Eve! You could visit the Bund for the countdown, go to a rooftop party, or enjoy fireworks at Disneyland Shanghai. The French Concession also has nice bars and restaurants.",
503
+ },
504
+ {"role": "user", "content": "What about food? Any restaurant recommendations?"},
505
+ {
506
+ "role": "assistant",
507
+ "content": "For New Year's Eve dining in Shanghai, I'd recommend trying some local specialties like xiaolongbao at Din Tai Fung, or for a fancy dinner, you could book at restaurants in the Bund area with great views.",
508
+ },
509
+ {"role": "user", "content": "I'm on a budget though. Any cheaper alternatives?"},
510
+ {
511
+ "role": "assistant",
512
+ "content": "For budget-friendly options, try street food in Yuyuan Garden area, local noodle shops, or food courts in shopping malls. You can also watch the fireworks from free public areas along the Huangpu River.",
513
+ },
514
+ ]
515
+
516
+ # Add all conversation messages at once
517
+ print("\nšŸ“ Adding all conversation messages at once:")
518
+ add_req = self.create_test_add_request(
519
+ user_id=self.current_user_id,
520
+ mem_cube_id=self.current_mem_cube_id,
521
+ messages=all_messages,
522
+ session_id=self.current_session_id,
523
+ )
524
+
525
+ print(
526
+ f"šŸ’¬ Adding {len(all_messages)} messages to conversation (Session: {self.current_session_id})"
527
+ )
528
+ self.add_memories(add_req)
529
+
530
+ # Update conversation history
531
+ self.conversation_history.extend(all_messages)
532
+ print(" āœ… Added all messages to memory successfully")
533
+
534
+ # Test searching within the conversation
535
+ print("\nšŸ” Testing search within conversation:")
536
+
537
+ # Search for trip-related information
538
+ self.search_in_conversation(
539
+ query="New Year's Eve Shanghai recommendations", mode=mode, top_k=5
540
+ )
541
+
542
+ # Search for food-related information
543
+ self.search_in_conversation(query="budget food Shanghai", mode=mode, top_k=3)
544
+
545
+ # Search without conversation history
546
+ self.search_in_conversation(
547
+ query="Shanghai travel", mode=mode, top_k=3, include_history=False
548
+ )
549
+
550
+ print("\nāœ… Continuous conversation test completed successfully!")
551
+ return True
552
+
553
+ except Exception as e:
554
+ print(f"āŒ Continuous conversation test failed: {e}")
555
+ import traceback
556
+
557
+ traceback.print_exc()
558
+ return False
559
+
560
+ def create_test_search_request(
561
+ self,
562
+ query="test query",
563
+ user_id="test_user",
564
+ mem_cube_id="test_cube",
565
+ mode="fast",
566
+ top_k=10,
567
+ chat_history=None,
568
+ session_id=None,
569
+ ):
570
+ """
571
+ Create a test APISearchRequest object with the given parameters.
572
+
573
+ Args:
574
+ query: Search query string
575
+ user_id: User ID for the request
576
+ mem_cube_id: Memory cube ID for the request
577
+ mode: Search mode ("fast" or "fine")
578
+ top_k: Number of results to return
579
+ chat_history: Chat history for context (optional)
580
+ session_id: Session ID for the request (optional)
581
+
582
+ Returns:
583
+ APISearchRequest: A configured request object
584
+ """
585
+ return self.APISearchRequest(
586
+ query=query,
587
+ user_id=user_id,
588
+ mem_cube_id=mem_cube_id,
589
+ mode=mode,
590
+ top_k=top_k,
591
+ chat_history=chat_history,
592
+ session_id=session_id,
593
+ )
594
+
595
+ def create_test_add_request(
596
+ self,
597
+ user_id="test_user",
598
+ mem_cube_id="test_cube",
599
+ messages=None,
600
+ memory_content=None,
601
+ session_id=None,
602
+ extract_mode=None,
603
+ async_mode="sync",
604
+ ):
605
+ """
606
+ Create a test APIADDRequest object with the given parameters.
607
+
608
+ Args:
609
+ user_id: User ID for the request
610
+ mem_cube_id: Memory cube ID for the request
611
+ messages: List of messages to add (optional)
612
+ memory_content: Direct memory content to add (optional)
613
+ session_id: Session ID for the request (optional)
614
+
615
+ Returns:
616
+ APIADDRequest: A configured request object
617
+ """
618
+ if messages is None and memory_content is None:
619
+ # Default test messages
620
+ messages = [
621
+ {"role": "user", "content": "What's the weather like today?"},
622
+ {
623
+ "role": "assistant",
624
+ "content": "I don't have access to real-time weather data, but you can check a weather app or website for current conditions.",
625
+ },
626
+ ]
627
+
628
+ # Ensure we have a valid session_id
629
+ if session_id is None:
630
+ session_id = "test_session_" + str(hash(user_id + mem_cube_id))[:8]
631
+
632
+ return self.APIADDRequest(
633
+ user_id=user_id,
634
+ mem_cube_id=mem_cube_id,
635
+ messages=messages,
636
+ memory_content=memory_content,
637
+ session_id=session_id,
638
+ doc_path=None,
639
+ source="api_analyzer_test",
640
+ chat_history=None,
641
+ operation=None,
642
+ mode=extract_mode,
643
+ async_mode=async_mode,
644
+ )
645
+
646
+ def run_all_tests(self, mode=SearchMode.MIXTURE):
647
+ """Run all available tests"""
648
+ print("šŸš€ Starting comprehensive test suite")
649
+ print("=" * 80)
650
+
651
+ # Test continuous conversation functionality
652
+ print("\nšŸ’¬ Testing CONTINUOUS CONVERSATION functions:")
653
+ try:
654
+ self.test_continuous_conversation(mode=mode)
655
+ print("āœ… Continuous conversation test completed successfully")
656
+ except Exception as e:
657
+ print(f"āŒ Continuous conversation test failed: {e}")
658
+
659
+ print("\n" + "=" * 80)
660
+ print("āœ… All tests completed!")
661
+
662
+
663
+ # Example usage
664
+ if __name__ == "__main__":
665
+ import argparse
666
+
667
+ parser = argparse.ArgumentParser(description="API Analyzer for Memory Scheduler")
668
+ parser.add_argument(
669
+ "--mode",
670
+ choices=["direct", "api"],
671
+ default="direct",
672
+ help="Test mode: 'direct' for direct function testing, 'api' for API testing (default: direct)",
673
+ )
674
+
675
+ args = parser.parse_args()
676
+
677
+ if args.mode == "direct":
678
+ # Direct test mode for search_memories and add_memories functions
679
+ print("Using direct test mode")
680
+ try:
681
+ direct_analyzer = DirectSearchMemoriesAnalyzer()
682
+ direct_analyzer.run_all_tests(mode=SearchMode.FINE)
683
+ except Exception as e:
684
+ print(f"Direct test mode failed: {e}")
685
+ import traceback
686
+
687
+ traceback.print_exc()
688
+ else:
689
+ # Original API test mode
690
+ print("Using API test mode")
691
+ analyzer = APIAnalyzerForScheduler()
692
+
693
+ # Test add operation
694
+ messages = [
695
+ {"role": "user", "content": "Where should I go for New Year's Eve in Shanghai?"},
696
+ {
697
+ "role": "assistant",
698
+ "content": "You could head to the Bund for the countdown, attend a rooftop party, or enjoy the fireworks at Disneyland Shanghai.",
699
+ },
700
+ ]
701
+
702
+ add_result = analyzer.add(
703
+ messages=messages, user_id="test_user_id", mem_cube_id="test_mem_cube_id"
704
+ )
705
+ print("Add result:", add_result)
706
+
707
+ # Test search operation
708
+ search_result = analyzer.search(
709
+ user_id="test_user_id",
710
+ mem_cube_id="test_mem_cube_id",
711
+ query="What are some good places to celebrate New Year's Eve in Shanghai?",
712
+ top_k=10,
713
+ )
714
+ print("Search result:", search_result)