powermem 0.1.0__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 (123) hide show
  1. powermem/__init__.py +103 -0
  2. powermem/agent/__init__.py +35 -0
  3. powermem/agent/abstract/__init__.py +22 -0
  4. powermem/agent/abstract/collaboration.py +259 -0
  5. powermem/agent/abstract/context.py +187 -0
  6. powermem/agent/abstract/manager.py +232 -0
  7. powermem/agent/abstract/permission.py +217 -0
  8. powermem/agent/abstract/privacy.py +267 -0
  9. powermem/agent/abstract/scope.py +199 -0
  10. powermem/agent/agent.py +791 -0
  11. powermem/agent/components/__init__.py +18 -0
  12. powermem/agent/components/collaboration_coordinator.py +645 -0
  13. powermem/agent/components/permission_controller.py +586 -0
  14. powermem/agent/components/privacy_protector.py +767 -0
  15. powermem/agent/components/scope_controller.py +685 -0
  16. powermem/agent/factories/__init__.py +16 -0
  17. powermem/agent/factories/agent_factory.py +266 -0
  18. powermem/agent/factories/config_factory.py +308 -0
  19. powermem/agent/factories/memory_factory.py +229 -0
  20. powermem/agent/implementations/__init__.py +16 -0
  21. powermem/agent/implementations/hybrid.py +728 -0
  22. powermem/agent/implementations/multi_agent.py +1040 -0
  23. powermem/agent/implementations/multi_user.py +1020 -0
  24. powermem/agent/types.py +53 -0
  25. powermem/agent/wrappers/__init__.py +14 -0
  26. powermem/agent/wrappers/agent_memory_wrapper.py +427 -0
  27. powermem/agent/wrappers/compatibility_wrapper.py +520 -0
  28. powermem/config_loader.py +318 -0
  29. powermem/configs.py +249 -0
  30. powermem/core/__init__.py +19 -0
  31. powermem/core/async_memory.py +1493 -0
  32. powermem/core/audit.py +258 -0
  33. powermem/core/base.py +165 -0
  34. powermem/core/memory.py +1567 -0
  35. powermem/core/setup.py +162 -0
  36. powermem/core/telemetry.py +215 -0
  37. powermem/integrations/__init__.py +17 -0
  38. powermem/integrations/embeddings/__init__.py +13 -0
  39. powermem/integrations/embeddings/aws_bedrock.py +100 -0
  40. powermem/integrations/embeddings/azure_openai.py +55 -0
  41. powermem/integrations/embeddings/base.py +31 -0
  42. powermem/integrations/embeddings/config/base.py +132 -0
  43. powermem/integrations/embeddings/configs.py +31 -0
  44. powermem/integrations/embeddings/factory.py +48 -0
  45. powermem/integrations/embeddings/gemini.py +39 -0
  46. powermem/integrations/embeddings/huggingface.py +41 -0
  47. powermem/integrations/embeddings/langchain.py +35 -0
  48. powermem/integrations/embeddings/lmstudio.py +29 -0
  49. powermem/integrations/embeddings/mock.py +11 -0
  50. powermem/integrations/embeddings/ollama.py +53 -0
  51. powermem/integrations/embeddings/openai.py +49 -0
  52. powermem/integrations/embeddings/qwen.py +102 -0
  53. powermem/integrations/embeddings/together.py +31 -0
  54. powermem/integrations/embeddings/vertexai.py +54 -0
  55. powermem/integrations/llm/__init__.py +18 -0
  56. powermem/integrations/llm/anthropic.py +87 -0
  57. powermem/integrations/llm/base.py +132 -0
  58. powermem/integrations/llm/config/anthropic.py +56 -0
  59. powermem/integrations/llm/config/azure.py +56 -0
  60. powermem/integrations/llm/config/base.py +62 -0
  61. powermem/integrations/llm/config/deepseek.py +56 -0
  62. powermem/integrations/llm/config/ollama.py +56 -0
  63. powermem/integrations/llm/config/openai.py +79 -0
  64. powermem/integrations/llm/config/qwen.py +68 -0
  65. powermem/integrations/llm/config/qwen_asr.py +46 -0
  66. powermem/integrations/llm/config/vllm.py +56 -0
  67. powermem/integrations/llm/configs.py +26 -0
  68. powermem/integrations/llm/deepseek.py +106 -0
  69. powermem/integrations/llm/factory.py +118 -0
  70. powermem/integrations/llm/gemini.py +201 -0
  71. powermem/integrations/llm/langchain.py +65 -0
  72. powermem/integrations/llm/ollama.py +106 -0
  73. powermem/integrations/llm/openai.py +166 -0
  74. powermem/integrations/llm/openai_structured.py +80 -0
  75. powermem/integrations/llm/qwen.py +207 -0
  76. powermem/integrations/llm/qwen_asr.py +171 -0
  77. powermem/integrations/llm/vllm.py +106 -0
  78. powermem/integrations/rerank/__init__.py +20 -0
  79. powermem/integrations/rerank/base.py +43 -0
  80. powermem/integrations/rerank/config/__init__.py +7 -0
  81. powermem/integrations/rerank/config/base.py +27 -0
  82. powermem/integrations/rerank/configs.py +23 -0
  83. powermem/integrations/rerank/factory.py +68 -0
  84. powermem/integrations/rerank/qwen.py +159 -0
  85. powermem/intelligence/__init__.py +17 -0
  86. powermem/intelligence/ebbinghaus_algorithm.py +354 -0
  87. powermem/intelligence/importance_evaluator.py +361 -0
  88. powermem/intelligence/intelligent_memory_manager.py +284 -0
  89. powermem/intelligence/manager.py +148 -0
  90. powermem/intelligence/plugin.py +229 -0
  91. powermem/prompts/__init__.py +29 -0
  92. powermem/prompts/graph/graph_prompts.py +217 -0
  93. powermem/prompts/graph/graph_tools_prompts.py +469 -0
  94. powermem/prompts/importance_evaluation.py +246 -0
  95. powermem/prompts/intelligent_memory_prompts.py +163 -0
  96. powermem/prompts/templates.py +193 -0
  97. powermem/storage/__init__.py +14 -0
  98. powermem/storage/adapter.py +896 -0
  99. powermem/storage/base.py +109 -0
  100. powermem/storage/config/base.py +13 -0
  101. powermem/storage/config/oceanbase.py +58 -0
  102. powermem/storage/config/pgvector.py +52 -0
  103. powermem/storage/config/sqlite.py +27 -0
  104. powermem/storage/configs.py +159 -0
  105. powermem/storage/factory.py +59 -0
  106. powermem/storage/migration_manager.py +438 -0
  107. powermem/storage/oceanbase/__init__.py +8 -0
  108. powermem/storage/oceanbase/constants.py +162 -0
  109. powermem/storage/oceanbase/oceanbase.py +1384 -0
  110. powermem/storage/oceanbase/oceanbase_graph.py +1441 -0
  111. powermem/storage/pgvector/__init__.py +7 -0
  112. powermem/storage/pgvector/pgvector.py +420 -0
  113. powermem/storage/sqlite/__init__.py +0 -0
  114. powermem/storage/sqlite/sqlite.py +218 -0
  115. powermem/storage/sqlite/sqlite_vector_store.py +311 -0
  116. powermem/utils/__init__.py +35 -0
  117. powermem/utils/utils.py +605 -0
  118. powermem/version.py +23 -0
  119. powermem-0.1.0.dist-info/METADATA +187 -0
  120. powermem-0.1.0.dist-info/RECORD +123 -0
  121. powermem-0.1.0.dist-info/WHEEL +5 -0
  122. powermem-0.1.0.dist-info/licenses/LICENSE +206 -0
  123. powermem-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,171 @@
1
+ import logging
2
+ import os
3
+ from typing import Dict, List, Optional, Union
4
+
5
+ try:
6
+ import dashscope
7
+ from dashscope.api_entities.dashscope_response import DashScopeAPIResponse
8
+ except ImportError:
9
+ dashscope = None
10
+ DashScopeAPIResponse = None
11
+
12
+ from powermem.integrations.llm import LLMBase
13
+ from powermem.integrations.llm.config.base import BaseLLMConfig
14
+ from powermem.integrations.llm.config.qwen_asr import QwenASRConfig
15
+
16
+
17
+ class QwenASR(LLMBase):
18
+ """
19
+ Qwen ASR (Automatic Speech Recognition) integration.
20
+ Converts audio to text using Qwen ASR models.
21
+ """
22
+
23
+ def __init__(self, config: Optional[Union[BaseLLMConfig, QwenASRConfig, Dict]] = None):
24
+ # Check if dashscope is available first
25
+ if dashscope is None:
26
+ raise ImportError(
27
+ "DashScope SDK is not installed. Please install it with: pip install dashscope"
28
+ )
29
+
30
+ # Convert to QwenASRConfig if needed
31
+ if config is None:
32
+ config = QwenASRConfig()
33
+ elif isinstance(config, dict):
34
+ config = QwenASRConfig(**config)
35
+ elif isinstance(config, BaseLLMConfig) and not isinstance(config, QwenASRConfig):
36
+ # Convert BaseLLMConfig to QwenASRConfig (only use model and api_key for ASR)
37
+ config = QwenASRConfig(
38
+ model=config.model,
39
+ api_key=config.api_key,
40
+ )
41
+
42
+ super().__init__(config)
43
+
44
+ if not self.config.model:
45
+ self.config.model = "qwen3-asr-flash"
46
+
47
+ # Set API key
48
+ api_key = self.config.api_key or os.getenv("DASHSCOPE_API_KEY")
49
+ if not api_key:
50
+ raise ValueError(
51
+ "API key is required. Set DASHSCOPE_API_KEY environment variable or pass api_key in config."
52
+ )
53
+
54
+ # Set API key for DashScope SDK
55
+ dashscope.api_key = api_key
56
+
57
+ # Set base URL
58
+ base_url = self.config.dashscope_base_url or os.getenv(
59
+ "DASHSCOPE_BASE_URL") or "https://dashscope.aliyuncs.com/api/v1"
60
+
61
+ if base_url:
62
+ dashscope.base_http_api_url = base_url
63
+
64
+ def _parse_response(self, response: DashScopeAPIResponse) -> str:
65
+ """
66
+ Parse the ASR response and extract text content.
67
+
68
+ Args:
69
+ response: The raw response from DashScope API.
70
+
71
+ Returns:
72
+ str: The extracted text content.
73
+ """
74
+ if response.status_code != 200:
75
+ raise Exception(f"API request failed with status {response.status_code}: {response.message}")
76
+
77
+ try:
78
+ choices = response.output.choices
79
+ if choices:
80
+ content = choices[0].message.content
81
+ if content:
82
+ # Extract all text fields from content list
83
+ texts = [item["text"] for item in content if "text" in item]
84
+ # Join all text segments
85
+ return " ".join(texts)
86
+ return ""
87
+ except Exception as e:
88
+ logging.error(f"Error parsing ASR response: {e}")
89
+ raise
90
+
91
+ def generate_response(
92
+ self,
93
+ messages: List[Dict[str, Union[str, List]]],
94
+ **kwargs,
95
+ ):
96
+ """
97
+ Generate text response from audio input using Qwen ASR.
98
+
99
+ Args:
100
+ messages: List of message dicts. For ASR, messages should contain audio content.
101
+ Example:
102
+ [
103
+ {
104
+ "role": "system",
105
+ "content": [{"text": ""}]
106
+ },
107
+ {
108
+ "role": "user",
109
+ "content": [{"audio": "https://example.com/audio.wav"}]
110
+ }
111
+ ]
112
+ **kwargs: Additional ASR-specific parameters.
113
+
114
+ Returns:
115
+ str: The transcribed text from the audio.
116
+ """
117
+ # Prepare ASR parameters
118
+ asr_params = {
119
+ "api_key": self.config.api_key or os.getenv("DASHSCOPE_API_KEY"),
120
+ "model": self.config.model,
121
+ "messages": messages,
122
+ "result_format": self.config.result_format,
123
+ }
124
+
125
+ # Add ASR options
126
+ asr_options = kwargs.get("asr_options", self.config.asr_options)
127
+ if asr_options:
128
+ asr_params["asr_options"] = asr_options
129
+
130
+ # Add any other kwargs
131
+ for key, value in kwargs.items():
132
+ if key not in ["asr_options"]:
133
+ asr_params[key] = value
134
+
135
+ try:
136
+ response = dashscope.MultiModalConversation.call(**asr_params)
137
+ return self._parse_response(response)
138
+ except Exception as e:
139
+ logging.error(f"Qwen ASR API call failed: {e}")
140
+ raise
141
+
142
+ def transcribe(
143
+ self,
144
+ audio_url: str,
145
+ system_text: Optional[str] = "",
146
+ asr_options: Optional[dict] = None,
147
+ ) -> str:
148
+ """
149
+ Convenience method to transcribe audio from URL file path.
150
+
151
+ Args:
152
+ audio_url: URL file path to the audio file.
153
+ system_text: Optional system message text for customization context.
154
+ asr_options: Optional ASR-specific options (e.g., {"language": "zh", "enable_itn": True}).
155
+
156
+ Returns:
157
+ str: The transcribed text from the audio.
158
+ """
159
+ messages = [
160
+ {
161
+ "role": "system",
162
+ "content": [{"text": system_text}]
163
+ },
164
+ {
165
+ "role": "user",
166
+ "content": [{"audio": audio_url}]
167
+ }
168
+ ]
169
+
170
+ return self.generate_response(messages, asr_options=asr_options or self.config.asr_options)
171
+
@@ -0,0 +1,106 @@
1
+ import json
2
+ import os
3
+ from typing import Dict, List, Optional, Union
4
+
5
+ from openai import OpenAI
6
+ from powermem.integrations.llm import LLMBase
7
+ from powermem.integrations.llm.config.base import BaseLLMConfig
8
+ from powermem.integrations.llm.config.vllm import VllmConfig
9
+ from powermem.utils.utils import extract_json
10
+
11
+
12
+ class VllmLLM(LLMBase):
13
+ def __init__(self, config: Optional[Union[BaseLLMConfig, VllmConfig, Dict]] = None):
14
+ # Convert to VllmConfig if needed
15
+ if config is None:
16
+ config = VllmConfig()
17
+ elif isinstance(config, dict):
18
+ config = VllmConfig(**config)
19
+ elif isinstance(config, BaseLLMConfig) and not isinstance(config, VllmConfig):
20
+ # Convert BaseLLMConfig to VllmConfig
21
+ config = VllmConfig(
22
+ model=config.model,
23
+ temperature=config.temperature,
24
+ api_key=config.api_key,
25
+ max_tokens=config.max_tokens,
26
+ top_p=config.top_p,
27
+ top_k=config.top_k,
28
+ enable_vision=config.enable_vision,
29
+ vision_details=config.vision_details,
30
+ http_client_proxies=config.http_client,
31
+ )
32
+
33
+ super().__init__(config)
34
+
35
+ if not self.config.model:
36
+ self.config.model = "Qwen/Qwen2.5-32B-Instruct"
37
+
38
+ self.config.api_key = self.config.api_key or os.getenv("VLLM_API_KEY") or "vllm-api-key"
39
+ base_url = self.config.vllm_base_url or os.getenv("VLLM_BASE_URL")
40
+ self.client = OpenAI(api_key=self.config.api_key, base_url=base_url)
41
+
42
+ def _parse_response(self, response, tools):
43
+ """
44
+ Process the response based on whether tools are used or not.
45
+
46
+ Args:
47
+ response: The raw response from API.
48
+ tools: The list of tools provided in the request.
49
+
50
+ Returns:
51
+ str or dict: The processed response.
52
+ """
53
+ if tools:
54
+ processed_response = {
55
+ "content": response.choices[0].message.content,
56
+ "tool_calls": [],
57
+ }
58
+
59
+ if response.choices[0].message.tool_calls:
60
+ for tool_call in response.choices[0].message.tool_calls:
61
+ processed_response["tool_calls"].append(
62
+ {
63
+ "name": tool_call.function.name,
64
+ "arguments": json.loads(extract_json(tool_call.function.arguments)),
65
+ }
66
+ )
67
+
68
+ return processed_response
69
+ else:
70
+ return response.choices[0].message.content
71
+
72
+ def generate_response(
73
+ self,
74
+ messages: List[Dict[str, str]],
75
+ response_format=None,
76
+ tools: Optional[List[Dict]] = None,
77
+ tool_choice: str = "auto",
78
+ **kwargs,
79
+ ):
80
+ """
81
+ Generate a response based on the given messages using vLLM.
82
+
83
+ Args:
84
+ messages (list): List of message dicts containing 'role' and 'content'.
85
+ response_format (str or object, optional): Format of the response. Defaults to "text".
86
+ tools (list, optional): List of tools that the model can call. Defaults to None.
87
+ tool_choice (str, optional): Tool choice method. Defaults to "auto".
88
+ **kwargs: Additional vLLM-specific parameters.
89
+
90
+ Returns:
91
+ str: The generated response.
92
+ """
93
+ params = self._get_supported_params(messages=messages, **kwargs)
94
+ params.update(
95
+ {
96
+ "model": self.config.model,
97
+ "messages": messages,
98
+ }
99
+ )
100
+
101
+ if tools:
102
+ params["tools"] = tools
103
+ params["tool_choice"] = tool_choice
104
+
105
+ response = self.client.chat.completions.create(**params)
106
+ return self._parse_response(response, tools)
@@ -0,0 +1,20 @@
1
+ """
2
+ Rerank integration module
3
+
4
+ This module provides integration with various rerank services.
5
+ """
6
+
7
+ from .base import RerankBase
8
+ from .factory import RerankFactory
9
+ from .qwen import QwenRerank
10
+ from .config.base import BaseRerankConfig
11
+ from .configs import RerankConfig
12
+
13
+ __all__ = [
14
+ "RerankBase",
15
+ "RerankFactory",
16
+ "QwenRerank",
17
+ "BaseRerankConfig",
18
+ "RerankConfig",
19
+ ]
20
+
@@ -0,0 +1,43 @@
1
+ """
2
+ Base class for rerank models
3
+ """
4
+ from abc import ABC, abstractmethod
5
+ from typing import List, Optional, Tuple
6
+
7
+ from powermem.integrations.rerank.config.base import BaseRerankConfig
8
+
9
+
10
+ class RerankBase(ABC):
11
+ """Base class for rerank models
12
+
13
+ Args:
14
+ config (Optional[BaseRerankConfig]): Configuration for the rerank model
15
+ """
16
+
17
+ def __init__(self, config: Optional[BaseRerankConfig] = None):
18
+ if config is None:
19
+ self.config = BaseRerankConfig()
20
+ else:
21
+ self.config = config
22
+
23
+ @abstractmethod
24
+ def rerank(
25
+ self,
26
+ query: str,
27
+ documents: List[str],
28
+ top_n: Optional[int] = None
29
+ ) -> List[Tuple[int, float]]:
30
+ """
31
+ Rerank documents based on relevance to the query.
32
+
33
+ Args:
34
+ query (str): The search query
35
+ documents (List[str]): List of document texts to rerank
36
+ top_n (Optional[int]): Number of top results to return. If None, uses config.top_n
37
+
38
+ Returns:
39
+ List[Tuple[int, float]]: List of (document_index, relevance_score) tuples,
40
+ sorted by relevance score in descending order
41
+ """
42
+ pass
43
+
@@ -0,0 +1,7 @@
1
+ """
2
+ Rerank configuration module
3
+ """
4
+ from .base import BaseRerankConfig
5
+
6
+ __all__ = ["BaseRerankConfig"]
7
+
@@ -0,0 +1,27 @@
1
+ """
2
+ Base configuration for rerank models
3
+ """
4
+ from typing import Optional
5
+
6
+
7
+ class BaseRerankConfig:
8
+ """Base configuration for rerank models
9
+
10
+ Args:
11
+ model (str): The rerank model to use
12
+ api_key (Optional[str]): API key for the rerank service
13
+ """
14
+
15
+ def __init__(
16
+ self,
17
+ model: Optional[str] = None,
18
+ api_key: Optional[str] = None,
19
+ **kwargs,
20
+ ):
21
+ self.model = model
22
+ self.api_key = api_key
23
+
24
+ # Store any additional kwargs
25
+ for key, value in kwargs.items():
26
+ setattr(self, key, value)
27
+
@@ -0,0 +1,23 @@
1
+ """
2
+ Configuration for rerank models
3
+ """
4
+ from typing import Optional, Dict, Any
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class RerankConfig(BaseModel):
9
+ """Configuration for rerank functionality."""
10
+
11
+ enabled: bool = Field(
12
+ description="Whether to enable reranker",
13
+ default=False,
14
+ )
15
+ provider: str = Field(
16
+ description="Reranker provider (e.g., 'qwen', 'cohere')",
17
+ default="qwen",
18
+ )
19
+ config: Optional[Dict[str, Any]] = Field(
20
+ description="Configuration for the specific reranker provider",
21
+ default=None
22
+ )
23
+
@@ -0,0 +1,68 @@
1
+ """
2
+ Rerank factory for creating rerank instances
3
+
4
+ This module provides a factory for creating different rerank instances.
5
+ """
6
+
7
+ import importlib
8
+ from typing import Optional
9
+
10
+ from powermem.integrations.rerank.config.base import BaseRerankConfig
11
+
12
+
13
+ def load_class(class_type):
14
+ """Dynamically load a class from a string path"""
15
+ module_path, class_name = class_type.rsplit(".", 1)
16
+ module = importlib.import_module(module_path)
17
+ return getattr(module, class_name)
18
+
19
+
20
+ class RerankFactory:
21
+ """Factory class for creating rerank model instances
22
+ """
23
+
24
+ provider_to_class = {
25
+ "qwen": "powermem.integrations.rerank.qwen.QwenRerank",
26
+ }
27
+
28
+ @classmethod
29
+ def create(cls, provider_name: str = "qwen", config: Optional[dict] = None):
30
+ """
31
+ Create a rerank instance based on provider name.
32
+
33
+ Args:
34
+ provider_name (str): Name of the rerank provider. Defaults to "qwen"
35
+ config (Optional[dict]): Configuration dictionary for the rerank model
36
+
37
+ Returns:
38
+ RerankBase: An instance of the requested rerank provider
39
+
40
+ Raises:
41
+ ValueError: If the provider is not supported
42
+ """
43
+ class_type = cls.provider_to_class.get(provider_name)
44
+ if class_type:
45
+ reranker_class = load_class(class_type)
46
+ # Create config if provided
47
+ if config:
48
+ base_config = BaseRerankConfig(**config)
49
+ return reranker_class(base_config)
50
+ else:
51
+ return reranker_class()
52
+ else:
53
+ supported = ", ".join(cls.provider_to_class.keys())
54
+ raise ValueError(
55
+ f"Unsupported rerank provider: {provider_name}. "
56
+ f"Supported providers: {supported}"
57
+ )
58
+
59
+ @classmethod
60
+ def list_providers(cls) -> list:
61
+ """
62
+ List all supported rerank providers.
63
+
64
+ Returns:
65
+ list: List of supported provider names
66
+ """
67
+ return list(cls.provider_to_class.keys())
68
+
@@ -0,0 +1,159 @@
1
+ """
2
+ Qwen Rerank implementation using Alibaba Cloud DashScope API
3
+ """
4
+ import os
5
+ from typing import List, Optional, Tuple
6
+
7
+ try:
8
+ import dashscope
9
+ from dashscope import TextReRank
10
+ except ImportError:
11
+ TextReRank = None
12
+ dashscope = None
13
+
14
+ from powermem.integrations.rerank.base import RerankBase
15
+ from powermem.integrations.rerank.config.base import BaseRerankConfig
16
+
17
+
18
+ class QwenRerank(RerankBase):
19
+ """Qwen3 Rerank implementation using Alibaba Cloud Bailian API
20
+
21
+ This implementation uses the qwen3-rerank model through DashScope SDK.
22
+
23
+ Args:
24
+ config (Optional[BaseRerankConfig]): Configuration for the rerank model
25
+ """
26
+
27
+ def __init__(self, config: Optional[BaseRerankConfig] = None):
28
+ super().__init__(config)
29
+
30
+ # Set default model
31
+ self.config.model = self.config.model or "qwen3-rerank"
32
+
33
+ # Check if dashscope is available
34
+ if TextReRank is None or dashscope is None:
35
+ raise ImportError(
36
+ "DashScope SDK is not installed. Please install it with: pip install dashscope"
37
+ )
38
+
39
+ # Set API key
40
+ api_key = self.config.api_key or os.getenv("DASHSCOPE_API_KEY")
41
+ if not api_key:
42
+ raise ValueError(
43
+ "API key is required. Set DASHSCOPE_API_KEY environment variable or pass api_key in config."
44
+ )
45
+
46
+ # Set API key for DashScope SDK
47
+ dashscope.api_key = api_key
48
+
49
+ def rerank(
50
+ self,
51
+ query: str,
52
+ documents: List[str],
53
+ top_n: Optional[int] = None,
54
+ instruct: Optional[str] = None
55
+ ) -> List[Tuple[int, float]]:
56
+ """
57
+ Rerank documents based on relevance to the query using Qwen3 Rerank model.
58
+
59
+ Args:
60
+ query (str): The search query
61
+ documents (List[str]): List of document texts to rerank
62
+ top_n (Optional[int]): Number of top results to return. If None, uses config.top_n
63
+ instruct (Optional[str]): Instruct for rerank
64
+
65
+ Returns:
66
+ List[Tuple[int, float]]: List of (document_index, relevance_score) tuples,
67
+ sorted by relevance score in descending order
68
+
69
+ Raises:
70
+ ValueError: If query is empty or documents list is empty
71
+ Exception: If API call fails
72
+ """
73
+ # Validate inputs
74
+ if not query or not query.strip():
75
+ raise ValueError("Query cannot be empty")
76
+
77
+ if not documents or len(documents) == 0:
78
+ raise ValueError("Documents list cannot be empty")
79
+
80
+ # Clean query
81
+ query = query.strip()
82
+
83
+ # Use provided top_n or return all results
84
+ effective_top_n = top_n if top_n is not None else len(documents)
85
+
86
+ try:
87
+ # Call the Rerank API
88
+ if instruct is not None:
89
+ response = TextReRank.call(
90
+ model=self.config.model,
91
+ query=query,
92
+ documents=documents,
93
+ top_n=effective_top_n,
94
+ return_documents=False,
95
+ instruct=instruct
96
+ )
97
+ else:
98
+ response = TextReRank.call(
99
+ model=self.config.model,
100
+ query=query,
101
+ documents=documents,
102
+ top_n=effective_top_n,
103
+ return_documents=False,
104
+ )
105
+
106
+ # Check response status
107
+ if response.status_code != 200:
108
+ raise Exception(
109
+ f"Rerank API request failed with status {response.status_code}: {response.message}"
110
+ )
111
+
112
+ # Parse results
113
+ results = []
114
+ if hasattr(response, 'output') and isinstance(response.output, dict):
115
+ rerank_results = response.output.get('results', [])
116
+ for result in rerank_results:
117
+ index = result.get('index')
118
+ score = result.get('relevance_score', 0.0)
119
+ if index is not None:
120
+ results.append((index, float(score)))
121
+ else:
122
+ raise Exception("Unexpected response format from Rerank API")
123
+
124
+ return results
125
+
126
+ except Exception as e:
127
+ raise Exception(f"Failed to rerank documents: {e}")
128
+
129
+ def rerank_with_texts(
130
+ self,
131
+ query: str,
132
+ documents: List[str],
133
+ top_n: Optional[int] = None,
134
+ instruct: Optional[str] = None
135
+ ) -> List[Tuple[str, float]]:
136
+ """
137
+ Rerank documents and return texts with scores instead of indices.
138
+
139
+ Args:
140
+ query (str): The search query
141
+ documents (List[str]): List of document texts to rerank
142
+ top_n (Optional[int]): Number of top results to return
143
+ instruct (Optional[str]): Instruct for rerank
144
+
145
+ Returns:
146
+ List[Tuple[str, float]]: List of (document_text, relevance_score) tuples,
147
+ sorted by relevance score in descending order
148
+ """
149
+ # Get reranked indices and scores
150
+ reranked_results = self.rerank(query, documents, top_n,instruct)
151
+
152
+ # Map indices back to document texts
153
+ results_with_texts = [
154
+ (documents[idx], score)
155
+ for idx, score in reranked_results
156
+ ]
157
+
158
+ return results_with_texts
159
+
@@ -0,0 +1,17 @@
1
+ """
2
+ Intelligence layer for memory processing
3
+
4
+ This module provides intelligent memory processing capabilities.
5
+ """
6
+
7
+ from .manager import IntelligenceManager
8
+ from .intelligent_memory_manager import IntelligentMemoryManager
9
+ from .importance_evaluator import ImportanceEvaluator
10
+ from .ebbinghaus_algorithm import EbbinghausAlgorithm
11
+
12
+ __all__ = [
13
+ "IntelligenceManager",
14
+ "IntelligentMemoryManager",
15
+ "ImportanceEvaluator",
16
+ "EbbinghausAlgorithm",
17
+ ]