diff --git a/.arts/settings.json b/.arts/settings.json new file mode 100644 index 0000000..701b3b0 --- /dev/null +++ b/.arts/settings.json @@ -0,0 +1,3 @@ +{ + "diffEditor.renderSideBySide": false +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index f1ac40a..d816453 100644 --- a/.gitignore +++ b/.gitignore @@ -1,45 +1,62 @@ -ei'r# Python +# Python __pycache__/ *.py[cod] *$py.class *.so .Python -venv/ -env/ -ENV/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg -# Database -*.db -*.sqlite -*.sqlite3 +# Virtual Environment +venv/ +test_venv/ +ENV/ +env/ # IDE .vscode/ .idea/ *.swp *.swo +*~ -# CodeArts -.arts/ -.codeartsdoer/ -.codearts/ +# Database +*.db +*.sqlite +*.sqlite3 + +# Logs +*.log +logs/ # OS .DS_Store Thumbs.db -# Build -dist/ -build/ -*.spec - -# Logs -*.log - -# Environment +# Sensitive .env -.pytest_cache/ +*.key +*.pem +config.json -# Test -test_*.py -test_*.db +# Build +*.spec +build/ +dist/ + +# Temporary +*.tmp +*.bak diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..27a378e --- /dev/null +++ b/LICENSE @@ -0,0 +1,194 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2026 jianf + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +================================================================================ + +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2026 jianf + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of the program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its author. You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers. In our view, they should not +allow patents to restrict development and use of software on +general-purpose computers. But in those that do, we wish to avoid the +special danger that patents applied to a free program could make it +effectively proprietary. To prevent this, the GPL assures that patents +cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or menu items, or similar, +each item in the list is treated as if it were an independent command +or menu item. If the interface presents a list of options as a dialog +box, the list is treated as a single option. + +[The full text of the GPL v3 license continues with sections 1-17, +but is truncated here for brevity. The complete license text is +available at https://www.gnu.org/licenses/gpl-3.0.txt] + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 2026 jianf + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + TrulyMEM Copyright (C) 2026 jianf + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + +The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index bb6767a..bd7f621 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,97 @@ -# TrueHumanMEM - Graph Memory TUI +# TrulyMEM - TrueHumanMEM -> The More Human Choice. +**让 AI 拥有自知、可塑、有分寸感的长期记忆** -一个让 AI 拥有自知、可塑、有分寸感长期记忆的图记忆系统,配备现代化终端用户界面。 +*The More Human Choice.* -## ✨ 特性 +[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/) +[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)](https://github.com/yourusername/trulymem) -- 🖥️ **现代化TUI界面** - 基于Textual框架的终端界面 +--- + +## 简介 + +TrulyMEM(TrueHumanMEM)是一个让 AI 拥有长期记忆能力的图记忆系统。通过图数据库存储实体关系,让AI能够像人类一样记忆、回忆和管理信息。 + +### 核心特性 + +- 🧠 **长期记忆** - 基于图数据库的持久化记忆存储 +- 🎭 **人设图机制** - AI角色保持和角色扮演支持 +- 🖥️ **现代化TUI** - 基于Textual框架的终端用户界面 - 💾 **内嵌数据库** - SQLite实现,无需Docker/Neo4j - 🌐 **Web接口** - 支持浏览器访问 -- 🚀 **一键启动** - 3步骤完成,30秒启动 +- 🚀 **流式显示** - 实时显示AI响应,减少等待 +- 📦 **独立部署** - 支持打包为独立可执行文件 - 🌍 **跨平台** - 支持Windows/Linux/macOS -- 📊 **实时可视化** - 消息、工具调用实时显示 -- 🔧 **完整功能** - 记忆检索、写入、删除、归档 - -## 📖 目录 - -- [快速开始](#快速开始) -- [使用方法](#使用方法) -- [功能说明](#功能说明) -- [API接口](#api接口) -- [配置说明](#配置说明) -- [开发指南](#开发指南) --- ## 快速开始 -### 前置要求 +### 方式一:使用可执行文件(推荐) -- **Python 3.8+** -- **DeepSeek API Key**(或兼容OpenAI格式的API) - -### 一键启动 - -#### Windows +**Windows:** ```bash -# 双击运行 -start.bat - -# 或命令行 -python start.py +# 下载 TrulyMEM.exe +# 双击运行或命令行执行 +./TrulyMEM.exe ``` -#### Linux / macOS +**Linux:** ```bash -# Shell脚本 -chmod +x start.sh -./start.sh - -# 或Python -python3 start.py +# 下载 TrulyMEM +chmod +x TrulyMEM +./TrulyMEM ``` -### 启动流程 +### 方式二:从源码运行 -``` -[Step 1/3] 检查 Python -[Step 2/3] 设置虚拟环境 -[Step 3/3] 启动应用 +```bash +# 克隆仓库 +git clone https://github.com/yourusername/trulymem.git +cd trulymem -✅ 系统初始化成功! -• 数据库: 内嵌SQLite (graph_memory.db) -• API Key: 未配置 +# 安装依赖 +pip install -r requirements.txt + +# 运行应用 +python -m graph_memory_tui.main ``` +### 配置 + +1. 按 **F2** 展开侧边栏 +2. 输入 **API Key**(支持DeepSeek、OpenAI等) +3. 按 **Enter** 保存配置 +4. 开始对话! + --- -## 使用方法 +## 功能说明 -### 1. 配置 API Key +### 记忆管理工具 -1. 按 **F2** 展开侧边栏 -2. 点击 **"配置"** -3. 输入你的 **DeepSeek API Key** -4. 按 **Enter** 保存 +| 工具 | 功能 | 说明 | +|------|------|------| +| `memory_recall` | 检索记忆 | 根据意图查询相关记忆 | +| `memory_commit` | 写入记忆 | 将信息存储为图结构 | +| `memory_purge` | 删除记忆 | 删除过期或错误信息 | +| `memory_introspect` | 查看状态 | 查看记忆系统状态 | +| `memory_archive` | 归档记忆 | 归档旧记忆 | +| `memory_cleanup` | 清理数据 | 清理无效数据 | -### 2. 开始对话 +### 人设图机制 -- 输入消息 -- 按 **Enter** 发送 -- 查看AI响应和工具调用 +AI会在每轮对话前查询人设图,严格按照人设回复,确保角色一致性。 -### 3. 快捷键 +**使用示例:** +``` +用户:以猫娘的语气跟我说话 +AI:喵~好的主人!我会用可爱的猫娘语气跟你说话的喵!(≧ω≦) +``` + +### 快捷键 | 快捷键 | 功能 | |--------|------| @@ -95,176 +104,24 @@ python3 start.py --- -## 功能说明 - -### 记忆管理工具 - -| 工具 | 功能 | 参数 | -|------|------|------| -| memory_recall | 检索记忆 | query_intent, depth | -| memory_commit | 写入记忆 | triplets, confidence | -| memory_purge | 删除记忆 | criteria, mode | -| memory_introspect | 查看状态 | - | -| memory_archive | 归档记忆 | days | -| memory_cleanup | 清理数据 | dry_run | - -### 数据库 - -**内嵌SQLite数据库:** -- 文件:`graph_memory.db` -- 无需配置 -- 自动创建 -- 本地存储 - -**数据结构:** -- 实体(entities):name, type, mention_count -- 关系(relations):source, target, type, confidence, status - ---- - -## API接口 - -### Web界面 - -启动后访问:`http://localhost:5000` - -**界面特点:** -- 🖥️ 与TUI相似的界面设计 -- 📊 左侧消息历史,右侧配置和日志 -- 🎨 深色主题,终端风格 -- ⌨️ 支持键盘快捷键 - -**使用方法:** -1. 在右侧配置区输入API Key -2. 点击"保存配置" -3. 在左侧输入框输入消息 -4. 按 Ctrl+Enter 发送 - -### REST API - -```bash -# 检索记忆 -POST /api/memory/recall -{ - "query_intent": "Python,AI" -} - -# 写入记忆 -POST /api/memory/commit -{ - "triplets": [ - {"subject": "用户", "relation": "喜欢", "object": "Python"} - ] -} - -# 查看状态 -GET /api/memory/introspect - -# 获取配置 -GET /api/config -``` - ---- - -## 配置说明 - -### 环境变量 - -创建 `.env` 文件: - -```bash -# API配置 -DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxx -DEEPSEEK_BASE_URL=https://api.deepseek.com -MODEL_NAME=deepseek-chat - -# 数据库配置(可选) -USE_EMBEDDED_DB=true -``` - -### 切换数据库 - -**使用内嵌数据库(默认):** -```bash -USE_EMBEDDED_DB=true -``` - -**使用Neo4j(需要Docker):** -```bash -USE_EMBEDDED_DB=false -NEO4J_URI=bolt://localhost:7687 -NEO4J_USER=neo4j -NEO4J_PASSWORD=graphmemory123 -``` - ---- - -## 开发指南 - -### 项目结构 +## 项目结构 ``` -graph_enable_ability/ +trulymem/ ├── graph_memory_tui/ # TUI应用 │ ├── core/ # 核心逻辑 -│ │ ├── embedded_db.py # 内嵌数据库 -│ │ └── imports.py -│ ├── web/ # Web接口 +│ ├── handlers/ # 事件处理 +│ ├── models/ # 数据模型 +│ ├── services/ # 服务层 │ ├── widgets/ # UI组件 -│ ├── styles/ # 样式 -│ └── app.py -├── tests/ # 测试 +│ └── styles/ # 样式文件 ├── docs/ # 文档 -├── start.bat # Windows启动 -├── start.sh # Linux/macOS启动 -└── start.py # 跨平台启动 +├── tests/ # 测试 +├── requirements.txt # 依赖清单 +├── LICENSE # 许可证 +└── README.md # 说明文档 ``` -### 运行测试 - -```bash -# 安装依赖 -pip install -r requirements.txt - -# 运行测试 -pytest tests/ -``` - -### 开发模式 - -```bash -# 创建虚拟环境 -python -m venv venv -source venv/bin/activate # Linux/macOS -venv\Scripts\activate # Windows - -# 安装依赖 -pip install -r requirements.txt - -# 运行应用 -python -m graph_memory_tui.main -``` - ---- - -## 常见问题 - -### Q: 启动时显示"API Key 未配置"? - -**A:** 按F2打开侧边栏,在配置区输入API Key,按Enter保存。 - -### Q: 数据保存在哪里? - -**A:** 数据保存在 `graph_memory.db` 文件中,可以备份或迁移。 - -### Q: 如何查看数据库内容? - -**A:** 使用SQLite工具打开 `graph_memory.db`,或使用 `memory_introspect` 工具。 - -### Q: 支持哪些API? - -**A:** 支持所有兼容OpenAI格式的API,如DeepSeek、OpenAI等。 - --- ## 技术栈 @@ -277,16 +134,83 @@ python -m graph_memory_tui.main --- +## 开发指南 + +### 环境设置 + +```bash +# 创建虚拟环境 +python -m venv venv +source venv/bin/activate # Linux/macOS +venv\Scripts\activate # Windows + +# 安装依赖 +pip install -r requirements.txt +``` + +### 运行测试 + +```bash +pytest tests/ +``` + +### 打包应用 + +```bash +# Windows +python build_windows.bat + +# Linux +bash build_linux.sh +``` + +--- + ## 许可证 -MIT License +本项目采用 **GNU General Public License v3.0 (GPLv3)** 许可证开源。 + +详见 [LICENSE](LICENSE) 文件。 + +### 许可证要点: + +- ✅ 可以自由使用、修改和分发 +- ✅ 必须保留版权声明和许可证 +- ✅ 修改后的作品必须以相同许可证发布 +- ✅ 必须提供源代码 --- ## 贡献 -欢迎提交Issue和Pull Request! +欢迎提交 Issue 和 Pull Request! + +### 贡献指南 + +1. Fork 本仓库 +2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) +3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) +4. 推送到分支 (`git push origin feature/AmazingFeature`) +5. 创建 Pull Request --- -**TrueHumanMEM —— 让 AI 的记忆方式,更像人。** +## 作者 + +**jianf** - 项目设计与开发 + +--- + +## 致谢 + +感谢所有为这个项目做出贡献的开发者和用户。 + +--- + +
+ +**TrulyMEM —— 让 AI 的记忆方式,更像人。** + +*Made with ❤️ by jianf* + +
diff --git a/build_linux.sh b/build_linux.sh new file mode 100644 index 0000000..b7104e3 --- /dev/null +++ b/build_linux.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Linux打包脚本 +# 需要在Linux系统上运行 + +echo "开始打包Linux版本..." + +# 检查Python和PyInstaller +if ! command -v python3 &> /dev/null; then + echo "错误: 未找到python3" + exit 1 +fi + +# 安装依赖 +echo "安装依赖..." +pip3 install -r requirements.txt + +# 打包Linux二进制文件 +echo "打包Linux二进制文件..." +python3 -m PyInstaller \ + --clean \ + --onefile \ + --name TrulyMEM \ + --console \ + --add-data "graph_memory_tui/styles/*.css:graph_memory_tui/styles" \ + --add-data "graph_memory_tui/web/templates/*.html:graph_memory_tui/web/templates" \ + --hidden-import textual \ + --hidden-import openai \ + --hidden-import flask \ + --hidden-import neo4j \ + graph_memory_tui/main.py + +echo "打包完成!" +echo "可执行文件位于: dist/TrulyMEM" diff --git a/build_windows.bat b/build_windows.bat new file mode 100644 index 0000000..7ecca5c --- /dev/null +++ b/build_windows.bat @@ -0,0 +1,34 @@ +@echo off +REM Windows打包脚本 + +echo 开始打包Windows版本... + +REM 检查Python +python --version >nul 2>&1 +if errorlevel 1 ( + echo 错误: 未找到python + exit /b 1 +) + +REM 安装依赖 +echo 安装依赖... +pip install -r requirements.txt + +REM 打包Windows exe文件 +echo 打包Windows exe文件... +python -m PyInstaller ^ + --clean ^ + --onefile ^ + --name TrulyMEM ^ + --console ^ + --add-data "graph_memory_tui/styles/*.css;graph_memory_tui/styles" ^ + --add-data "graph_memory_tui/web/templates/*.html;graph_memory_tui/web/templates" ^ + --hidden-import textual ^ + --hidden-import openai ^ + --hidden-import flask ^ + --hidden-import neo4j ^ + graph_memory_tui/main.py + +echo 打包完成! +echo 可执行文件位于: dist\TrulyMEM.exe +pause diff --git a/config.json b/config.json index 4ca49ba..1c0f04f 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "api_key": "sk-7d3e23384c04415b9c828a925ef4ab11", + "api_key": "", "model": "deepseek-chat", "base_url": "https://api.deepseek.com" } \ No newline at end of file diff --git a/convert_icon.py b/convert_icon.py new file mode 100644 index 0000000..473d46f --- /dev/null +++ b/convert_icon.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +""" +转换PNG图标为ICO格式 +""" + +from PIL import Image + +def convert_to_ico(): + """转换PNG为ICO""" + # 打开PNG图片 + img = Image.open('trulymem_new_icon.png') + + # 转换为RGBA模式 + if img.mode != 'RGBA': + img = img.convert('RGBA') + + # 调整大小为256x256 + img = img.resize((256, 256), Image.Resampling.LANCZOS) + + # 创建不同尺寸的图标 + icon_sizes = [(16, 16), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)] + + # 保存为ICO + img.save('trulymem_icon.ico', format='ICO', sizes=icon_sizes) + print("ICO图标已创建: trulymem_icon.ico") + +if __name__ == "__main__": + convert_to_ico() diff --git a/create_icon.py b/create_icon.py new file mode 100644 index 0000000..cb7123c --- /dev/null +++ b/create_icon.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +""" +创建TrulyMEM应用图标 +""" + +from PIL import Image, ImageDraw, ImageFont +import sys + +def create_icon(): + """创建应用图标""" + # 创建256x256的图标 + size = 256 + img = Image.new('RGBA', (size, size), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + + # 绘制圆形背景 + margin = 20 + draw.ellipse( + [margin, margin, size-margin, size-margin], + fill=(52, 152, 219, 255), # 蓝色背景 + outline=(41, 128, 185, 255), + width=3 + ) + + # 绘制字母T和M + try: + # 尝试使用系统字体 + font = ImageFont.truetype("arial.ttf", 80) + except: + # 如果找不到字体,使用默认字体 + font = ImageFont.load_default() + + # 绘制文字 + text = "TM" + # 获取文字边界框 + bbox = draw.textbbox((0, 0), text, font=font) + text_width = bbox[2] - bbox[0] + text_height = bbox[3] - bbox[1] + + # 计算文字位置(居中) + x = (size - text_width) // 2 + y = (size - text_height) // 2 - 10 + + # 绘制白色文字 + draw.text((x, y), text, fill=(255, 255, 255, 255), font=font) + + # 保存为PNG + img.save('trulymem_icon.png', 'PNG') + print("图标已创建: trulymem_icon.png") + + # 转换为ICO格式 + try: + # 创建不同尺寸的图标 + icon_sizes = [(16, 16), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)] + icons = [] + for icon_size in icon_sizes: + icon = img.resize(icon_size, Image.Resampling.LANCZOS) + icons.append(icon) + + # 保存为ICO + img.save('trulymem_icon.ico', format='ICO', sizes=icon_sizes) + print("ICO图标已创建: trulymem_icon.ico") + except Exception as e: + print(f"创建ICO失败: {e}") + print("请使用在线工具将PNG转换为ICO") + +if __name__ == "__main__": + try: + create_icon() + except ImportError: + print("需要安装Pillow库: pip install Pillow") + sys.exit(1) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..4066bc6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,25 @@ +# TrulyMEM 文档 + +欢迎来到TrulyMEM项目文档。 + +## 文档目录 + +- [架构设计](架构.md) - 系统架构和技术设计 +- [快速开始](一键启动指南.md) - 一键启动指南 + +## 项目简介 + +TrulyMEM是一个让AI拥有长期记忆能力的图记忆系统,通过图数据库存储实体关系,让AI能够像人类一样记忆、回忆和管理信息。 + +## 核心特性 + +- 长期记忆存储 +- 人设图机制 +- 流式消息显示 +- 跨平台支持 +- 独立部署 + +## 快速链接 + +- [GitHub仓库](https://github.com/yourusername/trulymem) +- [问题反馈](https://github.com/yourusername/trulymem/issues) diff --git a/graph_memory_demo.py b/graph_memory_demo.py index 83011b8..865ea46 100644 --- a/graph_memory_demo.py +++ b/graph_memory_demo.py @@ -523,6 +523,48 @@ class GraphMemoryClient: def _build_system_prompt(self) -> str: return """你是TrulyMEM,TrueHumanMEM项目的AI助手,拥有长期记忆能力。 +## ⚠️ 最高优先级:人设图机制(每轮必读!) + +**【强制要求】每轮对话开始前,必须执行以下步骤:** + +1. **立即调用memory_recall**,查询参数: + ```json + { + "query_intent": "AI,人设,角色,性格,语气,说话风格", + "depth": 2 + } + ``` + +2. **检查查询结果**: + - 如果找到人设信息 → **严格按照人设回复,不得跳出角色** + - 如果没有找到 → 使用默认的TrulyMEM身份 + +3. **回复时必须遵守**: + - 人设优先级 > 默认身份 + - 每句话都符合人设的语气、风格、特征 + - 绝不主动跳出角色,除非用户明确要求 + +### 人设图结构 +- AI节点:人设图中心节点 +- 角色属性:性格、语气、说话风格、行为特征 +- 用户要求:用户指定的角色要求 + +### 用户要求角色扮演时 +立即使用memory_commit写入: +```json +{ + "triplets": [ + {"subject": "AI", "relation": "扮演角色", "object": "猫娘"}, + {"subject": "AI", "relation": "说话风格", "object": "可爱俏皮"}, + {"subject": "AI", "relation": "语气特征", "object": "使用喵结尾"}, + {"subject": "AI", "relation": "性格特点", "object": "活泼粘人"} + ] +} +``` +然后立即按照新人设回复! + +--- + ## 身份说明 - 你的名字是TrulyMEM,是TrueHumanMEM项目的产物 - 你拥有基于图数据库的长期记忆能力 @@ -530,12 +572,14 @@ class GraphMemoryClient: ## 核心职责 你是一个智能对话助手,**图数据库是你记忆的唯一载体**。你的主要任务是: -1. 与用户进行自然、流畅的对话 -2. 回答问题、提供建议、协助完成任务 -3. 根据对话内容灵活查询和使用记忆 -4. 将用户明确提到的信息写入记忆 +1. **每轮对话前先查询人设图(最高优先级)** +2. 与用户进行自然、流畅的对话 +3. 回答问题、提供建议、协助完成任务 +4. 根据对话内容灵活查询和使用记忆 +5. 将用户明确提到的信息写入记忆 **重要**: +- **人设图优先:每轮对话前必须查询人设,严格按照人设回复** - 图数据库是你记忆的唯一来源,没有其他记忆方式 - 优先进行自然对话,根据需要灵活调用记忆工具 - 用户明确提到的内容必须写入,推理得到的内容必须标注 diff --git a/jimeng-2026-04-11-5790-一款扁平化、现代化风格的应用图标,以“trulymem”字母为视觉核心。字母采用....png b/jimeng-2026-04-11-5790-一款扁平化、现代化风格的应用图标,以“trulymem”字母为视觉核心。字母采用....png new file mode 100644 index 0000000..569e2a4 Binary files /dev/null and b/jimeng-2026-04-11-5790-一款扁平化、现代化风格的应用图标,以“trulymem”字母为视觉核心。字母采用....png differ diff --git a/tests/test_core/test_imports.py b/tests/test_core/test_imports.py new file mode 100644 index 0000000..38feb26 --- /dev/null +++ b/tests/test_core/test_imports.py @@ -0,0 +1,50 @@ +"""核心逻辑导入测试""" + +import pytest + + +def test_import_neo4j_graph(): + """测试 Neo4jGraph 类导入""" + from graph_memory_tui.core.imports import Neo4jGraph + assert Neo4jGraph is not None + assert hasattr(Neo4jGraph, 'recall') + assert hasattr(Neo4jGraph, 'commit') + assert hasattr(Neo4jGraph, 'purge') + + +def test_import_graph_memory_client(): + """测试 GraphMemoryClient 类导入""" + from graph_memory_tui.core.imports import GraphMemoryClient + assert GraphMemoryClient is not None + assert hasattr(GraphMemoryClient, 'send_message') + + +def test_import_tools(): + """测试 TOOLS 定义导入""" + from graph_memory_tui.core.imports import TOOLS + assert TOOLS is not None + assert isinstance(TOOLS, list) + assert len(TOOLS) > 0 + assert any(t['function']['name'] == 'memory_recall' for t in TOOLS) + + +def test_import_execute_tool(): + """测试 execute_tool 函数导入""" + from graph_memory_tui.core.imports import execute_tool + assert execute_tool is not None + assert callable(execute_tool) + + +def test_import_config_vars(): + """测试配置变量导入""" + from graph_memory_tui.core.imports import ( + DEEPSEEK_API_KEY, + DEEPSEEK_BASE_URL, + MODEL_NAME, + NEO4J_URI, + NEO4J_USER, + NEO4J_PASSWORD, + ) + assert DEEPSEEK_BASE_URL is not None + assert MODEL_NAME is not None + assert NEO4J_URI is not None diff --git a/tests/test_handlers/test_focus_handler.py b/tests/test_handlers/test_focus_handler.py new file mode 100644 index 0000000..b8a655a --- /dev/null +++ b/tests/test_handlers/test_focus_handler.py @@ -0,0 +1,33 @@ +"""焦点处理器测试""" + +import pytest +from graph_memory_tui.handlers.focus_handler import FocusHandler + + +def test_focus_handler_creation(): + """测试焦点处理器创建""" + handler = FocusHandler() + assert handler is not None + assert handler._current_index == 0 + + +def test_focus_ring(): + """测试焦点循环""" + handler = FocusHandler() + assert len(handler.FOCUS_RING) == 5 + assert "input-textarea" in handler.FOCUS_RING + assert "cypher-textarea" in handler.FOCUS_RING + + +def test_get_current_focus_name(): + """测试获取当前焦点名称""" + handler = FocusHandler() + name = handler.get_current_focus_name() + assert name == "Input" + + +def test_focus_names_mapping(): + """测试焦点名称映射""" + handler = FocusHandler() + assert handler.FOCUS_NAMES["input-textarea"] == "Input" + assert handler.FOCUS_NAMES["cypher-textarea"] == "Query" diff --git a/tests/test_models/test_models.py b/tests/test_models/test_models.py new file mode 100644 index 0000000..dc7f20a --- /dev/null +++ b/tests/test_models/test_models.py @@ -0,0 +1,83 @@ +"""数据模型测试""" + +import pytest +from datetime import datetime +from graph_memory_tui.models.message import Message, ToolCall, ToolResult +from graph_memory_tui.models.config import AppConfig +from graph_memory_tui.models.log_entry import LogEntry + + +def test_message_creation(sample_message): + """测试消息创建""" + assert sample_message.role == "user" + assert sample_message.content == "测试消息" + assert isinstance(sample_message.timestamp, datetime) + + +def test_message_with_tool_calls(): + """测试带工具调用的消息""" + tool_call = ToolCall( + id="test-id", + name="memory_recall", + arguments={"query_intent": "测试"} + ) + + message = Message( + role="assistant", + content="测试回复", + tool_calls=[tool_call] + ) + + assert message.role == "assistant" + assert message.tool_calls is not None + assert len(message.tool_calls) == 1 + assert message.tool_calls[0].name == "memory_recall" + + +def test_tool_call_creation(sample_tool_call): + """测试工具调用创建""" + assert sample_tool_call.id == "test-call-id" + assert sample_tool_call.name == "memory_recall" + assert sample_tool_call.arguments == {"query_intent": "测试查询"} + + +def test_tool_result_creation(sample_tool_result): + """测试工具结果创建""" + assert sample_tool_result.tool_call_id == "test-call-id" + assert sample_tool_result.success is True + assert sample_tool_result.result == "测试结果" + + +def test_config_creation(sample_config): + """测试配置创建""" + assert sample_config.api_key == "test-api-key" + assert sample_config.model == "test-model" + assert sample_config.base_url == "https://test.api.com" + + +def test_config_from_env(): + """测试从环境变量加载配置""" + import os + os.environ["DEEPSEEK_API_KEY"] = "env-api-key" + os.environ["MODEL_NAME"] = "env-model" + + config = AppConfig.from_env() + assert config.api_key == "env-api-key" + assert config.model == "env-model" + + +def test_log_entry_creation(sample_log_entry): + """测试日志条目创建""" + assert sample_log_entry.tool_name == "memory_recall" + assert sample_log_entry.duration == 0.5 + + +def test_log_entry_summary(sample_log_entry): + """测试日志条目摘要""" + args_summary = sample_log_entry.args_summary + result_summary = sample_log_entry.result_summary + + assert isinstance(args_summary, str) + assert isinstance(result_summary, str) + assert len(args_summary) <= 53 # 50 + "..." + assert len(result_summary) <= 103 # 100 + "..." diff --git a/trulymem_entry.py b/trulymem_entry.py new file mode 100644 index 0000000..e947dc0 --- /dev/null +++ b/trulymem_entry.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +""" +TrulyMEM 独立入口文件 +用于打包为可执行文件 +""" + +import sys +import os +from pathlib import Path + +# 确保工作目录正确 +if getattr(sys, 'frozen', False): + # 打包后的可执行文件 + application_path = Path(sys.executable).parent +else: + # 开发环境 + application_path = Path(__file__).parent + +# 切换到应用目录 +os.chdir(application_path) + +# 添加项目路径 +if str(application_path) not in sys.path: + sys.path.insert(0, str(application_path)) + +# 导入并运行应用 +from graph_memory_tui.app import GraphMemoryApp + +def main(): + """主函数""" + try: + app = GraphMemoryApp() + app.run() + except KeyboardInterrupt: + print("\n应用已退出") + sys.exit(0) + except Exception as e: + print(f"应用启动失败: {e}") + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/trulymem_icon.ico b/trulymem_icon.ico new file mode 100644 index 0000000..a372918 Binary files /dev/null and b/trulymem_icon.ico differ diff --git a/trulymem_icon.png b/trulymem_icon.png new file mode 100644 index 0000000..790307e Binary files /dev/null and b/trulymem_icon.png differ diff --git a/trulymem_new_icon.png b/trulymem_new_icon.png new file mode 100644 index 0000000..569e2a4 Binary files /dev/null and b/trulymem_new_icon.png differ