chore: 清理敏感信息,完善工程结构和文档
This commit is contained in:
3
.arts/settings.json
Normal file
3
.arts/settings.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"diffEditor.renderSideBySide": false
|
||||
}
|
||||
67
.gitignore
vendored
67
.gitignore
vendored
@ -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
|
||||
|
||||
194
LICENSE
Normal file
194
LICENSE
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
================================================================================
|
||||
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
364
README.md
364
README.md
@ -1,88 +1,97 @@
|
||||
# TrueHumanMEM - Graph Memory TUI
|
||||
# TrulyMEM - TrueHumanMEM
|
||||
|
||||
> The More Human Choice.
|
||||
**让 AI 拥有自知、可塑、有分寸感的长期记忆**
|
||||
|
||||
一个让 AI 拥有自知、可塑、有分寸感长期记忆的图记忆系统,配备现代化终端用户界面。
|
||||
*The More Human Choice.*
|
||||
|
||||
## ✨ 特性
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](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** - 项目设计与开发
|
||||
|
||||
---
|
||||
|
||||
## 致谢
|
||||
|
||||
感谢所有为这个项目做出贡献的开发者和用户。
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**TrulyMEM —— 让 AI 的记忆方式,更像人。**
|
||||
|
||||
*Made with ❤️ by jianf*
|
||||
|
||||
</div>
|
||||
|
||||
33
build_linux.sh
Normal file
33
build_linux.sh
Normal file
@ -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"
|
||||
34
build_windows.bat
Normal file
34
build_windows.bat
Normal file
@ -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
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"api_key": "sk-7d3e23384c04415b9c828a925ef4ab11",
|
||||
"api_key": "",
|
||||
"model": "deepseek-chat",
|
||||
"base_url": "https://api.deepseek.com"
|
||||
}
|
||||
28
convert_icon.py
Normal file
28
convert_icon.py
Normal file
@ -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()
|
||||
72
create_icon.py
Normal file
72
create_icon.py
Normal file
@ -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)
|
||||
25
docs/README.md
Normal file
25
docs/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# TrulyMEM 文档
|
||||
|
||||
欢迎来到TrulyMEM项目文档。
|
||||
|
||||
## 文档目录
|
||||
|
||||
- [架构设计](架构.md) - 系统架构和技术设计
|
||||
- [快速开始](一键启动指南.md) - 一键启动指南
|
||||
|
||||
## 项目简介
|
||||
|
||||
TrulyMEM是一个让AI拥有长期记忆能力的图记忆系统,通过图数据库存储实体关系,让AI能够像人类一样记忆、回忆和管理信息。
|
||||
|
||||
## 核心特性
|
||||
|
||||
- 长期记忆存储
|
||||
- 人设图机制
|
||||
- 流式消息显示
|
||||
- 跨平台支持
|
||||
- 独立部署
|
||||
|
||||
## 快速链接
|
||||
|
||||
- [GitHub仓库](https://github.com/yourusername/trulymem)
|
||||
- [问题反馈](https://github.com/yourusername/trulymem/issues)
|
||||
@ -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. 将用户明确提到的信息写入记忆
|
||||
|
||||
**重要**:
|
||||
- **人设图优先:每轮对话前必须查询人设,严格按照人设回复**
|
||||
- 图数据库是你记忆的唯一来源,没有其他记忆方式
|
||||
- 优先进行自然对话,根据需要灵活调用记忆工具
|
||||
- 用户明确提到的内容必须写入,推理得到的内容必须标注
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
50
tests/test_core/test_imports.py
Normal file
50
tests/test_core/test_imports.py
Normal file
@ -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
|
||||
33
tests/test_handlers/test_focus_handler.py
Normal file
33
tests/test_handlers/test_focus_handler.py
Normal file
@ -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"
|
||||
83
tests/test_models/test_models.py
Normal file
83
tests/test_models/test_models.py
Normal file
@ -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 + "..."
|
||||
44
trulymem_entry.py
Normal file
44
trulymem_entry.py
Normal file
@ -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()
|
||||
BIN
trulymem_icon.ico
Normal file
BIN
trulymem_icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 141 KiB |
BIN
trulymem_icon.png
Normal file
BIN
trulymem_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
Reference in New Issue
Block a user