mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
docs: add README
This commit is contained in:
@@ -1,190 +0,0 @@
|
||||
# 数据库和API修复总结
|
||||
|
||||
本次修复解决了数据库表设计和后端API的6个主要问题。
|
||||
|
||||
## ✅ 已完成的修复
|
||||
|
||||
### 1. 修复 isActive 数据类型 ✓
|
||||
|
||||
**问题**: `users.isActive` 字段使用 `text` 类型而不是 `boolean`
|
||||
|
||||
**修复**:
|
||||
- 文件: `packages/db/src/users.ts`
|
||||
- 将 `isActive: text('is_active').notNull().default('true')` 改为 `isActive: boolean('is_active').notNull().default(true)`
|
||||
|
||||
### 2. 添加外键约束 ✓
|
||||
|
||||
**问题**: 缺少重要的外键约束
|
||||
|
||||
**修复**:
|
||||
- 文件: `packages/db/src/settings.ts`
|
||||
- `userId` 字段从 `text` 改为 `uuid`,并添加外键引用 `users.id`
|
||||
- 文件: `packages/db/src/history.ts`
|
||||
- `user` 字段从 `text` 改为 `uuid`,并添加外键引用 `users.id`
|
||||
|
||||
### 3. 重构 JWT 中间件消除重复代码 ✓
|
||||
|
||||
**问题**: JWT 配置在多个模块中重复定义
|
||||
|
||||
**修复**:
|
||||
- 文件: `packages/api/src/middlewares/auth.ts`
|
||||
- 创建共享的 `jwtPlugin` 包含 JWT 和 Bearer token 配置
|
||||
- 所有中间件复用这个插件,消除重复代码
|
||||
- 更新的模块:
|
||||
- `packages/api/src/modules/auth/index.ts`
|
||||
- `packages/api/src/modules/memory/index.ts`
|
||||
- `packages/api/src/modules/settings/index.ts`
|
||||
- `packages/api/src/modules/agent/index.ts`
|
||||
|
||||
### 4. 实现统一错误处理中间件 ✓
|
||||
|
||||
**问题**: 缺少统一的错误处理机制
|
||||
|
||||
**修复**:
|
||||
- 文件: `packages/api/src/middlewares/error.ts` (新建)
|
||||
- 创建统一的错误处理中间件
|
||||
- 定义标准的错误响应格式 `ErrorResponse`
|
||||
- 定义标准的成功响应格式 `SuccessResponse`
|
||||
- 自动根据错误类型设置合适的 HTTP 状态码
|
||||
- 支持的错误类型:
|
||||
- `VALIDATION` (400)
|
||||
- `NOT_FOUND` (404)
|
||||
- `PARSE` (400)
|
||||
- `UNAUTHORIZED` (401)
|
||||
- `FORBIDDEN` (403)
|
||||
- `CONFLICT` (409)
|
||||
- `INTERNAL_SERVER_ERROR` (500)
|
||||
- 文件: `packages/api/src/index.ts`
|
||||
- 在主应用中启用错误处理中间件
|
||||
|
||||
### 5. 为 model 模块添加权限控制 ✓
|
||||
|
||||
**问题**: model 模块的创建、更新、删除操作没有权限检查
|
||||
|
||||
**修复**:
|
||||
- 文件: `packages/api/src/modules/model/index.ts`
|
||||
- 读取操作 (GET) 使用 `optionalAuthMiddleware`(公开或可选认证)
|
||||
- 写入操作 (POST, PUT, DELETE) 使用 `adminMiddleware`(仅管理员)
|
||||
- 使用 `guard` 分离不同权限级别的路由
|
||||
|
||||
### 6. 添加分页功能到列表接口 ✓
|
||||
|
||||
**问题**: 列表接口缺少分页、排序功能
|
||||
|
||||
**修复**:
|
||||
- 文件: `packages/api/src/utils/pagination.ts` (新建)
|
||||
- 创建通用的分页工具函数
|
||||
- `parsePaginationParams()` - 解析分页参数
|
||||
- `createPaginatedResult()` - 创建分页结果
|
||||
- `calculateOffset()` - 计算偏移量
|
||||
- 标准分页响应格式:
|
||||
```typescript
|
||||
{
|
||||
items: T[],
|
||||
pagination: {
|
||||
page: number,
|
||||
limit: number,
|
||||
total: number,
|
||||
totalPages: number,
|
||||
hasNext: boolean,
|
||||
hasPrev: boolean
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 文件: `packages/api/src/modules/user/service.ts`
|
||||
- 更新 `getUsers()` 支持分页和排序
|
||||
- 支持参数: `page`, `limit`, `sortBy`, `sortOrder`
|
||||
|
||||
- 文件: `packages/api/src/modules/user/index.ts`
|
||||
- GET `/user` 接口支持分页查询参数
|
||||
|
||||
- 文件: `packages/api/src/modules/model/service.ts`
|
||||
- 更新 `getModels()` 支持分页
|
||||
- 支持参数: `page`, `limit`, `sortOrder`
|
||||
|
||||
- 文件: `packages/api/src/modules/model/index.ts`
|
||||
- GET `/model` 接口支持分页查询参数
|
||||
|
||||
## 📋 API 使用示例
|
||||
|
||||
### 分页查询用户
|
||||
```bash
|
||||
GET /user?page=1&limit=10&sortBy=createdAt&sortOrder=desc
|
||||
```
|
||||
|
||||
响应:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"items": [...],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"limit": 10,
|
||||
"total": 50,
|
||||
"totalPages": 5,
|
||||
"hasNext": true,
|
||||
"hasPrev": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 分页查询模型
|
||||
```bash
|
||||
GET /model?page=1&limit=10&sortOrder=desc
|
||||
```
|
||||
|
||||
### 错误响应格式
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Error message",
|
||||
"code": "ERROR_CODE",
|
||||
"details": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
## 🔄 数据库迁移
|
||||
|
||||
修改了数据库 schema 后,需要运行迁移:
|
||||
|
||||
```bash
|
||||
cd packages/db
|
||||
pnpm run generate # 生成迁移文件
|
||||
pnpm run push # 执行迁移
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **数据库迁移**: 修改了 `users.isActive`, `settings.userId`, `history.user` 字段,需要迁移现有数据
|
||||
2. **API 响应格式变化**: 列表接口现在返回分页格式,前端需要适配
|
||||
3. **权限控制**: model 的写入操作现在需要管理员权限
|
||||
|
||||
## 📚 相关文件
|
||||
|
||||
### 数据库 Schema
|
||||
- `packages/db/src/users.ts`
|
||||
- `packages/db/src/settings.ts`
|
||||
- `packages/db/src/history.ts`
|
||||
|
||||
### 中间件
|
||||
- `packages/api/src/middlewares/auth.ts`
|
||||
- `packages/api/src/middlewares/error.ts`
|
||||
- `packages/api/src/middlewares/index.ts`
|
||||
|
||||
### API 模块
|
||||
- `packages/api/src/modules/user/index.ts`
|
||||
- `packages/api/src/modules/user/service.ts`
|
||||
- `packages/api/src/modules/model/index.ts`
|
||||
- `packages/api/src/modules/model/service.ts`
|
||||
- `packages/api/src/modules/auth/index.ts`
|
||||
- `packages/api/src/modules/memory/index.ts`
|
||||
- `packages/api/src/modules/settings/index.ts`
|
||||
- `packages/api/src/modules/agent/index.ts`
|
||||
|
||||
### 工具函数
|
||||
- `packages/api/src/utils/pagination.ts`
|
||||
|
||||
### 主应用
|
||||
- `packages/api/src/index.ts`
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<div align="center">
|
||||
<h1>MemoHome</h1>
|
||||
<p>Long-memory, self-hosted, AI-powered personal housekeeper and lifemate.</p>
|
||||
</div>
|
||||
|
||||
Memohome是一个专属于你的AI私人管家,你可以把它跑在你的NAS,路由器等个人设备上,24小时的为你提供服务。
|
||||
|
||||
## Features
|
||||
|
||||
- [x] 长记忆:Memohome拥有长记忆能力,可以为你的家庭成员提供个性化的服务。他会存储最近一段时间(默认最近15个小时)的上下文,超出时间后则会根据你的需求按需加载记忆
|
||||
- [x] 定时任务:Memohome可以帮你创建智能的定时任务,比如:每天早上七点生成一个早餐菜谱,通过Telegram发送给我
|
||||
- [ ] 聊天软件支持:Memohome可以支持多种聊天软件,比如:Telegram,微信,QQ等常用社交软件,通过直接发送消息与Memohome进行交互,同时Memohome也可以通过事件触发,选择工具主动给你发送消息
|
||||
- [ ] 文件系统管理:Memohome可以帮你管理你的文件系统,比如:文件搜索,图片分类,文件分享等。他可以创建文件,也可以通过聊天软件发送文件给你;你也可以通过发送文件给他帮你处理。
|
||||
- [ ] MCP支持:Memohome可以支持多种MCP接口,与多种外部工具进行交互。
|
||||
- More...
|
||||
|
||||
## Quick Start
|
||||
|
||||
环境:
|
||||
- PostgreSQL 16+
|
||||
- Bun 1.2+
|
||||
- PNPM
|
||||
- Qdrant
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
pnpm install
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Environment Variables</summary>
|
||||
- `DATABASE_URL`: PostgreSQL 连接字符串
|
||||
- `ROOT_USER`: 超级管理员用户名
|
||||
- `ROOT_USER_PASSWORD`: 超级管理员密码
|
||||
- `JWT_SECRET`: JWT 签名密钥
|
||||
- `QDRANT_URL`: Qdrant 连接字符串
|
||||
</details>
|
||||
|
||||
### 数据库初始化
|
||||
|
||||
```bash
|
||||
pnpm run db:push
|
||||
```
|
||||
|
||||
### API Server
|
||||
|
||||
```bash
|
||||
pnpm run api:dev
|
||||
```
|
||||
|
||||
API服务将在 `http://localhost:7002` 启动。
|
||||
|
||||
### 命令行工具
|
||||
|
||||
首先你需要登录:
|
||||
```bash
|
||||
pnpm cli auth login
|
||||
```
|
||||
|
||||
按照提示输入管理员用户名和密码。
|
||||
|
||||
|
||||
直接运行以下命令会进入Agent交互模式
|
||||
|
||||
```bash
|
||||
pnpm cli
|
||||
```
|
||||
|
||||
在此之前你需要配置模型,你至少需要配置一个聊天模型,一个嵌入模型,一个摘要模型。
|
||||
|
||||
```bash
|
||||
pnpm run model:create --name "GPT-4" --model-id "gpt-4" --base-url "https://api.openai.com/v1" --api-key "your-api-key" --client-type "openai" --type "chat"
|
||||
```
|
||||
- `--name`: 模型显示名称
|
||||
- `--model-id`: 模型ID
|
||||
- `--base-url`: 模型API地址
|
||||
- `--api-key`: 模型API密钥
|
||||
- `--client-type`: 模型提供者类型, 可选值为 `openai` 或 `anthropic` 或 `google`
|
||||
- `--type`: 模型类型,可选值为 `chat` 或 `embedding`
|
||||
|
||||
创建成功后你会得到一个uuid,你可以通过这个uuid来配置你的设置:
|
||||
|
||||
```bash
|
||||
pnpm cli config set --chat-model <uuid> --summary-model <uuid> --embedding-model <uuid>
|
||||
```
|
||||
- `--chat-model`: 聊天模型uuid
|
||||
- `--summary-model`: 摘要模型uuid
|
||||
- `--embedding-model`: 嵌入模型uuid
|
||||
|
||||
然后你就可以正常的使用Memohome了。
|
||||
|
||||
你可以设置你的最大上下文加载时间,默认是900分钟,你可以通过以下命令来设置:
|
||||
|
||||
```bash
|
||||
pnpm cli config set --max-context-time <minutes>
|
||||
```
|
||||
- `--max-context-time`: 最大上下文加载时间,单位为分钟
|
||||
@@ -1,383 +0,0 @@
|
||||
# Agent API 文档
|
||||
|
||||
## 概述
|
||||
|
||||
Agent API 提供了一个智能对话代理接口,支持流式响应、记忆管理和工具调用。
|
||||
|
||||
## 端点
|
||||
|
||||
### POST `/agent/stream` 🔒 需要认证
|
||||
|
||||
与 AI Agent 进行流式对话。
|
||||
|
||||
#### 权限要求
|
||||
|
||||
- 需要有效的 Bearer token
|
||||
- 自动使用当前登录用户的身份
|
||||
|
||||
#### 请求
|
||||
|
||||
**Headers:**
|
||||
```
|
||||
Authorization: Bearer <your_jwt_token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"message": "你好,请介绍一下你自己",
|
||||
"maxContextLoadTime": 60,
|
||||
"language": "Chinese"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
|
||||
| 字段 | 类型 | 必需 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| message | string | 是 | - | 用户消息内容 |
|
||||
| maxContextLoadTime | number | 否 | 从设置读取或60 | 加载上下文的时间范围(分钟,1-1440) |
|
||||
| language | string | 否 | 从设置读取或"Same as user input" | Agent 回复的首选语言 |
|
||||
|
||||
**注意**: `maxContextLoadTime` 和 `language` 如果在请求中未指定,将从用户设置(Settings)中读取。如果设置中也没有,则使用默认值。
|
||||
|
||||
#### 响应
|
||||
|
||||
返回 Server-Sent Events (SSE) 流式响应。
|
||||
|
||||
**Content-Type:** `text/event-stream`
|
||||
|
||||
**事件格式:**
|
||||
|
||||
每个事件以 `data: ` 开头,后跟 JSON 格式的数据:
|
||||
|
||||
```
|
||||
data: {"type":"text-delta","text":"你"}
|
||||
|
||||
data: {"type":"text-delta","text":"好"}
|
||||
|
||||
data: {"type":"tool-call","toolName":"search_memory","args":{...}}
|
||||
|
||||
data: [DONE]
|
||||
```
|
||||
|
||||
**事件类型:**
|
||||
|
||||
1. **text-delta** - 文本增量
|
||||
```json
|
||||
{
|
||||
"type": "text-delta",
|
||||
"text": "文本片段"
|
||||
}
|
||||
```
|
||||
|
||||
2. **tool-call** - 工具调用
|
||||
```json
|
||||
{
|
||||
"type": "tool-call",
|
||||
"toolName": "search_memory",
|
||||
"args": {
|
||||
"query": "搜索内容"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **[DONE]** - 流结束标记
|
||||
|
||||
4. **error** - 错误事件
|
||||
```json
|
||||
{
|
||||
"type": "error",
|
||||
"error": "错误消息"
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
**400 Bad Request** - 模型配置未找到
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Model configuration not found. Please configure your models in settings."
|
||||
}
|
||||
```
|
||||
|
||||
**401 Unauthorized** - 未认证
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "No bearer token provided"
|
||||
}
|
||||
```
|
||||
|
||||
**500 Internal Server Error** - 服务器错误
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Failed to process request"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### JavaScript/TypeScript (EventSource)
|
||||
|
||||
```typescript
|
||||
async function streamAgentChat(message: string, token: string) {
|
||||
const response = await fetch('http://localhost:7002/agent/stream', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
message,
|
||||
maxContextLoadTime: 60,
|
||||
language: 'Chinese',
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.error)
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
if (!reader) throw new Error('No reader available')
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
|
||||
const chunk = decoder.decode(value)
|
||||
const lines = chunk.split('\n')
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = line.slice(6)
|
||||
|
||||
if (data === '[DONE]') {
|
||||
console.log('\n✅ Stream completed')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const event = JSON.parse(data)
|
||||
|
||||
if (event.type === 'text-delta' && event.text) {
|
||||
process.stdout.write(event.text)
|
||||
} else if (event.type === 'tool-call') {
|
||||
console.log(`\n[Tool: ${event.toolName}]`)
|
||||
} else if (event.type === 'error') {
|
||||
console.error('\n❌ Error:', event.error)
|
||||
}
|
||||
} catch (e) {
|
||||
// Skip invalid JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
const token = 'your_jwt_token_here'
|
||||
await streamAgentChat('你好,请介绍一下你自己', token)
|
||||
```
|
||||
|
||||
### curl 示例
|
||||
|
||||
```bash
|
||||
# 1. 登录获取 token
|
||||
TOKEN=$(curl -X POST http://localhost:7002/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"your_password"}' \
|
||||
| jq -r '.data.token')
|
||||
|
||||
# 2. 流式对话
|
||||
curl -N -X POST http://localhost:7002/agent/stream \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"message": "你好,请介绍一下你自己",
|
||||
"maxContextLoadTime": 60,
|
||||
"language": "Chinese"
|
||||
}'
|
||||
```
|
||||
|
||||
### Python 示例
|
||||
|
||||
```python
|
||||
import requests
|
||||
import json
|
||||
|
||||
def stream_agent_chat(message: str, token: str):
|
||||
url = 'http://localhost:7002/agent/stream'
|
||||
headers = {
|
||||
'Authorization': f'Bearer {token}',
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
data = {
|
||||
'message': message,
|
||||
'maxContextLoadTime': 60,
|
||||
'language': 'Chinese',
|
||||
}
|
||||
|
||||
with requests.post(url, headers=headers, json=data, stream=True) as response:
|
||||
response.raise_for_status()
|
||||
|
||||
for line in response.iter_lines():
|
||||
if line:
|
||||
line_str = line.decode('utf-8')
|
||||
if line_str.startswith('data: '):
|
||||
data = line_str[6:]
|
||||
|
||||
if data == '[DONE]':
|
||||
print('\n✅ Stream completed')
|
||||
break
|
||||
|
||||
try:
|
||||
event = json.loads(data)
|
||||
|
||||
if event.get('type') == 'text-delta' and event.get('text'):
|
||||
print(event['text'], end='', flush=True)
|
||||
elif event.get('type') == 'tool-call':
|
||||
print(f"\n[Tool: {event['toolName']}]")
|
||||
elif event.get('type') == 'error':
|
||||
print(f"\n❌ Error: {event['error']}")
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 使用示例
|
||||
token = 'your_jwt_token_here'
|
||||
stream_agent_chat('你好,请介绍一下你自己', token)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 工作原理
|
||||
|
||||
### 1. 认证验证
|
||||
|
||||
- 验证 JWT token
|
||||
- 提取用户信息
|
||||
|
||||
### 2. 模型配置获取
|
||||
|
||||
从用户设置中获取:
|
||||
- Chat Model(对话模型)
|
||||
- Embedding Model(嵌入模型)
|
||||
- Summary Model(摘要模型)
|
||||
|
||||
### 3. Agent 创建
|
||||
|
||||
- 创建 Memory 实例(用于记忆管理)
|
||||
- 创建 Agent 实例,配置回调函数:
|
||||
- `onReadMemory`: 从数据库加载历史对话
|
||||
- `onSearchMemory`: 搜索相关记忆
|
||||
- `onFinish`: 保存对话到记忆
|
||||
|
||||
### 4. 流式响应
|
||||
|
||||
- Agent 处理用户消息
|
||||
- 实时流式返回生成的文本
|
||||
- 报告工具调用事件
|
||||
- 完成后自动保存到记忆
|
||||
|
||||
### 5. 记忆管理
|
||||
|
||||
- 自动加载近期对话历史作为上下文
|
||||
- 使用向量搜索查找相关记忆
|
||||
- 对话结束后自动保存
|
||||
|
||||
---
|
||||
|
||||
## 特性
|
||||
|
||||
### ✅ 流式响应
|
||||
|
||||
- 实时返回生成的文本
|
||||
- 无需等待完整响应
|
||||
- 更好的用户体验
|
||||
|
||||
### ✅ 记忆管理
|
||||
|
||||
- 自动保存对话历史
|
||||
- 智能加载相关上下文
|
||||
- 向量搜索相关记忆
|
||||
|
||||
### ✅ 工具调用
|
||||
|
||||
- 支持搜索记忆工具
|
||||
- 可扩展其他工具
|
||||
- 实时报告工具使用
|
||||
|
||||
### ✅ 个性化配置
|
||||
|
||||
- 每个用户使用自己的模型配置
|
||||
- 可自定义语言偏好
|
||||
- 可配置上下文加载时间
|
||||
|
||||
---
|
||||
|
||||
## 前置条件
|
||||
|
||||
### 1. 配置模型和 Agent 设置
|
||||
|
||||
使用 Settings API 配置默认模型和 Agent 参数:
|
||||
|
||||
```bash
|
||||
curl -X PUT http://localhost:7002/settings \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"defaultChatModel": "chat-model-uuid",
|
||||
"defaultEmbeddingModel": "embedding-model-uuid",
|
||||
"defaultSummaryModel": "summary-model-uuid",
|
||||
"maxContextLoadTime": 60,
|
||||
"language": "Chinese"
|
||||
}'
|
||||
```
|
||||
|
||||
详见 [Settings API 文档](./SETTINGS_API.md)
|
||||
|
||||
### 2. 创建模型配置
|
||||
|
||||
使用 Model API 创建模型:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:7002/model \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"modelId": "gpt-4",
|
||||
"baseUrl": "https://api.openai.com/v1",
|
||||
"apiKey": "your-api-key",
|
||||
"clientType": "openai",
|
||||
"name": "GPT-4"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 限制和注意事项
|
||||
|
||||
1. **模型配置必需**: 用户必须先配置好模型才能使用 Agent
|
||||
2. **流式连接**: 需要支持 Server-Sent Events 的客户端
|
||||
3. **超时处理**: 长时间无响应可能导致连接超时
|
||||
4. **并发限制**: 建议每个用户同时只维护一个对话流
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [认证系统](./AUTH_README.md)
|
||||
- [用户管理](./USER_MANAGEMENT.md)
|
||||
- [Settings API](./API_CHANGES.md)
|
||||
- [Model API](./README.md)
|
||||
- [Agent 包文档](../agent/README.md)
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
# API 变更说明
|
||||
|
||||
## 🔒 Settings 和 Memory 模块现已使用认证
|
||||
|
||||
### 概述
|
||||
|
||||
Settings 和 Memory 模块现在使用 JWT 认证中间件,自动从 token 中获取当前用户信息,**不再需要手动传入 userId**。
|
||||
|
||||
---
|
||||
|
||||
## Settings 模块变更
|
||||
|
||||
### ❌ 旧 API(已废弃)
|
||||
|
||||
```bash
|
||||
# 获取用户设置
|
||||
GET /settings/:userId
|
||||
|
||||
# 创建用户设置
|
||||
POST /settings
|
||||
{
|
||||
"userId": "user123",
|
||||
"defaultChatModel": "uuid-here"
|
||||
}
|
||||
|
||||
# 更新用户设置
|
||||
PUT /settings/:userId
|
||||
{
|
||||
"defaultChatModel": "uuid-here"
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ 新 API(需要认证)
|
||||
|
||||
```bash
|
||||
# 获取当前用户的设置
|
||||
GET /settings
|
||||
Authorization: Bearer <token>
|
||||
|
||||
# 更新或创建当前用户的设置
|
||||
PUT /settings
|
||||
Authorization: Bearer <token>
|
||||
{
|
||||
"defaultChatModel": "uuid-here",
|
||||
"defaultEmbeddingModel": "uuid-here",
|
||||
"defaultSummaryModel": "uuid-here",
|
||||
"maxContextLoadTime": 60,
|
||||
"language": "Chinese"
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```bash
|
||||
# 1. 登录获取 token
|
||||
TOKEN=$(curl -X POST http://localhost:7002/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"user","password":"pass"}' \
|
||||
| jq -r '.data.token')
|
||||
|
||||
# 2. 获取我的设置
|
||||
curl http://localhost:7002/settings \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# 3. 更新我的设置
|
||||
curl -X PUT http://localhost:7002/settings \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"defaultChatModel": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"maxContextLoadTime": 60,
|
||||
"language": "Chinese"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory 模块变更
|
||||
|
||||
### ❌ 旧 API(已废弃)
|
||||
|
||||
```bash
|
||||
# 添加记忆
|
||||
POST /memory
|
||||
{
|
||||
"messages": [...],
|
||||
"timestamp": "2024-01-10T10:00:00Z",
|
||||
"user": "user123"
|
||||
}
|
||||
|
||||
# 搜索记忆
|
||||
GET /memory/search?query=hello&userId=user123
|
||||
|
||||
# 获取消息历史
|
||||
GET /memory/message?page=1&limit=10&userId=user123
|
||||
|
||||
# 按日期过滤消息
|
||||
GET /memory/message/filter?from=2024-01-01&to=2024-01-31&userId=user123
|
||||
```
|
||||
|
||||
### ✅ 新 API(需要认证)
|
||||
|
||||
```bash
|
||||
# 添加记忆(自动使用当前用户)
|
||||
POST /memory
|
||||
Authorization: Bearer <token>
|
||||
{
|
||||
"messages": [...],
|
||||
"timestamp": "2024-01-10T10:00:00Z"
|
||||
}
|
||||
|
||||
# 搜索当前用户的记忆
|
||||
GET /memory/search?query=hello
|
||||
Authorization: Bearer <token>
|
||||
|
||||
# 获取当前用户的消息历史
|
||||
GET /memory/message?page=1&limit=10
|
||||
Authorization: Bearer <token>
|
||||
|
||||
# 按日期过滤当前用户的消息
|
||||
GET /memory/message/filter?from=2024-01-01&to=2024-01-31
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```bash
|
||||
# 1. 登录获取 token
|
||||
TOKEN=$(curl -X POST http://localhost:7002/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"user","password":"pass"}' \
|
||||
| jq -r '.data.token')
|
||||
|
||||
# 2. 添加记忆
|
||||
curl -X POST http://localhost:7002/memory \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"},
|
||||
{"role": "assistant", "content": "Hi there!"}
|
||||
],
|
||||
"timestamp": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'"
|
||||
}'
|
||||
|
||||
# 3. 搜索记忆
|
||||
curl "http://localhost:7002/memory/search?query=hello" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# 4. 获取消息历史(分页)
|
||||
curl "http://localhost:7002/memory/message?page=1&limit=10" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# 5. 按日期范围过滤
|
||||
curl "http://localhost:7002/memory/message/filter?from=2024-01-01&to=2024-01-31" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 安全优势
|
||||
|
||||
### ✅ 更安全
|
||||
|
||||
- 用户只能访问自己的数据
|
||||
- 无法通过修改 userId 参数访问其他用户的数据
|
||||
- 所有操作都需要有效的认证 token
|
||||
|
||||
### ✅ 更简洁
|
||||
|
||||
- API 调用更简单,无需手动传递 userId
|
||||
- 减少了参数验证和错误处理
|
||||
- 代码更清晰易维护
|
||||
|
||||
### ✅ 一致性
|
||||
|
||||
- 与其他需要认证的模块保持一致
|
||||
- 统一的认证流程和错误处理
|
||||
- 符合 RESTful 最佳实践
|
||||
|
||||
---
|
||||
|
||||
## 迁移指南
|
||||
|
||||
### 1. 更新客户端代码
|
||||
|
||||
#### 之前:
|
||||
```javascript
|
||||
// 需要手动传入 userId
|
||||
const settings = await fetch(`/settings/${userId}`)
|
||||
const memories = await fetch(`/memory/search?query=hello&userId=${userId}`)
|
||||
```
|
||||
|
||||
#### 现在:
|
||||
```javascript
|
||||
// 自动使用 token 中的用户信息
|
||||
const settings = await fetch('/settings', {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
})
|
||||
|
||||
const memories = await fetch('/memory/search?query=hello', {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
})
|
||||
```
|
||||
|
||||
### 2. 错误处理
|
||||
|
||||
新增错误响应:
|
||||
|
||||
```json
|
||||
// 401 Unauthorized - 未提供 token 或 token 无效
|
||||
{
|
||||
"success": false,
|
||||
"error": "No bearer token provided"
|
||||
}
|
||||
|
||||
// 401 Unauthorized - Token 过期
|
||||
{
|
||||
"success": false,
|
||||
"error": "Invalid or expired token"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 端点总结
|
||||
|
||||
### Settings 模块 `/settings` 🔒
|
||||
|
||||
| 方法 | 路径 | 说明 | 认证 |
|
||||
|------|------|------|------|
|
||||
| GET | `/` | 获取当前用户设置 | ✅ 必需 |
|
||||
| PUT | `/` | 更新当前用户设置 | ✅ 必需 |
|
||||
|
||||
### Memory 模块 `/memory` 🔒
|
||||
|
||||
| 方法 | 路径 | 说明 | 认证 |
|
||||
|------|------|------|------|
|
||||
| POST | `/` | 添加记忆 | ✅ 必需 |
|
||||
| GET | `/search` | 搜索记忆 | ✅ 必需 |
|
||||
| GET | `/message` | 获取消息列表(分页) | ✅ 必需 |
|
||||
| GET | `/message/filter` | 按日期范围过滤消息 | ✅ 必需 |
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [认证系统文档](./AUTH_README.md)
|
||||
- [用户管理文档](./USER_MANAGEMENT.md)
|
||||
- [项目设置指南](../../SETUP.md)
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
# 认证系统使用文档
|
||||
|
||||
## 概述
|
||||
|
||||
本项目使用 JWT (JSON Web Token) 进行用户认证,集成了 Elysia 官方插件:
|
||||
- [@elysiajs/jwt](https://elysiajs.com/plugins/jwt.html) - JWT token 生成和验证
|
||||
- [@elysiajs/bearer](https://elysiajs.com/plugins/bearer.html) - Bearer token 提取
|
||||
|
||||
## 环境变量配置
|
||||
|
||||
在项目根目录的 `.env` 文件中配置以下变量:
|
||||
|
||||
```bash
|
||||
# Root 超级用户配置
|
||||
ROOT_USER=admin
|
||||
ROOT_USER_PASSWORD=your_secure_password
|
||||
|
||||
# JWT 配置
|
||||
JWT_SECRET=your-jwt-secret-key-change-in-production
|
||||
JWT_EXPIRES_IN=7d # Token 有效期,可选
|
||||
|
||||
# API 服务器端口
|
||||
API_SERVER_PORT=7002
|
||||
```
|
||||
|
||||
## API 端点
|
||||
|
||||
### 1. 登录 POST `/auth/login`
|
||||
|
||||
用户登录并获取 JWT token。
|
||||
|
||||
**请求体:**
|
||||
```json
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "your_password"
|
||||
}
|
||||
```
|
||||
|
||||
**成功响应:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"user": {
|
||||
"id": "root",
|
||||
"username": "admin",
|
||||
"role": "admin",
|
||||
"displayName": "Root User",
|
||||
"email": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应:**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Invalid username or password"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 验证 Token GET `/auth/verify`
|
||||
|
||||
验证 JWT token 是否有效。
|
||||
|
||||
**请求头:**
|
||||
```
|
||||
Authorization: Bearer <your_jwt_token>
|
||||
```
|
||||
|
||||
**成功响应:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"userId": "root",
|
||||
"username": "admin",
|
||||
"role": "admin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应:**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Invalid or expired token"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 获取当前用户信息 GET `/auth/me`
|
||||
|
||||
获取当前登录用户的信息。
|
||||
|
||||
**请求头:**
|
||||
```
|
||||
Authorization: Bearer <your_jwt_token>
|
||||
```
|
||||
|
||||
**成功响应:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"userId": "root",
|
||||
"username": "admin",
|
||||
"role": "admin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### JavaScript/TypeScript 客户端
|
||||
|
||||
```typescript
|
||||
// 1. 登录
|
||||
const loginResponse = await fetch('http://localhost:7002/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: 'admin',
|
||||
password: 'your_password',
|
||||
}),
|
||||
})
|
||||
|
||||
const loginData = await loginResponse.json()
|
||||
const token = loginData.data.token
|
||||
|
||||
// 2. 使用 token 访问受保护的资源
|
||||
const meResponse = await fetch('http://localhost:7002/auth/me', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
|
||||
const userData = await meResponse.json()
|
||||
console.log(userData.data) // 用户信息
|
||||
```
|
||||
|
||||
### curl 示例
|
||||
|
||||
```bash
|
||||
# 1. 登录
|
||||
curl -X POST http://localhost:7002/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"your_password"}'
|
||||
|
||||
# 2. 使用返回的 token 访问受保护资源
|
||||
TOKEN="<your_jwt_token>"
|
||||
curl http://localhost:7002/auth/me \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
## 在其他模块中使用认证
|
||||
|
||||
### 使用认证中间件
|
||||
|
||||
项目提供了三个认证中间件:
|
||||
|
||||
#### 1. `authMiddleware` - 强制认证
|
||||
|
||||
要求请求必须包含有效的 Bearer token,否则返回 401。
|
||||
|
||||
```typescript
|
||||
import Elysia from 'elysia'
|
||||
import { authMiddleware } from './middlewares'
|
||||
|
||||
export const protectedModule = new Elysia({ prefix: '/protected' })
|
||||
.use(authMiddleware)
|
||||
.get('/data', ({ user }) => {
|
||||
// user 包含: { userId, username, role }
|
||||
return {
|
||||
success: true,
|
||||
message: `Hello ${user.username}!`,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### 2. `optionalAuthMiddleware` - 可选认证
|
||||
|
||||
如果有 token 则验证,没有 token 也允许访问(user 为 null)。
|
||||
|
||||
```typescript
|
||||
import Elysia from 'elysia'
|
||||
import { optionalAuthMiddleware } from './middlewares'
|
||||
|
||||
export const publicModule = new Elysia({ prefix: '/public' })
|
||||
.use(optionalAuthMiddleware)
|
||||
.get('/data', ({ user }) => {
|
||||
if (user) {
|
||||
return { message: `Welcome back, ${user.username}!` }
|
||||
}
|
||||
return { message: 'Welcome, guest!' }
|
||||
})
|
||||
```
|
||||
|
||||
#### 3. `adminMiddleware` - 管理员权限
|
||||
|
||||
要求用户必须是 admin 角色,否则返回 403。
|
||||
|
||||
```typescript
|
||||
import Elysia from 'elysia'
|
||||
import { adminMiddleware } from './middlewares'
|
||||
|
||||
export const adminModule = new Elysia({ prefix: '/admin' })
|
||||
.use(adminMiddleware)
|
||||
.get('/users', ({ user }) => {
|
||||
// 只有 admin 才能访问
|
||||
return { success: true, data: [] }
|
||||
})
|
||||
```
|
||||
|
||||
## Root 用户说明
|
||||
|
||||
Root 用户是通过环境变量配置的超级管理员:
|
||||
|
||||
- **优先级最高**:登录时优先检查是否为 Root 用户
|
||||
- **不存储在数据库**:直接通过环境变量验证
|
||||
- **始终是 admin 角色**:拥有最高权限
|
||||
- **用于系统初始化**:可用于创建其他管理员用户
|
||||
|
||||
## 数据库用户
|
||||
|
||||
除了 Root 用户外,系统支持在数据库中创建普通用户:
|
||||
|
||||
```typescript
|
||||
import { createUser } from '@memohome/db/src/user-helpers'
|
||||
|
||||
// 创建新用户
|
||||
const newUser = await createUser({
|
||||
username: 'john_doe',
|
||||
email: 'john@example.com',
|
||||
passwordHash: await Bun.password.hash('password123'),
|
||||
role: 'member',
|
||||
displayName: 'John Doe',
|
||||
})
|
||||
```
|
||||
|
||||
用户密码使用 `Bun.password.hash` 加密存储,登录时使用 `Bun.password.verify` 验证。
|
||||
|
||||
## 安全建议
|
||||
|
||||
1. **生产环境配置**:
|
||||
- 使用强密码作为 `ROOT_USER_PASSWORD`
|
||||
- 设置随机的 `JWT_SECRET`(至少 32 字符)
|
||||
- 不要将 `.env` 文件提交到代码仓库
|
||||
|
||||
2. **Token 管理**:
|
||||
- Token 默认有效期为 7 天
|
||||
- 前端应安全存储 token(如 httpOnly cookie 或安全的 localStorage)
|
||||
- Token 过期后需要重新登录
|
||||
|
||||
3. **密码策略**:
|
||||
- 要求用户使用强密码
|
||||
- 考虑实现密码复杂度验证
|
||||
- 考虑添加密码重置功能
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **Elysia**:Web 框架
|
||||
- **@elysiajs/jwt**:JWT token 生成和验证插件
|
||||
- **@elysiajs/bearer**:Bearer token 提取插件
|
||||
- **Bun.password**:密码加密和验证(基于 bcrypt)
|
||||
- **Drizzle ORM**:数据库操作
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Elysia JWT Plugin](https://elysiajs.com/plugins/jwt.html)
|
||||
- [Elysia Bearer Plugin](https://elysiajs.com/plugins/bearer.html)
|
||||
- [用户表设计文档](../db/USERS_SCHEMA.md)
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
# Settings API 文档
|
||||
|
||||
## 概述
|
||||
|
||||
Settings API 用于管理用户的个性化设置,包括默认模型配置和 Agent 行为设置。
|
||||
|
||||
## 端点
|
||||
|
||||
### GET `/settings` 🔒 需要认证
|
||||
|
||||
获取当前用户的设置。
|
||||
|
||||
#### 请求
|
||||
|
||||
**Headers:**
|
||||
```
|
||||
Authorization: Bearer <your_jwt_token>
|
||||
```
|
||||
|
||||
#### 成功响应 (200 OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"userId": "user-id",
|
||||
"defaultChatModel": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"defaultEmbeddingModel": "223e4567-e89b-12d3-a456-426614174001",
|
||||
"defaultSummaryModel": "323e4567-e89b-12d3-a456-426614174002",
|
||||
"maxContextLoadTime": 60,
|
||||
"language": "Chinese"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应 (404 Not Found)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Settings not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### PUT `/settings` 🔒 需要认证
|
||||
|
||||
更新或创建当前用户的设置(Upsert 操作)。
|
||||
|
||||
#### 请求
|
||||
|
||||
**Headers:**
|
||||
```
|
||||
Authorization: Bearer <your_jwt_token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"defaultChatModel": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"defaultEmbeddingModel": "223e4567-e89b-12d3-a456-426614174001",
|
||||
"defaultSummaryModel": "323e4567-e89b-12d3-a456-426614174002",
|
||||
"maxContextLoadTime": 60,
|
||||
"language": "Chinese"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
|
||||
| 字段 | 类型 | 必需 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| defaultChatModel | string (uuid) | 否 | null | 默认聊天模型 ID |
|
||||
| defaultEmbeddingModel | string (uuid) | 否 | null | 默认嵌入模型 ID |
|
||||
| defaultSummaryModel | string (uuid) | 否 | null | 默认摘要模型 ID |
|
||||
| maxContextLoadTime | number | 否 | 60 | Agent 加载上下文的时间范围(分钟,1-1440) |
|
||||
| language | string | 否 | "Same as user input" | Agent 回复的首选语言 |
|
||||
|
||||
#### 成功响应 (200 OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"userId": "user-id",
|
||||
"defaultChatModel": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"defaultEmbeddingModel": "223e4567-e89b-12d3-a456-426614174001",
|
||||
"defaultSummaryModel": "323e4567-e89b-12d3-a456-426614174002",
|
||||
"maxContextLoadTime": 60,
|
||||
"language": "Chinese"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 完整工作流
|
||||
|
||||
```bash
|
||||
# 1. 登录获取 token
|
||||
TOKEN=$(curl -X POST http://localhost:7002/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"your_password"}' \
|
||||
| jq -r '.data.token')
|
||||
|
||||
# 2. 获取当前设置
|
||||
curl http://localhost:7002/settings \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# 3. 更新设置
|
||||
curl -X PUT http://localhost:7002/settings \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"defaultChatModel": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"defaultEmbeddingModel": "223e4567-e89b-12d3-a456-426614174001",
|
||||
"defaultSummaryModel": "323e4567-e89b-12d3-a456-426614174002",
|
||||
"maxContextLoadTime": 120,
|
||||
"language": "English"
|
||||
}'
|
||||
|
||||
# 4. 只更新部分字段
|
||||
curl -X PUT http://localhost:7002/settings \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"maxContextLoadTime": 30,
|
||||
"language": "Chinese"
|
||||
}'
|
||||
```
|
||||
|
||||
### JavaScript/TypeScript 示例
|
||||
|
||||
```typescript
|
||||
// 获取设置
|
||||
async function getSettings(token: string) {
|
||||
const response = await fetch('http://localhost:7002/settings', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
return data
|
||||
}
|
||||
|
||||
// 更新设置
|
||||
async function updateSettings(token: string, settings: {
|
||||
defaultChatModel?: string
|
||||
defaultEmbeddingModel?: string
|
||||
defaultSummaryModel?: string
|
||||
maxContextLoadTime?: number
|
||||
language?: string
|
||||
}) {
|
||||
const response = await fetch('http://localhost:7002/settings', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(settings),
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
return data
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
const token = 'your_jwt_token'
|
||||
|
||||
// 获取当前设置
|
||||
const currentSettings = await getSettings(token)
|
||||
console.log(currentSettings)
|
||||
|
||||
// 更新 Agent 设置
|
||||
await updateSettings(token, {
|
||||
maxContextLoadTime: 90,
|
||||
language: 'Chinese',
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Agent 设置说明
|
||||
|
||||
### maxContextLoadTime
|
||||
|
||||
控制 Agent 加载历史对话上下文的时间范围。
|
||||
|
||||
- **类型**: 整数(分钟)
|
||||
- **范围**: 1-1440(1分钟到24小时)
|
||||
- **默认值**: 60(1小时)
|
||||
- **说明**:
|
||||
- 值越大,Agent 能访问更久远的对话历史
|
||||
- 但也会增加 token 使用量和响应时间
|
||||
- 建议根据实际需求调整
|
||||
|
||||
**示例:**
|
||||
- `30` - 加载最近30分钟的对话
|
||||
- `60` - 加载最近1小时的对话(默认)
|
||||
- `1440` - 加载最近24小时的对话
|
||||
|
||||
### language
|
||||
|
||||
设置 Agent 回复的首选语言。
|
||||
|
||||
- **类型**: 字符串
|
||||
- **默认值**: "Same as user input"(与用户输入相同)
|
||||
- **说明**:
|
||||
- 可以设置为任何语言名称
|
||||
- Agent 会尽量使用指定语言回复
|
||||
- 特殊值 "Same as user input" 表示跟随用户输入语言
|
||||
|
||||
**常用值:**
|
||||
- `"Same as user input"` - 自动匹配用户语言(默认)
|
||||
- `"Chinese"` - 中文
|
||||
- `"English"` - 英文
|
||||
- `"Japanese"` - 日文
|
||||
- `"Spanish"` - 西班牙语
|
||||
|
||||
---
|
||||
|
||||
## Agent 使用优先级
|
||||
|
||||
当使用 Agent API 时,配置的优先级为:
|
||||
|
||||
1. **请求参数** - `/agent/stream` 请求中直接指定的参数
|
||||
2. **用户设置** - Settings 中保存的默认值
|
||||
3. **系统默认** - 内置的默认值
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 情况1: 使用请求参数(最高优先级)
|
||||
curl -X POST http://localhost:7002/agent/stream \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d '{
|
||||
"message": "Hello",
|
||||
"maxContextLoadTime": 30,
|
||||
"language": "English"
|
||||
}'
|
||||
# 使用: maxContextLoadTime=30, language="English"
|
||||
|
||||
# 情况2: 使用用户设置(如果请求中未指定)
|
||||
# 假设用户设置: maxContextLoadTime=60, language="Chinese"
|
||||
curl -X POST http://localhost:7002/agent/stream \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d '{"message": "Hello"}'
|
||||
# 使用: maxContextLoadTime=60, language="Chinese"
|
||||
|
||||
# 情况3: 使用系统默认(如果都未设置)
|
||||
# 使用: maxContextLoadTime=60, language="Same as user input"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据库 Schema
|
||||
|
||||
```sql
|
||||
CREATE TABLE settings (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
default_chat_model UUID REFERENCES model(id),
|
||||
default_embedding_model UUID REFERENCES model(id),
|
||||
default_summary_model UUID REFERENCES model(id),
|
||||
max_context_load_time INTEGER DEFAULT 60,
|
||||
language TEXT DEFAULT 'Same as user input'
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Agent API](./AGENT_API.md)
|
||||
- [Model API](./README.md)
|
||||
- [认证系统](./AUTH_README.md)
|
||||
- [API 变更说明](./API_CHANGES.md)
|
||||
|
||||
@@ -1,480 +0,0 @@
|
||||
# 用户管理 API 文档
|
||||
|
||||
## 概述
|
||||
|
||||
用户管理模块提供了完整的用户 CRUD 操作接口,**仅限管理员访问**。所有端点都需要携带有效的管理员 JWT token。
|
||||
|
||||
## 权限要求
|
||||
|
||||
所有用户管理接口都受 `adminMiddleware` 保护:
|
||||
- 必须提供有效的 Bearer token
|
||||
- 用户角色必须是 `admin`
|
||||
- 否则返回 `403 Forbidden` 错误
|
||||
|
||||
## API 端点
|
||||
|
||||
### 基础 URL
|
||||
|
||||
```
|
||||
http://localhost:7002/user
|
||||
```
|
||||
|
||||
### 请求头
|
||||
|
||||
所有请求都需要包含认证 token:
|
||||
|
||||
```
|
||||
Authorization: Bearer <your_admin_jwt_token>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 获取所有用户 GET `/user`
|
||||
|
||||
获取系统中所有用户的列表。
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl http://localhost:7002/user \
|
||||
-H "Authorization: Bearer <admin_token>"
|
||||
```
|
||||
|
||||
### 成功响应 (200 OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"username": "john_doe",
|
||||
"email": "john@example.com",
|
||||
"role": "member",
|
||||
"displayName": "John Doe",
|
||||
"avatarUrl": "https://example.com/avatar.jpg",
|
||||
"isActive": "true",
|
||||
"createdAt": "2024-01-10T10:00:00Z",
|
||||
"updatedAt": "2024-01-10T10:00:00Z",
|
||||
"lastLoginAt": "2024-01-10T12:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 获取单个用户 GET `/user/:id`
|
||||
|
||||
根据用户 ID 获取用户详细信息。
|
||||
|
||||
### 路径参数
|
||||
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | UUID | 是 | 用户的唯一标识符 |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl http://localhost:7002/user/123e4567-e89b-12d3-a456-426614174000 \
|
||||
-H "Authorization: Bearer <admin_token>"
|
||||
```
|
||||
|
||||
### 成功响应 (200 OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"username": "john_doe",
|
||||
"email": "john@example.com",
|
||||
"role": "member",
|
||||
"displayName": "John Doe",
|
||||
"avatarUrl": "https://example.com/avatar.jpg",
|
||||
"isActive": "true",
|
||||
"createdAt": "2024-01-10T10:00:00Z",
|
||||
"updatedAt": "2024-01-10T10:00:00Z",
|
||||
"lastLoginAt": "2024-01-10T12:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应 (404 Not Found)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "User not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 创建用户 POST `/user`
|
||||
|
||||
创建新用户账户。
|
||||
|
||||
### 请求体
|
||||
|
||||
| 字段 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| username | string | 是 | 用户名(3-50字符,唯一) |
|
||||
| email | string | 否 | 邮箱地址(必须是有效格式,唯一) |
|
||||
| password | string | 是 | 密码(至少6个字符) |
|
||||
| role | string | 否 | 用户角色:`admin` 或 `member`(默认 `member`) |
|
||||
| displayName | string | 否 | 显示名称 |
|
||||
| avatarUrl | string | 否 | 头像 URL(必须是有效的 URL) |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:7002/user \
|
||||
-H "Authorization: Bearer <admin_token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "jane_smith",
|
||||
"email": "jane@example.com",
|
||||
"password": "secure_password123",
|
||||
"role": "member",
|
||||
"displayName": "Jane Smith"
|
||||
}'
|
||||
```
|
||||
|
||||
### 成功响应 (201 Created)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "223e4567-e89b-12d3-a456-426614174001",
|
||||
"username": "jane_smith",
|
||||
"email": "jane@example.com",
|
||||
"role": "member",
|
||||
"displayName": "Jane Smith",
|
||||
"avatarUrl": null,
|
||||
"isActive": "true",
|
||||
"createdAt": "2024-01-10T15:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: 创建用户时会自动创建一个默认的 settings 条目,包含:
|
||||
- `maxContextLoadTime`: 60(分钟)
|
||||
- `language`: "Same as user input"
|
||||
- 所有模型配置为 `null`(需要用户后续配置)
|
||||
|
||||
### 错误响应 (409 Conflict)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Username already exists"
|
||||
}
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Email already exists"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 更新用户 PUT `/user/:id`
|
||||
|
||||
更新用户信息(不包括密码)。
|
||||
|
||||
### 路径参数
|
||||
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | UUID | 是 | 用户的唯一标识符 |
|
||||
|
||||
### 请求体(所有字段都是可选的)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| email | string | 邮箱地址 |
|
||||
| role | string | 用户角色:`admin` 或 `member` |
|
||||
| displayName | string | 显示名称 |
|
||||
| avatarUrl | string | 头像 URL |
|
||||
| isActive | string | 账户状态:`true` 或 `false` |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl -X PUT http://localhost:7002/user/223e4567-e89b-12d3-a456-426614174001 \
|
||||
-H "Authorization: Bearer <admin_token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"displayName": "Jane Smith (Updated)",
|
||||
"role": "admin",
|
||||
"isActive": "true"
|
||||
}'
|
||||
```
|
||||
|
||||
### 成功响应 (200 OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "223e4567-e89b-12d3-a456-426614174001",
|
||||
"username": "jane_smith",
|
||||
"email": "jane@example.com",
|
||||
"role": "admin",
|
||||
"displayName": "Jane Smith (Updated)",
|
||||
"avatarUrl": null,
|
||||
"isActive": "true",
|
||||
"createdAt": "2024-01-10T15:00:00Z",
|
||||
"updatedAt": "2024-01-10T16:00:00Z",
|
||||
"lastLoginAt": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应 (404 Not Found)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "User not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 删除用户 DELETE `/user/:id`
|
||||
|
||||
删除用户账户。
|
||||
|
||||
### 路径参数
|
||||
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | UUID | 是 | 用户的唯一标识符 |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl -X DELETE http://localhost:7002/user/223e4567-e89b-12d3-a456-426614174001 \
|
||||
-H "Authorization: Bearer <admin_token>"
|
||||
```
|
||||
|
||||
### 成功响应 (200 OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "223e4567-e89b-12d3-a456-426614174001",
|
||||
"username": "jane_smith"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应 (404 Not Found)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "User not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 更新用户密码 PATCH `/user/:id/password`
|
||||
|
||||
重置或更新用户密码。
|
||||
|
||||
### 路径参数
|
||||
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | UUID | 是 | 用户的唯一标识符 |
|
||||
|
||||
### 请求体
|
||||
|
||||
| 字段 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| password | string | 是 | 新密码(至少6个字符) |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl -X PATCH http://localhost:7002/user/223e4567-e89b-12d3-a456-426614174001/password \
|
||||
-H "Authorization: Bearer <admin_token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"password": "new_secure_password456"
|
||||
}'
|
||||
```
|
||||
|
||||
### 成功响应 (200 OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "223e4567-e89b-12d3-a456-426614174001",
|
||||
"username": "jane_smith"
|
||||
},
|
||||
"message": "Password updated successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应 (404 Not Found)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "User not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误响应
|
||||
|
||||
### 401 Unauthorized
|
||||
|
||||
未提供 token 或 token 无效:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "No bearer token provided"
|
||||
}
|
||||
```
|
||||
|
||||
### 403 Forbidden
|
||||
|
||||
非管理员用户尝试访问:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Forbidden: Admin access required"
|
||||
}
|
||||
```
|
||||
|
||||
### 400 Bad Request
|
||||
|
||||
请求数据验证失败:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Username must be at least 3 characters"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 完整工作流
|
||||
|
||||
```bash
|
||||
# 1. 管理员登录获取 token
|
||||
TOKEN=$(curl -X POST http://localhost:7002/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"your_admin_password"}' \
|
||||
| jq -r '.data.token')
|
||||
|
||||
# 2. 创建新用户
|
||||
curl -X POST http://localhost:7002/user \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "new_user",
|
||||
"email": "newuser@example.com",
|
||||
"password": "password123",
|
||||
"role": "member",
|
||||
"displayName": "New User"
|
||||
}'
|
||||
|
||||
# 3. 获取所有用户
|
||||
curl http://localhost:7002/user \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# 4. 获取特定用户
|
||||
USER_ID="123e4567-e89b-12d3-a456-426614174000"
|
||||
curl http://localhost:7002/user/$USER_ID \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# 5. 更新用户信息
|
||||
curl -X PUT http://localhost:7002/user/$USER_ID \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"displayName": "Updated Name",
|
||||
"role": "admin"
|
||||
}'
|
||||
|
||||
# 6. 重置用户密码
|
||||
curl -X PATCH http://localhost:7002/user/$USER_ID/password \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"password": "new_password123"
|
||||
}'
|
||||
|
||||
# 7. 删除用户
|
||||
curl -X DELETE http://localhost:7002/user/$USER_ID \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 自动化行为
|
||||
|
||||
### 创建用户时的自动操作
|
||||
|
||||
当创建新用户时,系统会自动执行以下操作:
|
||||
|
||||
1. **密码加密**: 使用 bcrypt 算法加密密码
|
||||
2. **创建 Settings**: 自动为用户创建默认设置条目
|
||||
- `maxContextLoadTime`: 60 分钟
|
||||
- `language`: "Same as user input"
|
||||
- 模型配置: 全部为 null(需要用户后续配置)
|
||||
|
||||
这确保每个用户都有完整的配置,可以立即使用系统功能。
|
||||
|
||||
---
|
||||
|
||||
## 安全注意事项
|
||||
|
||||
1. **管理员权限**:
|
||||
- 所有接口仅限管理员访问
|
||||
- 确保 Root 用户的密码强度足够
|
||||
- 定期审计管理员账户
|
||||
|
||||
2. **密码安全**:
|
||||
- 密码使用 bcrypt 加密存储
|
||||
- 最少 6 个字符(建议更高要求)
|
||||
- 重置密码后建议通知用户
|
||||
|
||||
3. **数据验证**:
|
||||
- 所有输入都经过严格验证
|
||||
- 邮箱和用户名必须唯一
|
||||
- URL 格式必须有效
|
||||
|
||||
4. **用户状态**:
|
||||
- 使用 `isActive` 字段禁用用户而非直接删除
|
||||
- 保留用户数据用于审计
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [认证系统文档](./AUTH_README.md)
|
||||
- [用户表设计](../db/USERS_SCHEMA.md)
|
||||
- [项目设置指南](../../SETUP.md)
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
/**
|
||||
* Agent Stream Client Example
|
||||
*
|
||||
* This example demonstrates how to use the /agent/stream API endpoint
|
||||
* to have a streaming conversation with the AI agent.
|
||||
*
|
||||
* Usage:
|
||||
* bun run agent-stream-client.ts
|
||||
*/
|
||||
|
||||
const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:7002'
|
||||
|
||||
interface LoginResponse {
|
||||
success: boolean
|
||||
data?: {
|
||||
token: string
|
||||
user: {
|
||||
id: string
|
||||
username: string
|
||||
role: string
|
||||
}
|
||||
}
|
||||
error?: string
|
||||
}
|
||||
|
||||
async function login(username: string, password: string): Promise<string> {
|
||||
const response = await fetch(`${API_BASE_URL}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password }),
|
||||
})
|
||||
|
||||
const data: LoginResponse = await response.json()
|
||||
|
||||
if (!data.success || !data.data?.token) {
|
||||
throw new Error(data.error || 'Login failed')
|
||||
}
|
||||
|
||||
return data.data.token
|
||||
}
|
||||
|
||||
async function streamAgentChat(message: string, token: string) {
|
||||
console.log(`\n📤 User: ${message}`)
|
||||
console.log('🤖 Agent: ', { end: '' })
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/agent/stream`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
message,
|
||||
maxContextLoadTime: 60,
|
||||
language: 'Same as user input',
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.error || 'Stream request failed')
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
if (!reader) {
|
||||
throw new Error('No reader available')
|
||||
}
|
||||
|
||||
let hasOutput = false
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
|
||||
const chunk = decoder.decode(value, { stream: true })
|
||||
const lines = chunk.split('\n')
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = line.slice(6).trim()
|
||||
|
||||
if (data === '[DONE]') {
|
||||
console.log('\n\n✅ Stream completed')
|
||||
return
|
||||
}
|
||||
|
||||
if (!data) continue
|
||||
|
||||
try {
|
||||
const event = JSON.parse(data)
|
||||
|
||||
if (event.type === 'text-delta' && event.text) {
|
||||
process.stdout.write(event.text)
|
||||
hasOutput = true
|
||||
} else if (event.type === 'tool-call') {
|
||||
console.log(`\n[🔧 Tool: ${event.toolName}]`)
|
||||
hasOutput = true
|
||||
} else if (event.type === 'error') {
|
||||
console.error('\n❌ Error:', event.error)
|
||||
throw new Error(event.error)
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
// Skip invalid JSON
|
||||
continue
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock()
|
||||
}
|
||||
|
||||
if (!hasOutput) {
|
||||
console.log('(No response)')
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// Get credentials from environment or use defaults
|
||||
const username = process.env.USERNAME || 'admin'
|
||||
const password = process.env.PASSWORD || 'admin'
|
||||
|
||||
console.log('🔐 Logging in...')
|
||||
const token = await login(username, password)
|
||||
console.log('✅ Login successful')
|
||||
|
||||
// Example conversations
|
||||
const messages = [
|
||||
'你好,请介绍一下你自己',
|
||||
'你能帮我做什么?',
|
||||
'记住:我的名字是张三',
|
||||
'我的名字是什么?',
|
||||
]
|
||||
|
||||
for (const message of messages) {
|
||||
await streamAgentChat(message, token)
|
||||
// Wait a bit between messages
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
}
|
||||
|
||||
console.log('\n🎉 All conversations completed!')
|
||||
} catch (error) {
|
||||
console.error('\n❌ Error:', error instanceof Error ? error.message : String(error))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Ctrl+C gracefully
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n\n👋 Goodbye!')
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user