File size: 4,706 Bytes
6719409
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env python3
"""
OpenClaw 记忆同步脚本
将本地记忆文件同步至 Hugging Face Dataset,并从 Dataset 恢复。
"""

import os
import sys
import time
import logging
from pathlib import Path
from huggingface_hub import HfApi, hf_hub_download, upload_file, list_repo_files

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class MemorySync:
    def __init__(self):
        self.hf_token = os.getenv('HF_TOKEN')
        # 请将下面的 'your-username/openclaw-memory' 替换成你实际创建的 Dataset 名字
        self.hf_dataset = os.getenv('HF_MEMORY_DATASET', 'a8926764/openclaw-memory')
        # 确保这个路径和启动脚本中设置的 OPENCLAW_MEMORY_DIR 一致
        self.local_memory_dir = Path(os.getenv('OPENCLAW_MEMORY_DIR', '/root/.openclaw/memory'))
        self.api = HfApi(token=self.hf_token)

    def ensure_memory_dir(self):
        """确保本地记忆目录存在"""
        self.local_memory_dir.mkdir(parents=True, exist_ok=True)
        logger.info(f"Memory directory ensured: {self.local_memory_dir}")

    def download_memory(self):
        """从 Dataset 拉取所有记忆文件(.md 文件)"""
        self.ensure_memory_dir()
        try:
            logger.info(f'开始从 Dataset 下载记忆文件: {self.hf_dataset}')
            files = list_repo_files(repo_id=self.hf_dataset, repo_type="dataset")
            downloaded_count = 0
            for file_name in files:
                if file_name.endswith('.md'):
                    try:
                        local_path = hf_hub_download(
                            repo_id=self.hf_dataset,
                            filename=file_name,
                            repo_type="dataset",
                            token=self.hf_token,
                            local_dir=self.local_memory_dir
                        )
                        logger.info(f'已下载记忆文件: {file_name}')
                        downloaded_count += 1
                    except Exception as e:
                        logger.error(f'下载文件 {file_name} 失败: {e}')
                        continue
            if downloaded_count == 0:
                logger.info('Dataset 中未找到记忆文件,将使用新记忆。')
            else:
                logger.info(f'记忆文件下载完成,共下载 {downloaded_count} 个文件。')
            return True
        except Exception as e:
            logger.error(f'从 Dataset 拉取记忆失败: {e}')
            # 首次运行时 Dataset 可能为空,这不一定是错误,返回 True 允许流程继续
            return True

    def upload_memory(self):
        """推送本地所有 .md 记忆文件到 Dataset"""
        self.ensure_memory_dir()
        try:
            logger.info(f'开始推送记忆文件到 Dataset: {self.hf_dataset}')
            memory_files = list(self.local_memory_dir.glob('*.md'))
            if not memory_files:
                logger.warning('本地没有找到 .md 格式的记忆文件,跳过上传。')
                return False

            uploaded_count = 0
            for mem_file in memory_files:
                try:
                    upload_file(
                        path_or_fileobj=str(mem_file),
                        path_in_repo=mem_file.name,
                        repo_id=self.hf_dataset,
                        repo_type="dataset",
                        token=self.hf_token,
                        commit_message=f"自动同步记忆: {mem_file.name} - {time.strftime('%Y-%m-%d %H:%M:%S')}"
                    )
                    logger.info(f'已上传记忆文件: {mem_file.name}')
                    uploaded_count += 1
                except Exception as e:
                    logger.error(f'上传文件 {mem_file.name} 失败: {e}')
                    continue

            logger.info(f'记忆文件上传完成,共上传 {uploaded_count} 个文件。')
            return uploaded_count > 0
        except Exception as e:
            logger.error(f'推送记忆到 Dataset 失败: {e}')
            return False

def main():
    """主函数,处理命令行参数"""
    if len(sys.argv) != 2 or sys.argv[1] not in ['download', 'upload']:
        print('用法: python memory_sync.py [download|upload]')
        sys.exit(1)

    sync = MemorySync()
    mode = sys.argv[1]

    if mode == 'download':
        success = sync.download_memory()
        sys.exit(0 if success else 1)
    elif mode == 'upload':
        success = sync.upload_memory()
        sys.exit(0 if success else 1)

if __name__ == '__main__':
    main()