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.
- memoryos-2.0.3.dist-info/METADATA +418 -0
- memoryos-2.0.3.dist-info/RECORD +315 -0
- memoryos-2.0.3.dist-info/WHEEL +4 -0
- memoryos-2.0.3.dist-info/entry_points.txt +3 -0
- memoryos-2.0.3.dist-info/licenses/LICENSE +201 -0
- memos/__init__.py +20 -0
- memos/api/client.py +571 -0
- memos/api/config.py +1018 -0
- memos/api/context/dependencies.py +50 -0
- memos/api/exceptions.py +53 -0
- memos/api/handlers/__init__.py +62 -0
- memos/api/handlers/add_handler.py +158 -0
- memos/api/handlers/base_handler.py +194 -0
- memos/api/handlers/chat_handler.py +1401 -0
- memos/api/handlers/component_init.py +388 -0
- memos/api/handlers/config_builders.py +190 -0
- memos/api/handlers/feedback_handler.py +93 -0
- memos/api/handlers/formatters_handler.py +237 -0
- memos/api/handlers/memory_handler.py +316 -0
- memos/api/handlers/scheduler_handler.py +497 -0
- memos/api/handlers/search_handler.py +222 -0
- memos/api/handlers/suggestion_handler.py +117 -0
- memos/api/mcp_serve.py +614 -0
- memos/api/middleware/request_context.py +101 -0
- memos/api/product_api.py +38 -0
- memos/api/product_models.py +1206 -0
- memos/api/routers/__init__.py +1 -0
- memos/api/routers/product_router.py +477 -0
- memos/api/routers/server_router.py +394 -0
- memos/api/server_api.py +44 -0
- memos/api/start_api.py +433 -0
- memos/chunkers/__init__.py +4 -0
- memos/chunkers/base.py +24 -0
- memos/chunkers/charactertext_chunker.py +41 -0
- memos/chunkers/factory.py +24 -0
- memos/chunkers/markdown_chunker.py +62 -0
- memos/chunkers/sentence_chunker.py +54 -0
- memos/chunkers/simple_chunker.py +50 -0
- memos/cli.py +113 -0
- memos/configs/__init__.py +0 -0
- memos/configs/base.py +82 -0
- memos/configs/chunker.py +59 -0
- memos/configs/embedder.py +88 -0
- memos/configs/graph_db.py +236 -0
- memos/configs/internet_retriever.py +100 -0
- memos/configs/llm.py +151 -0
- memos/configs/mem_agent.py +54 -0
- memos/configs/mem_chat.py +81 -0
- memos/configs/mem_cube.py +105 -0
- memos/configs/mem_os.py +83 -0
- memos/configs/mem_reader.py +91 -0
- memos/configs/mem_scheduler.py +385 -0
- memos/configs/mem_user.py +70 -0
- memos/configs/memory.py +324 -0
- memos/configs/parser.py +38 -0
- memos/configs/reranker.py +18 -0
- memos/configs/utils.py +8 -0
- memos/configs/vec_db.py +80 -0
- memos/context/context.py +355 -0
- memos/dependency.py +52 -0
- memos/deprecation.py +262 -0
- memos/embedders/__init__.py +0 -0
- memos/embedders/ark.py +95 -0
- memos/embedders/base.py +106 -0
- memos/embedders/factory.py +29 -0
- memos/embedders/ollama.py +77 -0
- memos/embedders/sentence_transformer.py +49 -0
- memos/embedders/universal_api.py +51 -0
- memos/exceptions.py +30 -0
- memos/graph_dbs/__init__.py +0 -0
- memos/graph_dbs/base.py +274 -0
- memos/graph_dbs/factory.py +27 -0
- memos/graph_dbs/item.py +46 -0
- memos/graph_dbs/nebular.py +1794 -0
- memos/graph_dbs/neo4j.py +1942 -0
- memos/graph_dbs/neo4j_community.py +1058 -0
- memos/graph_dbs/polardb.py +5446 -0
- memos/hello_world.py +97 -0
- memos/llms/__init__.py +0 -0
- memos/llms/base.py +25 -0
- memos/llms/deepseek.py +13 -0
- memos/llms/factory.py +38 -0
- memos/llms/hf.py +443 -0
- memos/llms/hf_singleton.py +114 -0
- memos/llms/ollama.py +135 -0
- memos/llms/openai.py +222 -0
- memos/llms/openai_new.py +198 -0
- memos/llms/qwen.py +13 -0
- memos/llms/utils.py +14 -0
- memos/llms/vllm.py +218 -0
- memos/log.py +237 -0
- memos/mem_agent/base.py +19 -0
- memos/mem_agent/deepsearch_agent.py +391 -0
- memos/mem_agent/factory.py +36 -0
- memos/mem_chat/__init__.py +0 -0
- memos/mem_chat/base.py +30 -0
- memos/mem_chat/factory.py +21 -0
- memos/mem_chat/simple.py +200 -0
- memos/mem_cube/__init__.py +0 -0
- memos/mem_cube/base.py +30 -0
- memos/mem_cube/general.py +240 -0
- memos/mem_cube/navie.py +172 -0
- memos/mem_cube/utils.py +169 -0
- memos/mem_feedback/base.py +15 -0
- memos/mem_feedback/feedback.py +1192 -0
- memos/mem_feedback/simple_feedback.py +40 -0
- memos/mem_feedback/utils.py +230 -0
- memos/mem_os/client.py +5 -0
- memos/mem_os/core.py +1203 -0
- memos/mem_os/main.py +582 -0
- memos/mem_os/product.py +1608 -0
- memos/mem_os/product_server.py +455 -0
- memos/mem_os/utils/default_config.py +359 -0
- memos/mem_os/utils/format_utils.py +1403 -0
- memos/mem_os/utils/reference_utils.py +162 -0
- memos/mem_reader/__init__.py +0 -0
- memos/mem_reader/base.py +47 -0
- memos/mem_reader/factory.py +53 -0
- memos/mem_reader/memory.py +298 -0
- memos/mem_reader/multi_modal_struct.py +965 -0
- memos/mem_reader/read_multi_modal/__init__.py +43 -0
- memos/mem_reader/read_multi_modal/assistant_parser.py +311 -0
- memos/mem_reader/read_multi_modal/base.py +273 -0
- memos/mem_reader/read_multi_modal/file_content_parser.py +826 -0
- memos/mem_reader/read_multi_modal/image_parser.py +359 -0
- memos/mem_reader/read_multi_modal/multi_modal_parser.py +252 -0
- memos/mem_reader/read_multi_modal/string_parser.py +139 -0
- memos/mem_reader/read_multi_modal/system_parser.py +327 -0
- memos/mem_reader/read_multi_modal/text_content_parser.py +131 -0
- memos/mem_reader/read_multi_modal/tool_parser.py +210 -0
- memos/mem_reader/read_multi_modal/user_parser.py +218 -0
- memos/mem_reader/read_multi_modal/utils.py +358 -0
- memos/mem_reader/simple_struct.py +912 -0
- memos/mem_reader/strategy_struct.py +163 -0
- memos/mem_reader/utils.py +157 -0
- memos/mem_scheduler/__init__.py +0 -0
- memos/mem_scheduler/analyzer/__init__.py +0 -0
- memos/mem_scheduler/analyzer/api_analyzer.py +714 -0
- memos/mem_scheduler/analyzer/eval_analyzer.py +219 -0
- memos/mem_scheduler/analyzer/mos_for_test_scheduler.py +571 -0
- memos/mem_scheduler/analyzer/scheduler_for_eval.py +280 -0
- memos/mem_scheduler/base_scheduler.py +1319 -0
- memos/mem_scheduler/general_modules/__init__.py +0 -0
- memos/mem_scheduler/general_modules/api_misc.py +137 -0
- memos/mem_scheduler/general_modules/base.py +80 -0
- memos/mem_scheduler/general_modules/init_components_for_scheduler.py +425 -0
- memos/mem_scheduler/general_modules/misc.py +313 -0
- memos/mem_scheduler/general_modules/scheduler_logger.py +389 -0
- memos/mem_scheduler/general_modules/task_threads.py +315 -0
- memos/mem_scheduler/general_scheduler.py +1495 -0
- memos/mem_scheduler/memory_manage_modules/__init__.py +5 -0
- memos/mem_scheduler/memory_manage_modules/memory_filter.py +306 -0
- memos/mem_scheduler/memory_manage_modules/retriever.py +547 -0
- memos/mem_scheduler/monitors/__init__.py +0 -0
- memos/mem_scheduler/monitors/dispatcher_monitor.py +366 -0
- memos/mem_scheduler/monitors/general_monitor.py +394 -0
- memos/mem_scheduler/monitors/task_schedule_monitor.py +254 -0
- memos/mem_scheduler/optimized_scheduler.py +410 -0
- memos/mem_scheduler/orm_modules/__init__.py +0 -0
- memos/mem_scheduler/orm_modules/api_redis_model.py +518 -0
- memos/mem_scheduler/orm_modules/base_model.py +729 -0
- memos/mem_scheduler/orm_modules/monitor_models.py +261 -0
- memos/mem_scheduler/orm_modules/redis_model.py +699 -0
- memos/mem_scheduler/scheduler_factory.py +23 -0
- memos/mem_scheduler/schemas/__init__.py +0 -0
- memos/mem_scheduler/schemas/analyzer_schemas.py +52 -0
- memos/mem_scheduler/schemas/api_schemas.py +233 -0
- memos/mem_scheduler/schemas/general_schemas.py +55 -0
- memos/mem_scheduler/schemas/message_schemas.py +173 -0
- memos/mem_scheduler/schemas/monitor_schemas.py +406 -0
- memos/mem_scheduler/schemas/task_schemas.py +132 -0
- memos/mem_scheduler/task_schedule_modules/__init__.py +0 -0
- memos/mem_scheduler/task_schedule_modules/dispatcher.py +740 -0
- memos/mem_scheduler/task_schedule_modules/local_queue.py +247 -0
- memos/mem_scheduler/task_schedule_modules/orchestrator.py +74 -0
- memos/mem_scheduler/task_schedule_modules/redis_queue.py +1385 -0
- memos/mem_scheduler/task_schedule_modules/task_queue.py +162 -0
- memos/mem_scheduler/utils/__init__.py +0 -0
- memos/mem_scheduler/utils/api_utils.py +77 -0
- memos/mem_scheduler/utils/config_utils.py +100 -0
- memos/mem_scheduler/utils/db_utils.py +50 -0
- memos/mem_scheduler/utils/filter_utils.py +176 -0
- memos/mem_scheduler/utils/metrics.py +125 -0
- memos/mem_scheduler/utils/misc_utils.py +290 -0
- memos/mem_scheduler/utils/monitor_event_utils.py +67 -0
- memos/mem_scheduler/utils/status_tracker.py +229 -0
- memos/mem_scheduler/webservice_modules/__init__.py +0 -0
- memos/mem_scheduler/webservice_modules/rabbitmq_service.py +485 -0
- memos/mem_scheduler/webservice_modules/redis_service.py +380 -0
- memos/mem_user/factory.py +94 -0
- memos/mem_user/mysql_persistent_user_manager.py +271 -0
- memos/mem_user/mysql_user_manager.py +502 -0
- memos/mem_user/persistent_factory.py +98 -0
- memos/mem_user/persistent_user_manager.py +260 -0
- memos/mem_user/redis_persistent_user_manager.py +225 -0
- memos/mem_user/user_manager.py +488 -0
- memos/memories/__init__.py +0 -0
- memos/memories/activation/__init__.py +0 -0
- memos/memories/activation/base.py +42 -0
- memos/memories/activation/item.py +56 -0
- memos/memories/activation/kv.py +292 -0
- memos/memories/activation/vllmkv.py +219 -0
- memos/memories/base.py +19 -0
- memos/memories/factory.py +42 -0
- memos/memories/parametric/__init__.py +0 -0
- memos/memories/parametric/base.py +19 -0
- memos/memories/parametric/item.py +11 -0
- memos/memories/parametric/lora.py +41 -0
- memos/memories/textual/__init__.py +0 -0
- memos/memories/textual/base.py +92 -0
- memos/memories/textual/general.py +236 -0
- memos/memories/textual/item.py +304 -0
- memos/memories/textual/naive.py +187 -0
- memos/memories/textual/prefer_text_memory/__init__.py +0 -0
- memos/memories/textual/prefer_text_memory/adder.py +504 -0
- memos/memories/textual/prefer_text_memory/config.py +106 -0
- memos/memories/textual/prefer_text_memory/extractor.py +221 -0
- memos/memories/textual/prefer_text_memory/factory.py +85 -0
- memos/memories/textual/prefer_text_memory/retrievers.py +177 -0
- memos/memories/textual/prefer_text_memory/spliter.py +132 -0
- memos/memories/textual/prefer_text_memory/utils.py +93 -0
- memos/memories/textual/preference.py +344 -0
- memos/memories/textual/simple_preference.py +161 -0
- memos/memories/textual/simple_tree.py +69 -0
- memos/memories/textual/tree.py +459 -0
- memos/memories/textual/tree_text_memory/__init__.py +0 -0
- memos/memories/textual/tree_text_memory/organize/__init__.py +0 -0
- memos/memories/textual/tree_text_memory/organize/handler.py +184 -0
- memos/memories/textual/tree_text_memory/organize/manager.py +518 -0
- memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +238 -0
- memos/memories/textual/tree_text_memory/organize/reorganizer.py +622 -0
- memos/memories/textual/tree_text_memory/retrieve/__init__.py +0 -0
- memos/memories/textual/tree_text_memory/retrieve/advanced_searcher.py +364 -0
- memos/memories/textual/tree_text_memory/retrieve/bm25_util.py +186 -0
- memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +419 -0
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +270 -0
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +102 -0
- memos/memories/textual/tree_text_memory/retrieve/reasoner.py +61 -0
- memos/memories/textual/tree_text_memory/retrieve/recall.py +497 -0
- memos/memories/textual/tree_text_memory/retrieve/reranker.py +111 -0
- memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +16 -0
- memos/memories/textual/tree_text_memory/retrieve/retrieve_utils.py +472 -0
- memos/memories/textual/tree_text_memory/retrieve/searcher.py +848 -0
- memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +135 -0
- memos/memories/textual/tree_text_memory/retrieve/utils.py +54 -0
- memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +387 -0
- memos/memos_tools/dinding_report_bot.py +453 -0
- memos/memos_tools/lockfree_dict.py +120 -0
- memos/memos_tools/notification_service.py +44 -0
- memos/memos_tools/notification_utils.py +142 -0
- memos/memos_tools/singleton.py +174 -0
- memos/memos_tools/thread_safe_dict.py +310 -0
- memos/memos_tools/thread_safe_dict_segment.py +382 -0
- memos/multi_mem_cube/__init__.py +0 -0
- memos/multi_mem_cube/composite_cube.py +86 -0
- memos/multi_mem_cube/single_cube.py +874 -0
- memos/multi_mem_cube/views.py +54 -0
- memos/parsers/__init__.py +0 -0
- memos/parsers/base.py +15 -0
- memos/parsers/factory.py +21 -0
- memos/parsers/markitdown.py +28 -0
- memos/reranker/__init__.py +4 -0
- memos/reranker/base.py +25 -0
- memos/reranker/concat.py +103 -0
- memos/reranker/cosine_local.py +102 -0
- memos/reranker/factory.py +72 -0
- memos/reranker/http_bge.py +324 -0
- memos/reranker/http_bge_strategy.py +327 -0
- memos/reranker/noop.py +19 -0
- memos/reranker/strategies/__init__.py +4 -0
- memos/reranker/strategies/base.py +61 -0
- memos/reranker/strategies/concat_background.py +94 -0
- memos/reranker/strategies/concat_docsource.py +110 -0
- memos/reranker/strategies/dialogue_common.py +109 -0
- memos/reranker/strategies/factory.py +31 -0
- memos/reranker/strategies/single_turn.py +107 -0
- memos/reranker/strategies/singleturn_outmem.py +98 -0
- memos/settings.py +10 -0
- memos/templates/__init__.py +0 -0
- memos/templates/advanced_search_prompts.py +211 -0
- memos/templates/cloud_service_prompt.py +107 -0
- memos/templates/instruction_completion.py +66 -0
- memos/templates/mem_agent_prompts.py +85 -0
- memos/templates/mem_feedback_prompts.py +822 -0
- memos/templates/mem_reader_prompts.py +1096 -0
- memos/templates/mem_reader_strategy_prompts.py +238 -0
- memos/templates/mem_scheduler_prompts.py +626 -0
- memos/templates/mem_search_prompts.py +93 -0
- memos/templates/mos_prompts.py +403 -0
- memos/templates/prefer_complete_prompt.py +735 -0
- memos/templates/tool_mem_prompts.py +139 -0
- memos/templates/tree_reorganize_prompts.py +230 -0
- memos/types/__init__.py +34 -0
- memos/types/general_types.py +151 -0
- memos/types/openai_chat_completion_types/__init__.py +15 -0
- memos/types/openai_chat_completion_types/chat_completion_assistant_message_param.py +56 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_image_param.py +27 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_input_audio_param.py +23 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_param.py +43 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_refusal_param.py +16 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_text_param.py +16 -0
- memos/types/openai_chat_completion_types/chat_completion_message_custom_tool_call_param.py +27 -0
- memos/types/openai_chat_completion_types/chat_completion_message_function_tool_call_param.py +32 -0
- memos/types/openai_chat_completion_types/chat_completion_message_param.py +18 -0
- memos/types/openai_chat_completion_types/chat_completion_message_tool_call_union_param.py +15 -0
- memos/types/openai_chat_completion_types/chat_completion_system_message_param.py +36 -0
- memos/types/openai_chat_completion_types/chat_completion_tool_message_param.py +30 -0
- memos/types/openai_chat_completion_types/chat_completion_user_message_param.py +34 -0
- memos/utils.py +123 -0
- memos/vec_dbs/__init__.py +0 -0
- memos/vec_dbs/base.py +117 -0
- memos/vec_dbs/factory.py +23 -0
- memos/vec_dbs/item.py +50 -0
- memos/vec_dbs/milvus.py +654 -0
- memos/vec_dbs/qdrant.py +355 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from memos.memories.textual.item import TextualMemoryItem
|
|
5
|
+
|
|
6
|
+
from .dialogue_common import DialogueRankingTracker
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseRerankerStrategy(ABC):
|
|
10
|
+
"""Abstract interface for memory rerankers with concatenation strategy."""
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def prepare_documents(
|
|
14
|
+
self,
|
|
15
|
+
query: str,
|
|
16
|
+
graph_results: list[TextualMemoryItem],
|
|
17
|
+
top_k: int,
|
|
18
|
+
**kwargs,
|
|
19
|
+
) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
20
|
+
"""
|
|
21
|
+
Prepare documents for ranking based on the strategy.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
query: The search query
|
|
25
|
+
graph_results: List of TextualMemoryItem objects to process
|
|
26
|
+
top_k: Maximum number of items to return
|
|
27
|
+
**kwargs: Additional strategy-specific parameters
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
31
|
+
- Tracker: DialogueRankingTracker instance
|
|
32
|
+
- original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
33
|
+
- documents: List of text documents ready for ranking
|
|
34
|
+
"""
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def reconstruct_items(
|
|
39
|
+
self,
|
|
40
|
+
ranked_indices: list[int],
|
|
41
|
+
scores: list[float],
|
|
42
|
+
tracker: DialogueRankingTracker,
|
|
43
|
+
original_items: dict[str, Any],
|
|
44
|
+
top_k: int,
|
|
45
|
+
**kwargs,
|
|
46
|
+
) -> list[tuple[TextualMemoryItem, float]]:
|
|
47
|
+
"""
|
|
48
|
+
Reconstruct TextualMemoryItem objects from ranked results.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
ranked_indices: List of indices sorted by relevance
|
|
52
|
+
scores: Corresponding relevance scores
|
|
53
|
+
tracker: DialogueRankingTracker instance
|
|
54
|
+
original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
55
|
+
top_k: Maximum number of items to return
|
|
56
|
+
**kwargs: Additional strategy-specific parameters
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
List of (reconstructed_memory_item, aggregated_score) tuples
|
|
60
|
+
"""
|
|
61
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# memos/reranker/strategies/single_turn.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .base import BaseRerankerStrategy
|
|
9
|
+
from .dialogue_common import DialogueRankingTracker
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_TAG1 = re.compile(r"^\s*\[[^\]]*\]\s*")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ConcatBackgroundStrategy(BaseRerankerStrategy):
|
|
16
|
+
"""
|
|
17
|
+
Concat background strategy.
|
|
18
|
+
|
|
19
|
+
This strategy processes dialogue pairs by concatenating background and
|
|
20
|
+
user and assistant messages into single strings for ranking. Each dialogue pair becomes a
|
|
21
|
+
separate document for ranking.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def prepare_documents(
|
|
25
|
+
self,
|
|
26
|
+
query: str,
|
|
27
|
+
graph_results: list,
|
|
28
|
+
top_k: int,
|
|
29
|
+
**kwargs,
|
|
30
|
+
) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
31
|
+
"""
|
|
32
|
+
Prepare documents based on single turn concatenation strategy.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
query: The search query
|
|
36
|
+
graph_results: List of graph results
|
|
37
|
+
top_k: Maximum number of items to return
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
41
|
+
- Tracker: DialogueRankingTracker instance
|
|
42
|
+
- original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
43
|
+
- documents: List of text documents ready for ranking
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
original_items = {}
|
|
47
|
+
tracker = DialogueRankingTracker()
|
|
48
|
+
documents = []
|
|
49
|
+
for item in graph_results:
|
|
50
|
+
memory = getattr(item, "memory", None)
|
|
51
|
+
if isinstance(memory, str):
|
|
52
|
+
memory = _TAG1.sub("", memory)
|
|
53
|
+
|
|
54
|
+
background = ""
|
|
55
|
+
if hasattr(item, "metadata") and hasattr(item.metadata, "background"):
|
|
56
|
+
background = getattr(item.metadata, "background", "")
|
|
57
|
+
if not isinstance(background, str):
|
|
58
|
+
background = ""
|
|
59
|
+
|
|
60
|
+
documents.append(f"{memory}\n{background}")
|
|
61
|
+
return tracker, original_items, documents
|
|
62
|
+
|
|
63
|
+
def reconstruct_items(
|
|
64
|
+
self,
|
|
65
|
+
ranked_indices: list[int],
|
|
66
|
+
scores: list[float],
|
|
67
|
+
tracker: DialogueRankingTracker,
|
|
68
|
+
original_items: dict[str, Any],
|
|
69
|
+
top_k: int,
|
|
70
|
+
**kwargs,
|
|
71
|
+
) -> list[tuple[Any, float]]:
|
|
72
|
+
"""
|
|
73
|
+
Reconstruct TextualMemoryItem objects from ranked dialogue pairs.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
ranked_indices: List of dialogue pair indices sorted by relevance
|
|
77
|
+
scores: Corresponding relevance scores
|
|
78
|
+
tracker: DialogueRankingTracker instance
|
|
79
|
+
original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
80
|
+
top_k: Maximum number of items to return
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
List of (reconstructed_memory_item, aggregated_score) tuples
|
|
84
|
+
"""
|
|
85
|
+
graph_results = kwargs.get("graph_results")
|
|
86
|
+
documents = kwargs.get("documents")
|
|
87
|
+
reconstructed_items = []
|
|
88
|
+
for idx in ranked_indices:
|
|
89
|
+
item = graph_results[idx]
|
|
90
|
+
item.memory = f"{item.memory}\n{documents[idx]}"
|
|
91
|
+
reconstructed_items.append((item, scores[idx]))
|
|
92
|
+
|
|
93
|
+
reconstructed_items.sort(key=lambda x: x[1], reverse=True)
|
|
94
|
+
return reconstructed_items[:top_k]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# memos/reranker/strategies/single_turn.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .base import BaseRerankerStrategy
|
|
9
|
+
from .dialogue_common import DialogueRankingTracker
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_TAG1 = re.compile(r"^\s*\[[^\]]*\]\s*")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ConcatDocSourceStrategy(BaseRerankerStrategy):
|
|
16
|
+
"""
|
|
17
|
+
Concat background strategy.
|
|
18
|
+
|
|
19
|
+
This strategy processes dialogue pairs by concatenating background and
|
|
20
|
+
user and assistant messages into single strings for ranking. Each dialogue pair becomes a
|
|
21
|
+
separate document for ranking.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
Concat background strategy.
|
|
26
|
+
|
|
27
|
+
This strategy processes dialogue pairs by concatenating background and
|
|
28
|
+
user and assistant messages into single strings for ranking. Each dialogue pair becomes a
|
|
29
|
+
separate document for ranking.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def prepare_documents(
|
|
33
|
+
self,
|
|
34
|
+
query: str,
|
|
35
|
+
graph_results: list,
|
|
36
|
+
top_k: int,
|
|
37
|
+
**kwargs,
|
|
38
|
+
) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
39
|
+
"""
|
|
40
|
+
Prepare documents based on single turn concatenation strategy.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
query: The search query
|
|
44
|
+
graph_results: List of graph results
|
|
45
|
+
top_k: Maximum number of items to return
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
49
|
+
- Tracker: DialogueRankingTracker instance
|
|
50
|
+
- original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
51
|
+
- documents: List of text documents ready for ranking
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
original_items = {}
|
|
55
|
+
tracker = DialogueRankingTracker()
|
|
56
|
+
documents = []
|
|
57
|
+
documents_set = set()
|
|
58
|
+
for item in graph_results:
|
|
59
|
+
memory = getattr(item, "memory", None)
|
|
60
|
+
if isinstance(memory, str):
|
|
61
|
+
memory = _TAG1.sub("", memory)
|
|
62
|
+
|
|
63
|
+
chunk_text = ""
|
|
64
|
+
if hasattr(item, "metadata") and hasattr(item.metadata, "sources"):
|
|
65
|
+
sources = getattr(item.metadata, "sources", [])
|
|
66
|
+
for source in sources:
|
|
67
|
+
if source.type == "file":
|
|
68
|
+
chunk_text += source.content
|
|
69
|
+
if chunk_text:
|
|
70
|
+
if chunk_text in documents_set:
|
|
71
|
+
continue
|
|
72
|
+
else:
|
|
73
|
+
documents_set.add(chunk_text)
|
|
74
|
+
documents.append(f"{memory}\n\n[Sources]:\n{chunk_text}")
|
|
75
|
+
else:
|
|
76
|
+
documents.append(memory)
|
|
77
|
+
return tracker, original_items, documents
|
|
78
|
+
|
|
79
|
+
def reconstruct_items(
|
|
80
|
+
self,
|
|
81
|
+
ranked_indices: list[int],
|
|
82
|
+
scores: list[float],
|
|
83
|
+
tracker: DialogueRankingTracker,
|
|
84
|
+
original_items: dict[str, Any],
|
|
85
|
+
top_k: int,
|
|
86
|
+
**kwargs,
|
|
87
|
+
) -> list[tuple[Any, float]]:
|
|
88
|
+
"""
|
|
89
|
+
Reconstruct TextualMemoryItem objects from ranked dialogue pairs.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
ranked_indices: List of dialogue pair indices sorted by relevance
|
|
93
|
+
scores: Corresponding relevance scores
|
|
94
|
+
tracker: DialogueRankingTracker instance
|
|
95
|
+
original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
96
|
+
top_k: Maximum number of items to return
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
List of (reconstructed_memory_item, aggregated_score) tuples
|
|
100
|
+
"""
|
|
101
|
+
graph_results = kwargs.get("graph_results")
|
|
102
|
+
documents = kwargs.get("documents")
|
|
103
|
+
reconstructed_items = []
|
|
104
|
+
for idx in ranked_indices:
|
|
105
|
+
item = graph_results[idx]
|
|
106
|
+
item.memory = f"{documents[idx]}"
|
|
107
|
+
reconstructed_items.append((item, scores[idx]))
|
|
108
|
+
|
|
109
|
+
reconstructed_items.sort(key=lambda x: x[1], reverse=True)
|
|
110
|
+
return reconstructed_items[:top_k]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from memos.memories.textual.item import SourceMessage, TextualMemoryItem
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Strip a leading "[...]" tag (e.g., "[2025-09-01] ..." or "[meta] ...")
|
|
13
|
+
# before sending text to the reranker. This keeps inputs clean and
|
|
14
|
+
# avoids misleading the model with bracketed prefixes.
|
|
15
|
+
_TAG1 = re.compile(r"^\s*\[[^\]]*\]\s*")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def strip_memory_tags(item: TextualMemoryItem) -> str:
|
|
19
|
+
"""Strip leading tags from memory text."""
|
|
20
|
+
memory = _TAG1.sub("", m) if isinstance((m := getattr(item, "memory", None)), str) else m
|
|
21
|
+
return memory
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def extract_content(msg: dict[str, Any] | str) -> str:
|
|
25
|
+
"""Extract content from message, handling both string and dict formats."""
|
|
26
|
+
if isinstance(msg, dict):
|
|
27
|
+
return msg.get("content", str(msg))
|
|
28
|
+
if isinstance(msg, SourceMessage):
|
|
29
|
+
return msg.content
|
|
30
|
+
return str(msg)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DialoguePair(BaseModel):
|
|
34
|
+
"""Represents a single dialogue pair extracted from sources."""
|
|
35
|
+
|
|
36
|
+
pair_id: str # Unique identifier for this dialogue pair
|
|
37
|
+
memory_id: str # ID of the source TextualMemoryItem
|
|
38
|
+
memory: str
|
|
39
|
+
pair_index: int # Index of this pair within the source memory's dialogue
|
|
40
|
+
user_msg: str | dict[str, Any] | SourceMessage # User message content
|
|
41
|
+
assistant_msg: str | dict[str, Any] | SourceMessage # Assistant message content
|
|
42
|
+
combined_text: str # The concatenated text used for ranking
|
|
43
|
+
chat_time: str | None = None
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def user_content(self) -> str:
|
|
47
|
+
"""Get user message content as string."""
|
|
48
|
+
return extract_content(self.user_msg)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def assistant_content(self) -> str:
|
|
52
|
+
"""Get assistant message content as string."""
|
|
53
|
+
return extract_content(self.assistant_msg)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DialogueRankingTracker:
|
|
57
|
+
"""Tracks dialogue pairs and their rankings for memory reconstruction."""
|
|
58
|
+
|
|
59
|
+
def __init__(self):
|
|
60
|
+
self.dialogue_pairs: list[DialoguePair] = []
|
|
61
|
+
|
|
62
|
+
def add_dialogue_pair(
|
|
63
|
+
self,
|
|
64
|
+
memory_id: str,
|
|
65
|
+
pair_index: int,
|
|
66
|
+
user_msg: str | dict[str, Any],
|
|
67
|
+
assistant_msg: str | dict[str, Any],
|
|
68
|
+
memory: str,
|
|
69
|
+
chat_time: str | None = None,
|
|
70
|
+
concat_format: Literal["user_assistant", "user_only"] = "user_assistant",
|
|
71
|
+
) -> str:
|
|
72
|
+
"""Add a dialogue pair and return its unique ID."""
|
|
73
|
+
user_content = extract_content(user_msg)
|
|
74
|
+
assistant_content = extract_content(assistant_msg)
|
|
75
|
+
if concat_format == "user_assistant":
|
|
76
|
+
combined_text = f"[{chat_time}]: \nuser: {user_content}\nassistant: {assistant_content}"
|
|
77
|
+
elif concat_format == "user_only":
|
|
78
|
+
combined_text = f"[{chat_time}]: \nuser: {user_content}"
|
|
79
|
+
else:
|
|
80
|
+
raise ValueError(f"Invalid concat format: {concat_format}")
|
|
81
|
+
|
|
82
|
+
pair_id = f"{memory_id}_{pair_index}"
|
|
83
|
+
|
|
84
|
+
dialogue_pair = DialoguePair(
|
|
85
|
+
pair_id=pair_id,
|
|
86
|
+
memory_id=memory_id,
|
|
87
|
+
pair_index=pair_index,
|
|
88
|
+
user_msg=user_msg,
|
|
89
|
+
assistant_msg=assistant_msg,
|
|
90
|
+
combined_text=combined_text,
|
|
91
|
+
memory=memory,
|
|
92
|
+
chat_time=chat_time,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
self.dialogue_pairs.append(dialogue_pair)
|
|
96
|
+
return pair_id
|
|
97
|
+
|
|
98
|
+
def get_documents_for_ranking(self, concat_memory: bool = True) -> list[str]:
|
|
99
|
+
"""Get the combined text documents for ranking."""
|
|
100
|
+
if concat_memory:
|
|
101
|
+
return [(pair.memory + "\n\n" + pair.combined_text) for pair in self.dialogue_pairs]
|
|
102
|
+
else:
|
|
103
|
+
return [pair.combined_text for pair in self.dialogue_pairs]
|
|
104
|
+
|
|
105
|
+
def get_dialogue_pair_by_index(self, index: int) -> DialoguePair | None:
|
|
106
|
+
"""Get dialogue pair by its index in the ranking results."""
|
|
107
|
+
if 0 <= index < len(self.dialogue_pairs):
|
|
108
|
+
return self.dialogue_pairs[index]
|
|
109
|
+
return None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# memos/reranker/factory.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
5
|
+
|
|
6
|
+
from .concat_background import ConcatBackgroundStrategy
|
|
7
|
+
from .concat_docsource import ConcatDocSourceStrategy
|
|
8
|
+
from .single_turn import SingleTurnStrategy
|
|
9
|
+
from .singleturn_outmem import SingleTurnOutMemStrategy
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from .base import BaseRerankerStrategy
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RerankerStrategyFactory:
|
|
17
|
+
"""Factory class for creating reranker strategy instances."""
|
|
18
|
+
|
|
19
|
+
backend_to_class: ClassVar[dict[str, Any]] = {
|
|
20
|
+
"single_turn": SingleTurnStrategy,
|
|
21
|
+
"concat_background": ConcatBackgroundStrategy,
|
|
22
|
+
"singleturn_outmem": SingleTurnOutMemStrategy,
|
|
23
|
+
"concat_docsource": ConcatDocSourceStrategy,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def from_config(cls, config_factory: str = "single_turn") -> BaseRerankerStrategy:
|
|
28
|
+
if config_factory not in cls.backend_to_class:
|
|
29
|
+
raise ValueError(f"Invalid backend: {config_factory}")
|
|
30
|
+
strategy_class = cls.backend_to_class[config_factory]
|
|
31
|
+
return strategy_class()
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# memos/reranker/strategies/single_turn.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .base import BaseRerankerStrategy
|
|
8
|
+
from .dialogue_common import DialogueRankingTracker, extract_content, strip_memory_tags
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SingleTurnStrategy(BaseRerankerStrategy):
|
|
12
|
+
"""
|
|
13
|
+
Single turn dialogue strategy.
|
|
14
|
+
|
|
15
|
+
This strategy processes dialogue pairs by concatenating user and assistant
|
|
16
|
+
messages into single strings for ranking. Each dialogue pair becomes a
|
|
17
|
+
separate document for ranking.
|
|
18
|
+
example:
|
|
19
|
+
>>> documents = ["chat_time: 2025-01-01 12:00:00\nuser: hello\nassistant: hi there"]
|
|
20
|
+
>>> output memory item: ["Memory:xxx \n\n chat_time: 2025-01-01 12:00:00\nuser: hello\nassistant: hi there"]
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def prepare_documents(
|
|
24
|
+
self,
|
|
25
|
+
query: str,
|
|
26
|
+
graph_results: list,
|
|
27
|
+
top_k: int,
|
|
28
|
+
**kwargs,
|
|
29
|
+
) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
30
|
+
"""
|
|
31
|
+
Prepare documents based on single turn concatenation strategy.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
query: The search query
|
|
35
|
+
graph_results: List of graph results
|
|
36
|
+
top_k: Maximum number of items to return
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
40
|
+
- Tracker: DialogueRankingTracker instance
|
|
41
|
+
- original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
42
|
+
- documents: List of text documents ready for ranking
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
original_items = {}
|
|
46
|
+
tracker = DialogueRankingTracker()
|
|
47
|
+
for item in graph_results:
|
|
48
|
+
memory = strip_memory_tags(item)
|
|
49
|
+
sources = getattr(item.metadata, "sources", [])
|
|
50
|
+
original_items[item.id] = item
|
|
51
|
+
|
|
52
|
+
# Group messages into pairs and concatenate
|
|
53
|
+
for i in range(0, len(sources), 2):
|
|
54
|
+
user_msg = sources[i] if i < len(sources) else {}
|
|
55
|
+
assistant_msg = sources[i + 1] if i + 1 < len(sources) else {}
|
|
56
|
+
|
|
57
|
+
user_content = extract_content(user_msg)
|
|
58
|
+
assistant_content = extract_content(assistant_msg)
|
|
59
|
+
chat_time = getattr(user_msg, "chat_time", "")
|
|
60
|
+
|
|
61
|
+
if user_content or assistant_content: # Only add non-empty pairs
|
|
62
|
+
pair_index = i // 2
|
|
63
|
+
tracker.add_dialogue_pair(
|
|
64
|
+
item.id, pair_index, user_msg, assistant_msg, memory or "", chat_time
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
documents = tracker.get_documents_for_ranking()
|
|
68
|
+
return tracker, original_items, documents
|
|
69
|
+
|
|
70
|
+
def reconstruct_items(
|
|
71
|
+
self,
|
|
72
|
+
ranked_indices: list[int],
|
|
73
|
+
scores: list[float],
|
|
74
|
+
tracker: DialogueRankingTracker,
|
|
75
|
+
original_items: dict[str, Any],
|
|
76
|
+
top_k: int,
|
|
77
|
+
**kwargs,
|
|
78
|
+
) -> list[tuple[Any, float]]:
|
|
79
|
+
"""
|
|
80
|
+
Reconstruct TextualMemoryItem objects from ranked dialogue pairs.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
ranked_indices: List of dialogue pair indices sorted by relevance
|
|
84
|
+
scores: Corresponding relevance scores
|
|
85
|
+
tracker: DialogueRankingTracker instance
|
|
86
|
+
original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
87
|
+
top_k: Maximum number of items to return
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
List of (reconstructed_memory_item, aggregated_score) tuples
|
|
91
|
+
"""
|
|
92
|
+
reconstructed_items = []
|
|
93
|
+
for idx, score in zip(ranked_indices, scores, strict=False):
|
|
94
|
+
dialogue_pair = tracker.get_dialogue_pair_by_index(idx)
|
|
95
|
+
if dialogue_pair and (dialogue_pair.memory_id in original_items):
|
|
96
|
+
original_item = original_items[dialogue_pair.memory_id]
|
|
97
|
+
reconstructed_item = deepcopy(original_item)
|
|
98
|
+
reconstructed_item.memory = (
|
|
99
|
+
dialogue_pair.memory
|
|
100
|
+
+ "\n\nsources-dialogue-pairs"
|
|
101
|
+
+ dialogue_pair.combined_text
|
|
102
|
+
)
|
|
103
|
+
reconstructed_items.append((reconstructed_item, score))
|
|
104
|
+
|
|
105
|
+
# Sort by aggregated score and return top_k
|
|
106
|
+
reconstructed_items.sort(key=lambda x: x[1], reverse=True)
|
|
107
|
+
return reconstructed_items[:top_k]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# memos/reranker/strategies/single_turn.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from .dialogue_common import DialogueRankingTracker
|
|
8
|
+
from .single_turn import SingleTurnStrategy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .dialogue_common import DialogueRankingTracker
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SingleTurnOutMemStrategy(SingleTurnStrategy):
|
|
16
|
+
"""
|
|
17
|
+
Single turn dialogue strategy.
|
|
18
|
+
|
|
19
|
+
This strategy processes dialogue pairs by concatenating user and assistant
|
|
20
|
+
messages into single strings for ranking. Each dialogue pair becomes a
|
|
21
|
+
separate document for ranking.
|
|
22
|
+
example:
|
|
23
|
+
>>> documents = ["chat_time: 2025-01-01 12:00:00\nuser: hello\nassistant: hi there"]
|
|
24
|
+
>>> output memory item: ["Memory:xxx \n\n chat_time: 2025-01-01 12:00:00\nuser: hello\nassistant: hi there"]
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def prepare_documents(
|
|
28
|
+
self,
|
|
29
|
+
query: str,
|
|
30
|
+
graph_results: list,
|
|
31
|
+
top_k: int,
|
|
32
|
+
**kwargs,
|
|
33
|
+
) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
34
|
+
"""
|
|
35
|
+
Prepare documents based on single turn concatenation strategy.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
query: The search query
|
|
39
|
+
graph_results: List of graph results
|
|
40
|
+
top_k: Maximum number of items to return
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
|
|
44
|
+
- Tracker: DialogueRankingTracker instance
|
|
45
|
+
- original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
46
|
+
- documents: List of text documents ready for ranking
|
|
47
|
+
"""
|
|
48
|
+
return super().prepare_documents(query, graph_results, top_k, **kwargs)
|
|
49
|
+
|
|
50
|
+
def reconstruct_items(
|
|
51
|
+
self,
|
|
52
|
+
ranked_indices: list[int],
|
|
53
|
+
scores: list[float],
|
|
54
|
+
tracker: DialogueRankingTracker,
|
|
55
|
+
original_items: dict[str, Any],
|
|
56
|
+
top_k: int,
|
|
57
|
+
**kwargs,
|
|
58
|
+
) -> list[tuple[Any, float]]:
|
|
59
|
+
"""
|
|
60
|
+
Reconstruct TextualMemoryItem objects from ranked dialogue pairs.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
ranked_indices: List of dialogue pair indices sorted by relevance
|
|
64
|
+
scores: Corresponding relevance scores
|
|
65
|
+
tracker: DialogueRankingTracker instance
|
|
66
|
+
original_items: Dict mapping memory_id to original TextualMemoryItem
|
|
67
|
+
top_k: Maximum number of items to return
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
List of (reconstructed_memory_item, aggregated_score) tuples
|
|
71
|
+
"""
|
|
72
|
+
# Group ranked pairs by memory_id
|
|
73
|
+
memory_groups = defaultdict(list)
|
|
74
|
+
memory_scores = defaultdict(list)
|
|
75
|
+
|
|
76
|
+
for idx, score in zip(ranked_indices, scores, strict=False):
|
|
77
|
+
dialogue_pair = tracker.get_dialogue_pair_by_index(idx)
|
|
78
|
+
if dialogue_pair:
|
|
79
|
+
memory_groups[dialogue_pair.memory_id].append(dialogue_pair)
|
|
80
|
+
memory_scores[dialogue_pair.memory_id].append(score)
|
|
81
|
+
|
|
82
|
+
reconstructed_items = []
|
|
83
|
+
|
|
84
|
+
for memory_id, _pairs in memory_groups.items():
|
|
85
|
+
if memory_id not in original_items:
|
|
86
|
+
continue
|
|
87
|
+
original_item = original_items[memory_id]
|
|
88
|
+
|
|
89
|
+
# Calculate aggregated score (e.g., max, mean, or weighted average)
|
|
90
|
+
pair_scores = memory_scores[memory_id]
|
|
91
|
+
|
|
92
|
+
aggregated_score = max(pair_scores) if pair_scores else 0.0
|
|
93
|
+
|
|
94
|
+
reconstructed_items.append((original_item, aggregated_score))
|
|
95
|
+
|
|
96
|
+
# Sort by aggregated score and return top_k
|
|
97
|
+
reconstructed_items.sort(key=lambda x: x[1], reverse=True)
|
|
98
|
+
return reconstructed_items[:top_k]
|
memos/settings.py
ADDED
|
File without changes
|