Merge pull request #2822 from wayyoungboy/add-powermem

fix: powermem sqlite支持用户画像以及修改配置模式
This commit is contained in:
Sakura-RanChen
2026-01-09 14:46:31 +08:00
committed by GitHub
4 changed files with 239 additions and 181 deletions
+129 -105
View File
@@ -46,36 +46,58 @@ selected_module:
Memory: Memory:
powermem: powermem:
type: powermem type: powermem
# 是否启用用户画像功能(需要OceanBase # 是否启用用户画像功能
enable_user_profile: false # 用户画像支持: oceanbase、seekdb、sqlite (powermem 0.3.0+)
# 数据库提供者: oceanbase(推荐,最佳性能), seekdb, postgres, sqlite(轻量备选) enable_user_profile: true
database_provider: sqlite # 资源充足时建议使用 oceanbase 或 seekdb
# LLM提供者: qwen(默认), openai, 等 # ========== LLM 配置 ==========
llm_provider: qwen llm:
# 嵌入模型提供者: qwen(默认), openai, 等 provider: openai # 可选: qwen, openai, zhipu
embedding_provider: qwen config:
# LLM配置 api_key: 你的LLM API密钥
llm_api_key: 你的LLM API密钥 model: qwen-plus
llm_model: qwen-plus # openai_base_url: https://api.openai.com/v1 # 可选,自定义服务地址
# 嵌入模型配置
embedding_api_key: 你的嵌入模型API密钥 # ========== Embedding 配置 ==========
embedding_model: text-embedding-v3 embedder:
provider: openai # 可选: qwen, openai 等
config:
api_key: 你的嵌入模型API密钥
model: text-embedding-v4
openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
# ========== Database 配置 ==========
vector_store:
provider: sqlite # 可选: oceanbase(推荐), seekdb(推荐), postgres, sqlite(轻量)
config: {} # SQLite 无需额外配置
``` ```
### 配置参数详解 ### 配置参数详解
| 参数 | 说明 | 默认值 | 可选值 | #### LLM 配置
|------|------|--------|--------|
| `enable_user_profile` | 启用用户画像模式 | `false` | `true`(需OceanBase), `false` | | 参数 | 说明 | 可选值 |
| `database_provider` | 存储后端类型 | `sqlite` | `oceanbase`(推荐), `seekdb`, `postgres`, `sqlite`(轻量) | |------|------|--------|
| `llm_provider` | LLM 提供商 | `qwen` | `qwen`, `zhipu`(免费), `openai`, 等 | | `llm.provider` | LLM 提供商 | `qwen`, `openai`, `zhipu` 等 |
| `embedding_provider` | 嵌入模型提供商 | `qwen` | `qwen`, `zhipu`, `openai`, 等 | | `llm.config.api_key` | API 密钥 | - |
| `llm_api_key` | LLM API 密钥 | - | - | | `llm.config.model` | 模型名称 | 根据提供商选择 |
| `llm_model` | LLM 模型名称 | - | 根据提供商选择 | | `llm.config.openai_base_url` | 自定义服务地址(可选) | - |
| `llm_base_url` | LLM API 地址(可选) | - | - |
| `embedding_api_key` | 嵌入模型 API 密钥 | - | - | #### Embedding 配置
| `embedding_model` | 嵌入模型名称 | - | 根据提供商选择 |
| `embedding_base_url` | 嵌入模型 API 地址(可选) | - | - | | 参数 | 说明 | 可选值 |
|------|------|--------|
| `embedder.provider` | 嵌入模型提供商 | `qwen`, `openai` 等 |
| `embedder.config.api_key` | API 密钥 | - |
| `embedder.config.model` | 模型名称 | 根据提供商选择 |
| `embedder.config.openai_base_url` | 自定义服务地址(可选) | - |
#### Database 配置
| 参数 | 说明 | 可选值 |
|------|------|--------|
| `vector_store.provider` | 存储后端类型 | `oceanbase`(推荐), `seekdb`(推荐), `postgres`, `sqlite`(轻量) |
| `vector_store.config` | 数据库连接配置 | 根据 provider 设置 |
### 记忆模式说明 ### 记忆模式说明
@@ -84,7 +106,9 @@ PowerMem 支持两种记忆模式:
| 模式 | 配置 | 功能 | 存储要求 | | 模式 | 配置 | 功能 | 存储要求 |
|------|------|------|----------| |------|------|------|----------|
| **普通记忆** | `enable_user_profile: false` | 对话记忆存储与检索 | 支持所有数据库 | | **普通记忆** | `enable_user_profile: false` | 对话记忆存储与检索 | 支持所有数据库 |
| **用户画像** | `enable_user_profile: true` | 记忆 + 自动提取用户画像 | 仅支持 OceanBase | | **用户画像** | `enable_user_profile: true` | 记忆 + 自动提取用户画像 | oceanbase、seekdb、sqlite |
> 📌 **版本说明**PowerMem 0.3.0+ 版本,用户画像功能支持 OceanBase、SeekDB、SQLite 三种存储后端。
### 使用通义千问(推荐) ### 使用通义千问(推荐)
@@ -96,13 +120,21 @@ PowerMem 支持两种记忆模式:
Memory: Memory:
powermem: powermem:
type: powermem type: powermem
database_provider: sqlite enable_user_profile: true
llm_provider: qwen llm:
embedding_provider: qwen provider: qwen
llm_api_key: sk-xxxxxxxxxxxxxxxx config:
llm_model: qwen-plus api_key: sk-xxxxxxxxxxxxxxxx
embedding_api_key: sk-xxxxxxxxxxxxxxxx model: qwen-plus
embedding_model: text-embedding-v3 embedder:
provider: openai
config:
api_key: sk-xxxxxxxxxxxxxxxx
model: text-embedding-v4
openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
vector_store:
provider: sqlite
config: {}
``` ```
### 使用智谱免费 LLM(完全免费方案) ### 使用智谱免费 LLM(完全免费方案)
@@ -117,15 +149,22 @@ Memory:
Memory: Memory:
powermem: powermem:
type: powermem type: powermem
database_provider: sqlite enable_user_profile: true
llm_provider: zhipu llm:
embedding_provider: zhipu provider: openai # 使用 openai 兼容模式
llm_api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx config:
llm_model: glm-4-flash api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx
llm_base_url: https://open.bigmodel.cn/api/paas/v4/ model: glm-4-flash
embedding_api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx openai_base_url: https://open.bigmodel.cn/api/paas/v4/
embedding_model: embedding-3 embedder:
embedding_base_url: https://open.bigmodel.cn/api/paas/v4/ provider: openai
config:
api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx
model: embedding-3
openai_base_url: https://open.bigmodel.cn/api/paas/v4/
vector_store:
provider: sqlite
config: {}
``` ```
### 使用 OpenAI ### 使用 OpenAI
@@ -134,15 +173,22 @@ Memory:
Memory: Memory:
powermem: powermem:
type: powermem type: powermem
database_provider: sqlite enable_user_profile: true
llm_provider: openai llm:
embedding_provider: openai provider: openai
llm_api_key: sk-xxxxxxxxxxxxxxxx config:
llm_model: gpt-4o-mini api_key: sk-xxxxxxxxxxxxxxxx
llm_base_url: https://api.openai.com/v1 model: gpt-4o-mini
embedding_api_key: sk-xxxxxxxxxxxxxxxx openai_base_url: https://api.openai.com/v1
embedding_model: text-embedding-3-small embedder:
embedding_base_url: https://api.openai.com/v1 provider: openai
config:
api_key: sk-xxxxxxxxxxxxxxxx
model: text-embedding-3-small
openai_base_url: https://api.openai.com/v1
vector_store:
provider: sqlite
config: {}
``` ```
### 使用 OceanBase(最佳性能方案) ### 使用 OceanBase(最佳性能方案)
@@ -158,14 +204,18 @@ OceanBase 是 PowerMem 的最佳搭档,可实现最大性能释放:
Memory: Memory:
powermem: powermem:
type: powermem type: powermem
database_provider: oceanbase enable_user_profile: true
llm_provider: qwen llm:
embedding_provider: qwen provider: qwen
llm_api_key: sk-xxxxxxxxxxxxxxxx config:
llm_model: qwen-plus api_key: sk-xxxxxxxxxxxxxxxx
embedding_api_key: sk-xxxxxxxxxxxxxxxx model: qwen-plus
embedding_model: text-embedding-v3 embedder:
# OceanBase 数据库连接配置 provider: openai
config:
api_key: sk-xxxxxxxxxxxxxxxx
model: text-embedding-v4
openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
vector_store: vector_store:
provider: oceanbase provider: oceanbase
config: config:
@@ -173,35 +223,9 @@ Memory:
port: 2881 port: 2881
user: root@test user: root@test
password: your_password password: your_password
database: powermem db_name: powermem
``` collection_name: memories # 默认值
embedding_model_dims: 1536 # 嵌入向量维度,必需参数
### 高级配置
如果需要更精细的控制,可以使用完整的配置结构:
```yaml
Memory:
powermem:
type: powermem
# 向量存储配置
vector_store:
provider: sqlite
config:
path: ./data/powermem.db
# LLM 配置
llm:
provider: qwen
config:
api_key: sk-xxxxxxxxxxxxxxxx
model: qwen-plus
# 嵌入模型配置
embedder:
provider: qwen
config:
api_key: sk-xxxxxxxxxxxxxxxx
model: text-embedding-v3
``` ```
## 设备记忆隔离 ## 设备记忆隔离
@@ -216,6 +240,8 @@ PowerMem 会自动使用设备 ID`device_id`)作为 `user_id` 进行记忆
PowerMem 提供 `UserMemory` 类,可自动从对话中提取用户画像信息。 PowerMem 提供 `UserMemory` 类,可自动从对话中提取用户画像信息。
> 📌 **版本说明**PowerMem 0.3.0+ 版本,用户画像功能支持 OceanBase、SeekDB、SQLite 三种存储后端。
### 启用用户画像 ### 启用用户画像
在配置中设置 `enable_user_profile: true` 即可启用: 在配置中设置 `enable_user_profile: true` 即可启用:
@@ -225,22 +251,20 @@ Memory:
powermem: powermem:
type: powermem type: powermem
enable_user_profile: true # 启用用户画像 enable_user_profile: true # 启用用户画像
database_provider: oceanbase # 必须使用 OceanBase llm:
llm_provider: qwen provider: qwen
embedding_provider: qwen
llm_api_key: sk-xxxxxxxxxxxxxxxx
llm_model: qwen-plus
embedding_api_key: sk-xxxxxxxxxxxxxxxx
embedding_model: text-embedding-v3
# OceanBase 数据库连接配置
vector_store:
provider: oceanbase
config: config:
host: 127.0.0.1 api_key: sk-xxxxxxxxxxxxxxxx
port: 2881 model: qwen-plus
user: root@test embedder:
password: your_password provider: openai
database: powermem config:
api_key: sk-xxxxxxxxxxxxxxxx
model: text-embedding-v4
openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
vector_store:
provider: sqlite # 用户画像支持: oceanbase、seekdb、sqlite
config: {}
``` ```
### 用户画像能力 ### 用户画像能力
@@ -258,7 +282,7 @@ Memory:
1. **用户画像**:用户的基本信息、兴趣爱好等 1. **用户画像**:用户的基本信息、兴趣爱好等
2. **相关记忆**:与当前对话相关的历史记忆 2. **相关记忆**:与当前对话相关的历史记忆
> ⚠️ **注意**`UserMemory` 功能需要 OceanBase 作为存储后端,其他数据库暂不支持 > **版本说明**PowerMem 0.3.0+ 版本,用户画像功能支持 OceanBase、SeekDB、SQLite 三种存储后端
## 与其他记忆组件的对比 ## 与其他记忆组件的对比
+38 -18
View File
@@ -284,26 +284,46 @@ Memory:
# 官网: https://www.powermem.ai/ # 官网: https://www.powermem.ai/
# 使用示例: https://github.com/oceanbase/powermem/tree/main/examples # 使用示例: https://github.com/oceanbase/powermem/tree/main/examples
type: powermem type: powermem
# 是否启用用户画像功能(需要OceanBase作为存储后端) # 是否启用用户画像功能
# - false: 使用普通记忆模式(AsyncMemory),支持所有数据库 # - false: 使用普通记忆模式(AsyncMemory)
# - true: 使用用户画像模式(UserMemory),自动提取用户信息,仅支持OceanBase # - true: 使用用户画像模式(UserMemory),自动提取用户信息
enable_user_profile: false # 用户画像功能支持: oceanbase、seekdb、sqlite (powermem 0.3.0+)
# 数据库提供者: oceanbase(推荐,最佳性能), seekdb(推荐,AI应用存储一体), postgres, sqlite(轻量备选) enable_user_profile: true
# 资源充足时建议使用 oceanbase 或 seekdb
# 注意:用户画像功能(enable_user_profile: true)仅支持oceanbase # ========== LLM 配置 ==========
database_provider: sqlite
# LLM提供者: qwen(默认), zhipu(免费glm-4-flash), openai, 等
llm_provider: qwen
# 嵌入模型提供者: qwen(默认), zhipu, openai, 等
embedding_provider: qwen
# LLM配置 (使用通义千问为例)
# 通义千问: https://bailian.console.aliyun.com/?apiKey=1#/api-key # 通义千问: https://bailian.console.aliyun.com/?apiKey=1#/api-key
# 智谱AI(免费): https://bigmodel.cn/usercenter/proj-mgmt/apikeys # 智谱AI(免费): https://bigmodel.cn/usercenter/proj-mgmt/apikeys
llm_api_key: 你的LLM API密钥 llm:
llm_model: qwen-plus provider: openai # 可选: qwen, openai, zhipu 等
# 嵌入模型配置 (使用通义千问为例) config:
embedding_api_key: 你的嵌入模型API密钥 api_key: 你的LLM API密钥
embedding_model: text-embedding-v3 model: qwen-plus
# openai_base_url: https://api.openai.com/v1 # 可选,自定义服务地址
# ========== Embedding 配置 ==========
embedder:
provider: openai # 可选: qwen, openai 等
config:
api_key: 你的嵌入模型API密钥
model: text-embedding-v4
openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
# ========== Database 配置 ==========
# oceanbase(推荐,最佳性能), seekdb(推荐,AI应用存储一体), postgres, sqlite(轻量备选)
# 用户画像功能支持: oceanbase、seekdb、sqlite
vector_store:
provider: sqlite
config: {} # SQLite 无需额外配置
# OceanBase 配置示例:
# provider: oceanbase
# config:
# host: 127.0.0.1
# port: 2881
# user: root@test
# password: your_password
# db_name: powermem
# collection_name: memories # 默认值
# embedding_model_dims: 1536 # 嵌入向量维度,必需参数
nomem: nomem:
# 不想使用记忆功能,可以使用nomem # 不想使用记忆功能,可以使用nomem
type: nomem type: nomem
@@ -10,6 +10,7 @@
@Author: wayyoungboy @Author: wayyoungboy
""" """
import asyncio
import traceback import traceback
from typing import Optional, Dict, Any from typing import Optional, Dict, Any
@@ -21,13 +22,13 @@ TAG = __name__
class MemoryProvider(MemoryProviderBase): class MemoryProvider(MemoryProviderBase):
""" """
PowerMem memory provider implementation. PowerMem memory provider implementation.
PowerMem is an open-source agent memory component that provides PowerMem is an open-source agent memory component that provides
efficient memory management for AI agents. efficient memory management for AI agents.
Supports multiple storage backends (sqlite, oceanbase, postgres), Supports multiple storage backends (sqlite, oceanbase, postgres),
LLM providers (qwen, openai, etc.) and embedding providers. LLM providers (qwen, openai, etc.) and embedding providers.
Config options: Config options:
- enable_user_profile: bool - Enable UserMemory for user profiling (requires OceanBase) - enable_user_profile: bool - Enable UserMemory for user profiling (requires OceanBase)
- database_provider: str - Storage backend (sqlite, oceanbase, postgres) - database_provider: str - Storage backend (sqlite, oceanbase, postgres)
@@ -40,7 +41,8 @@ class MemoryProvider(MemoryProviderBase):
self.use_powermem = False self.use_powermem = False
self.memory_client = None self.memory_client = None
self.enable_user_profile = False self.enable_user_profile = False
self.last_profile_content = "" # Cache for user profile from UserMemory
try: try:
# Check if user profile mode is enabled # Check if user profile mode is enabled
self.enable_user_profile = config.get("enable_user_profile", False) self.enable_user_profile = config.get("enable_user_profile", False)
@@ -49,21 +51,13 @@ class MemoryProvider(MemoryProviderBase):
database_provider = config.get("database_provider", "sqlite") database_provider = config.get("database_provider", "sqlite")
llm_provider = config.get("llm_provider", "qwen") llm_provider = config.get("llm_provider", "qwen")
embedding_provider = config.get("embedding_provider", "qwen") embedding_provider = config.get("embedding_provider", "qwen")
# UserMemory requires OceanBase
if self.enable_user_profile and database_provider not in ["oceanbase"]:
logger.bind(tag=TAG).warning(
f"UserMemory requires OceanBase as storage backend, but got {database_provider}. "
"Falling back to AsyncMemory mode."
)
self.enable_user_profile = False
# Build powermem configuration dict # Build powermem configuration dict
# PowerMem supports two config styles: # PowerMem supports two config styles:
# 1. powermem style: database, llm, embedding # 1. powermem style: database, llm, embedding
# 2. mem0 style: vector_store, llm, embedder # 2. mem0 style: vector_store, llm, embedder
powermem_config = {} powermem_config = {}
# Configure vector store / database # Configure vector store / database
if "vector_store" in config: if "vector_store" in config:
powermem_config["vector_store"] = config["vector_store"] powermem_config["vector_store"] = config["vector_store"]
@@ -74,7 +68,7 @@ class MemoryProvider(MemoryProviderBase):
"provider": database_provider, "provider": database_provider,
"config": {} "config": {}
} }
# Configure LLM # Configure LLM
if "llm" in config: if "llm" in config:
powermem_config["llm"] = config["llm"] powermem_config["llm"] = config["llm"]
@@ -84,13 +78,23 @@ class MemoryProvider(MemoryProviderBase):
llm_config["api_key"] = config["llm_api_key"] llm_config["api_key"] = config["llm_api_key"]
if "llm_model" in config: if "llm_model" in config:
llm_config["model"] = config["llm_model"] llm_config["model"] = config["llm_model"]
# Handle base_url based on provider type
# - qwen provider uses dashscope_base_url
# - openai provider uses openai_base_url
if "llm_base_url" in config: if "llm_base_url" in config:
llm_config["base_url"] = config["llm_base_url"] if llm_provider == "qwen":
llm_config["dashscope_base_url"] = config["llm_base_url"]
else:
llm_config["openai_base_url"] = config["llm_base_url"]
if "openai_base_url" in config:
llm_config["openai_base_url"] = config["openai_base_url"]
if "dashscope_base_url" in config:
llm_config["dashscope_base_url"] = config["dashscope_base_url"]
powermem_config["llm"] = { powermem_config["llm"] = {
"provider": llm_provider, "provider": llm_provider,
"config": llm_config "config": llm_config
} }
# Configure embedder # Configure embedder
if "embedder" in config: if "embedder" in config:
powermem_config["embedder"] = config["embedder"] powermem_config["embedder"] = config["embedder"]
@@ -100,13 +104,25 @@ class MemoryProvider(MemoryProviderBase):
embedder_config["api_key"] = config["embedding_api_key"] embedder_config["api_key"] = config["embedding_api_key"]
if "embedding_model" in config: if "embedding_model" in config:
embedder_config["model"] = config["embedding_model"] embedder_config["model"] = config["embedding_model"]
# Handle base_url based on provider type
# - qwen provider uses dashscope_base_url
# - openai provider uses openai_base_url
# Priority: embedding_xxx_base_url > embedding_base_url > xxx_base_url
if "embedding_base_url" in config: if "embedding_base_url" in config:
embedder_config["base_url"] = config["embedding_base_url"] if embedding_provider == "qwen":
embedder_config["dashscope_base_url"] = config["embedding_base_url"]
else:
embedder_config["openai_base_url"] = config["embedding_base_url"]
# Embedding-specific base_url (higher priority)
if "embedding_openai_base_url" in config:
embedder_config["openai_base_url"] = config["embedding_openai_base_url"]
if "embedding_dashscope_base_url" in config:
embedder_config["dashscope_base_url"] = config["embedding_dashscope_base_url"]
powermem_config["embedder"] = { powermem_config["embedder"] = {
"provider": embedding_provider, "provider": embedding_provider,
"config": embedder_config "config": embedder_config
} }
# Initialize memory client based on mode # Initialize memory client based on mode
if self.enable_user_profile: if self.enable_user_profile:
from powermem import UserMemory from powermem import UserMemory
@@ -116,7 +132,7 @@ class MemoryProvider(MemoryProviderBase):
from powermem import AsyncMemory from powermem import AsyncMemory
self.memory_client = AsyncMemory(config=powermem_config) self.memory_client = AsyncMemory(config=powermem_config)
memory_mode = "AsyncMemory (普通记忆模式)" memory_mode = "AsyncMemory (普通记忆模式)"
self.use_powermem = True self.use_powermem = True
logger.bind(tag=TAG).info( logger.bind(tag=TAG).info(
@@ -140,6 +156,7 @@ class MemoryProvider(MemoryProviderBase):
Args: Args:
msgs: List of message objects with 'role' and 'content' attributes msgs: List of message objects with 'role' and 'content' attributes
session_id: Session identifier (optional, for compatibility) session_id: Session identifier (optional, for compatibility)
Returns: Returns:
@@ -148,7 +165,7 @@ class MemoryProvider(MemoryProviderBase):
if not self.use_powermem or self.memory_client is None: if not self.use_powermem or self.memory_client is None:
logger.bind(tag=TAG).warning("PowerMem is not available, skipping save_memory") logger.bind(tag=TAG).warning("PowerMem is not available, skipping save_memory")
return None return None
if len(msgs) < 2: if len(msgs) < 2:
logger.bind(tag=TAG).debug("Not enough messages to save (need at least 2)") logger.bind(tag=TAG).debug("Not enough messages to save (need at least 2)")
return None return None
@@ -160,16 +177,26 @@ class MemoryProvider(MemoryProviderBase):
for message in msgs for message in msgs
if message.role != "system" if message.role != "system"
] ]
# Add memory using PowerMem SDK # Add memory using PowerMem SDK
result = await self.memory_client.add( result = self.memory_client.add(
messages=messages, messages=messages,
user_id=self.role_id user_id=self.role_id
) )
# Handle both sync and async returns
if asyncio.iscoroutine(result):
result = await result
logger.bind(tag=TAG).debug(f"Save memory result: {result}") logger.bind(tag=TAG).debug(f"Save memory result: {result}")
# Cache user profile if UserMemory mode and profile was extracted
if self.enable_user_profile and result:
if result.get('profile_extracted'):
self.last_profile_content = result.get('profile_content', '')
logger.bind(tag=TAG).debug(f"User profile extracted: {self.last_profile_content}")
return result return result
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"Error saving memory: {str(e)}") logger.bind(tag=TAG).error(f"Error saving memory: {str(e)}")
logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}") logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}")
@@ -178,24 +205,24 @@ class MemoryProvider(MemoryProviderBase):
async def query_memory(self, query: str) -> str: async def query_memory(self, query: str) -> str:
""" """
Query memories from PowerMem based on similarity search. Query memories from PowerMem based on similarity search.
Args: Args:
query: The search query string query: The search query string
Returns: Returns:
Formatted string of relevant memories or empty string if none found Formatted string of relevant memories or empty string if none found
""" """
if not self.use_powermem or self.memory_client is None: if not self.use_powermem or self.memory_client is None:
logger.bind(tag=TAG).warning("PowerMem is not available, skipping query_memory") logger.bind(tag=TAG).warning("PowerMem is not available, skipping query_memory")
return "" return ""
try: try:
if not getattr(self, "role_id", None): if not getattr(self, "role_id", None):
logger.bind(tag=TAG).debug("No role_id set, returning empty memory") logger.bind(tag=TAG).debug("No role_id set, returning empty memory")
return "" return ""
result_parts = [] result_parts = []
# If user profile mode is enabled, include user profile in results # If user profile mode is enabled, include user profile in results
if self.enable_user_profile: if self.enable_user_profile:
profile = await self.get_user_profile() profile = await self.get_user_profile()
@@ -208,7 +235,7 @@ class MemoryProvider(MemoryProviderBase):
user_id=self.role_id, user_id=self.role_id,
limit=30 limit=30
) )
if results and "results" in results: if results and "results" in results:
# Format each memory entry with its update time # Format each memory entry with its update time
memories = [] memories = []
@@ -219,7 +246,7 @@ class MemoryProvider(MemoryProviderBase):
timestamp = str(entry["updated_at"]) timestamp = str(entry["updated_at"])
elif "created_at" in entry and entry["created_at"]: elif "created_at" in entry and entry["created_at"]:
timestamp = str(entry["created_at"]) timestamp = str(entry["created_at"])
if timestamp: if timestamp:
try: try:
# Parse and reformat the timestamp (remove milliseconds if present) # Parse and reformat the timestamp (remove milliseconds if present)
@@ -232,7 +259,7 @@ class MemoryProvider(MemoryProviderBase):
formatted_time = timestamp formatted_time = timestamp
else: else:
formatted_time = "" formatted_time = ""
memory = entry.get("memory", "") or entry.get("content", "") memory = entry.get("memory", "") or entry.get("content", "")
if memory: if memory:
if formatted_time: if formatted_time:
@@ -252,7 +279,7 @@ class MemoryProvider(MemoryProviderBase):
final_result = "\n\n".join(result_parts) final_result = "\n\n".join(result_parts)
logger.bind(tag=TAG).debug(f"Query results: {final_result}") logger.bind(tag=TAG).debug(f"Query results: {final_result}")
return final_result return final_result
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"Error querying memory: {str(e)}") logger.bind(tag=TAG).error(f"Error querying memory: {str(e)}")
logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}") logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}")
@@ -262,35 +289,22 @@ class MemoryProvider(MemoryProviderBase):
""" """
Get user profile from PowerMem (only available in UserMemory mode). Get user profile from PowerMem (only available in UserMemory mode).
In PowerMem 0.3.0+, user profile is automatically extracted during add()
and cached in last_profile_content.
Returns: Returns:
Formatted user profile string or empty string if not available Formatted user profile string or empty string if not available
""" """
if not self.use_powermem or self.memory_client is None: if not self.use_powermem or self.memory_client is None:
return "" return ""
if not self.enable_user_profile: if not self.enable_user_profile:
logger.bind(tag=TAG).debug("User profile mode is not enabled") logger.bind(tag=TAG).debug("User profile mode is not enabled")
return "" return ""
try: # Return cached profile content from last add() operation
if not getattr(self, "role_id", None): if self.last_profile_content:
return "" return self.last_profile_content
# Get user profile using UserMemory SDK return ""
profile = await self.memory_client.get_profile(user_id=self.role_id)
if not profile:
return ""
# Format profile as readable string
profile_parts = []
for key, value in profile.items():
if value:
profile_parts.append(f"- {key}: {value}")
return "\n".join(profile_parts)
except Exception as e:
logger.bind(tag=TAG).error(f"Error getting user profile: {str(e)}")
logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}")
return ""
+1 -1
View File
@@ -23,7 +23,7 @@ loguru==0.7.3
requests==2.32.5 requests==2.32.5
cozepy==0.20.0 cozepy==0.20.0
mem0ai==1.0.0 mem0ai==1.0.0
powermem>=0.2.1 powermem>=0.3.0
bs4==0.0.2 bs4==0.0.2
modelscope==1.32.0 modelscope==1.32.0
sherpa_onnx==1.12.17 sherpa_onnx==1.12.17