Files
Cyrene/docs/api/gateway-api.md
T
AskaEth 22d7b91cb1 feat: Gateway 消息排队机制 — 同会话串行化处理
同一 session 的消息按顺序处理:当前回复未完成时新消息进入队列,
完成后自动消费下一条。避免并发请求导致上下文竞争和响应交错。
客户端收到 type:"queued" 时可显示排队状态。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 21:06:59 +08:00

1125 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cyrene Gateway API 文档
**版本:** v1 | **Base URL:** `http://<gateway-host>:<port>/api/v1`
所有需要认证的接口在 HTTP Header 中携带 JWT
```
Authorization: Bearer <jwt_token>
```
WebSocket 通过 query 参数传 token`/ws/chat?token=<jwt_token>`
---
## 目录
1. [认证](#1-认证)
2. [WebSocket 实时通信](#2-websocket-实时通信)
3. [会话管理](#3-会话管理)
4. [消息搜索](#4-消息搜索)
5. [语音 STT / TTS](#5-语音-stt--tts)
6. [文件管理](#6-文件管理)
7. [图片分析](#7-图片分析)
8. [知识库](#8-知识库)
9. [记忆管理](#9-记忆管理)
10. [自动化规则与场景](#10-自动化规则与场景)
11. [通知推送](#11-通知推送)
12. [提醒管理](#12-提醒管理)
13. [Webhook 第三方接入](#13-webhook-第三方接入)
14. [Admin 管理](#14-admin-管理)
15. [健康检查](#15-健康检查)
---
## 1. 认证
### POST /auth/register — 注册
Auth: 无。IP 限流 ~5/min。仅 `REGISTRATION_ENABLED=true` 时可用(默认关闭)。
```json
// 请求
{
"username": "string (3-32 chars, ^[a-zA-Z0-9_]+$)",
"password": "string (6-64 chars)",
"email": "string (需含 @)",
"nickname": "string (1-32 chars)",
"verify_code": "string (6 chars, 开发阶段固定 000000)"
}
// 响应 201
{
"user_id": "user_<username>",
"token": "JWT",
"refresh_token": "JWT (30天)",
"expires": 1717000000,
"nickname": "string"
}
```
错误: 400 `用户名格式无效`, 400 `邮箱格式无效`, 400 `验证码错误`, 403 `当前不开放公开注册`, 409 `用户名已被注册`
---
### POST /auth/login — 登录
Auth: 无。IP 限流 ~5/min。
管理员始终通过 `.env` 中的 `ADMIN_USERNAME` / `ADMIN_PASSWORD` 验证,不依赖数据库状态。普通用户通过数据库 bcrypt 密码哈希验证。
```json
// 请求
{
"username": "string",
"password": "string"
}
// 响应 200
{
"user_id": "admin", // admin 用户固定为 "admin",普通用户为 "user_<username>"
"nickname": "string",
"token": "JWT",
"refresh_token": "JWT (30天)",
"expires": 1717000000
}
```
错误: 400 `用户名格式无效`, 401 `用户名或密码错误`
---
### POST /auth/refresh — 刷新 Token
Auth: JWT(可接受已过期的 token,或在 body 中提供 refresh_token)。
```json
// 请求(可选)
{ "refresh_token": "string" }
// 响应 200
{
"token": "新 JWT",
"refresh_token": "新 JWT (30天)",
"expires": 1717000000
}
```
错误: 401 `刷新令牌无效或已过期`, 401 `未提供认证令牌`
---
### GET /profile — 查询当前用户
Auth: JWT。根据 token 返回当前登录用户的信息。
```json
// 响应 200 (管理员)
{
"user_id": "admin",
"username": "admin",
"nickname": "管理员",
"is_admin": true,
"created_at": "2026-01-01T00:00:00Z"
}
// 响应 200 (普通用户)
{
"user_id": "user_alice",
"username": "alice",
"nickname": "Alice",
"is_admin": false,
"created_at": "2026-01-01T00:00:00Z"
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `user_id` | string | 用户 IDadmin 为 `"admin"`,普通用户为 `"user_<username>"` |
| `username` | string | 用户名(管理员为 `.env` 中配置的 `ADMIN_USERNAME` |
| `nickname` | string | 显示昵称 |
| `is_admin` | bool | 是否管理员 |
| `created_at` | string | 注册时间 (RFC3339),数据库不可用时为 null |
错误: 401 `未提供认证令牌`, 404 `用户不存在`
---
## 2. WebSocket 实时通信
### GET /ws/chat — 建立连接
```
ws://<gateway>/ws/chat?token=<jwt>&session_id=<optional>&client_id=<optional>&device_name=<optional>
```
| Query 参数 | 必填 | 说明 |
|-----------|------|------|
| `token` | 是* | JWT(也可用 `Authorization: Bearer` header |
| `session_id` | 否 | 不传自动生成 `session_` + 12 随机字符 |
| `client_id` | 否 | 多端标识 |
| `device_name` | 否 | 设备名 |
注意:WebSocket 仅限 admin 用户;非 admin 收到 **403**
---
### 客户端 → 服务端 (ClientMessage)
```json
{
"type": "message|voice_input|ping|history",
"session_id": "string (可选)",
"mode": "text|voice_msg|voice_assistant",
"content": "string (纯图片消息可留空,文字+图片时填写提问内容)",
"audio_data": "string (voice_input 类型必填, base64)",
"attachments": [
{
"type": "image",
"url": "string (base64 data URI, 旧格式, 向后兼容)",
"file_id": "string (文件 UUID, 新格式推荐, 配合 POST /files/upload 使用)",
"thumbnail_url": "string (缩略图 URL, 跨设备同步友好)",
"filename": "string",
"width": 0,
"height": 0,
"size": 0,
"description": "string"
}
],
```
> **图片附件两种格式**
> - **旧格式** (`url`): base64 data URI,直接内嵌于 WebSocket 消息中,简单但不适合跨设备同步。
> - **新格式(推荐)** (`file_id`): 先通过 [`POST /api/v1/files/upload`](#post-filesupload) 上传图片获取 `file_id` 和 `thumbnail_url`,消息中只携带轻量引用。Gateway 自动解析为本地文件 URL 传给 AI-Core。
```
"timestamp": 1717000000000,
"client_id": "string",
"device_name": "string",
"user_agent": "string"
}
```
| type | 说明 |
|------|------|
| `message` | 文字聊天,触发 AI 回复 |
| `voice_input` | 语音输入,先转录再作为 message 处理 |
| `ping` | 心跳,自动回复 pong |
| `history` | 请求历史消息 |
---
### 服务端 → 客户端 (ServerMessage)
```json
{
"type": "string (见下表)",
"message_id": "string",
"text": "string (完整文本)",
"content": "string (增量文本/完整内容)",
"role": "user|assistant|action|system",
"msg_type": "chat|action|thinking|tool_progress|system_info|markdown|code (后端始终填充,前端无需自行解析)",
"session_id": "string",
"error": "string (仅错误时)",
"timestamp": 1717000000000,
"segments": [ { "index": 0, "text": "string", "audio_url": "string", "duration_ms": 0 } ],
"messages": [ Message ],
"thinking_status": "string",
"thinking_content": "string",
"tool_progress": { "tool_name": "string", "status": "started|running|completed|failed", "progress": 0.5, "message": "string" },
"system_info": { "level": "info|warning|error", "message": "string", "action": "string" },
"notification": { "id": "string", "type": "info|warning|success|thinking|reminder", "title": "string", "body": "string", "timestamp": "string", "data": {} },
"multi_message": { "messages": [ { "index": 0, "content": "string", "msg_type": "chat|action|system_info" } ] },
"review_messages": [ { "type": "action|chat|markdown|code|search_result", "content": "string", "delay_ms": 0, "metadata": { "language": "string (code 类型时)", "url": "string (search_result 类型时)" } } ],
"client_info": { "client_id": "string", "device_name": "string", "user_agent": "string" },
"full_audio_url": "string",
"response_mode": "string"
}
```
### ServerMessage type 列表
| type | 说明 |
|------|------|
| `response` | 完整文本回复 |
| `stream_start` | AI 开始生成 |
| `stream_chunk` | 增量文本块 |
| `stream_end` | AI 生成结束(含完整 text) |
| `stream_segments` | 流式断句(语音) |
| `voice_transcript` | 语音转录结果 |
| `error` | 错误 |
| `history_response` | 历史消息返回 |
| `notification` | 推送通知 |
| `multi_message` | 多条消息合并投递 |
| `review` | 审查后结构化消息 |
| `thinking` | 后台思考更新 |
| `tool_progress` | 工具执行进度 |
| `system_info` | 系统级通知 |
| `pong` | 响应 ping |
| `device_update` | IoT 设备状态更新 |
| `background_thinking` | 后台思考状态变更 |
| `queued` | 消息已加入处理队列(会话繁忙时) |
> **广播机制**:服务端推送分为两类:
> - **用户消息回显**`type: "response"`, `role: "user"`):通过 `SendToUserExcept` 广播,排除发送者自身(发送者本地已渲染),仅同步到同用户的其他设备。
> - **AI 回复消息**`stream_start`、`stream_end`、`response`/`review`、`multi_message`、`stream_segments` 等 `role: "assistant"` 的消息):通过 `SendToUser` 广播给所有设备,包括发送者。
> **消息类型分类 (`msg_type`)**:所有 `response`、`multi_message`、`history_response`、`stream_chunk`、`thinking`、`tool_progress`、`system_info` 类型的服务端消息中,`msg_type` 字段均由后端自动分类填充,前端只需直接读取 `msg_type` 并据此渲染,无需解析消息内容来猜测类型。
>
> `msg_type` 可选值:
> - `chat` — 普通聊天消息
> - `action` — 动作/旁白消息(如 `<action>昔涟轻轻推开窗户</action>`),前端以斜体灰色样式渲染
> - `markdown` — Markdown 格式消息,前端使用安全渲染器转换后显示(支持标题、列表、表格、代码块、链接等)
> - `code` — 独立代码块消息,前端以深色背景 + 语言标签渲染,可包含 `metadata.language` 指明编程语言
> - `thinking` — 后台思考过程,前端显示为可折叠详情块
> - `tool_progress` — 工具执行进度,前端显示进度条
> - `system_info` — 系统通知,前端居中显示为 toast 样式
>
> **`review` 消息类型详情:**
>
> 当 AI 回复包含多种格式内容(如工具调用结果中的 Markdown 文档或代码片段)时,后端解析器将回复拆分为多条 `review_messages`,每条独立指定 `type` 和 `content`
>
> - `action` — 动作/旁白文本,由 `<action>...</action>` XML 标签标记(向后兼容旧括号 `()` 和 `()` 格式),不做 Markdown 渲染,禁止断句拆分
> - `chat` — 普通聊天文本,可按句长断句
> - `markdown` — Markdown 格式文本,禁止断句拆分,前端使用安全渲染器(先 HTML 转义再转换 Markdown 语法)
> - `code` — 代码块,禁止断句拆分,前端深色背景渲染,`metadata.language` 字段携带语言标识
> - `search_result` — 搜索工具调用结果摘要(后端内部使用,通常转为 `markdown` 或 `chat` 类型展示)
>
> Markdown 渲染器支持语法:标题 (h1-h6)、粗体/斜体、行内代码/围栏代码块、链接、图片、无序/有序列表、引用块、表格、水平线。
---
### 流式响应流程
```
Client Gateway AI-Core
| | |
|-- {type:"message"} --> | |
| |-- POST /api/v1/chat --> |
| | (SSE streaming) |
| |<-- data: {delta:"..."} |
|<-- {type:"stream_start"} | |
|<-- {type:"review"} | (多条结构化消息) |
|<-- {type:"stream_segments"} | (断句事件) |
|<-- {type:"stream_end"} | (含 full text) |
```
### 消息排队机制
同一会话的消息处理为串行化队列。若上一轮 AI 回复尚未完成时用户发送新消息,新消息会加入等待队列,并在当前处理完成后自动消费。
```
Client Gateway
| |
|-- msg1 (type:"message") --> |
|<-- {type:"stream_start"} | → AI-Core 处理中...
| |
|-- msg2 (type:"message") --> |
|<-- {type:"queued"} | → 加入队列等待
| |
|<-- {type:"stream_end"} (msg1) | → msg1 完成
|<-- {type:"stream_start"} (msg2) | → 自动取出 msg2 处理
|<-- ... |
|<-- {type:"stream_end"} (msg2) |
```
> `queued` 消息表明用户消息已接收但尚未开始处理,客户端可据此显示"排队中"状态。
---
### 语音输入流程
```
Client Gateway Voice-Service
| | |
|-- {type:"voice_input", | |
| audio_data:"<base64>"} --> | |
| |-- POST /transcribe --> |
| |<-- {text:"..."} |
|<-- {type:"voice_transcript", | |
| text:"转录结果"} | |
| | |
| (Gateway 自动将转录文本 | |
| 作为 message 发给 AI-Core) | |
|<-- {type:"stream_start"} | |
|<-- ... 正常流式回复 ... | |
```
---
## 3. 会话管理
所有接口需要 JWT。Admin 可操作所有用户;非 admin 仅限自己的数据。
### POST /sessions — 创建会话
```json
// 请求 (全部可选,空 body 也可)
{
"session_id": "string (可选,不传自动生成)",
"title": "string (默认 '新的对话')",
"is_main": false
}
// 响应 201
{
"id": "string",
"user_id": "string",
"title": "string",
"is_main": false,
"created_at": 1717000000000,
"updated_at": 1717000000000
}
```
### GET /sessions — 列表
`?user_id=xxx` — admin 可查任意用户;非 admin 只能查自己。
```json
// 响应 200
{ "sessions": [ Session, ... ] }
```
### GET /sessions/:id — 详情
同 Session 结构。
错误: 404 `{"error":"会话不存在","errorType":"session_not_found","hint":"该会话可能已被删除或尚未创建"}`
### DELETE /sessions/:id — 删除
`{"status":"deleted"}`
### DELETE /sessions — 清空全部
`?user_id=xxx``{"status":"deleted"}`
### GET /sessions/:id/messages — 消息
`?limit=50&offset=0`
```json
// 响应 200
{
"messages": [
{
"id": 1,
"session_id": "string",
"role": "user|assistant|system|action",
"msg_type": "chat|action|markdown|code",
"content": "string",
"created_at": 1717000000000
}
]
}
```
### DELETE /sessions/:id/messages — 清空服务器本地所有消息
`{"status":"cleared"}`
### GET /sessions/:id/export — 导出
`?format=json|markdown|txt(默认 json`
返回文件下载。JSON 导出格式:
```json
{
"session": { "id", "title", "created_at", "updated_at" },
"messages": [ { "role", "content", "created_at" } ]
}
```
---
## 4. 消息搜索
### GET /messages/search — 全文搜索
`?q=<keyword>&user_id=xxx&limit=50&offset=0`
```json
// 响应 200
{
"results": [
{
"message_id": "string",
"session_id": "string",
"session_title": "string",
"role": "string",
"content": "string",
"created_at": 1717000000000
}
],
"total": 10,
"query": "keyword",
"limit": 50,
"offset": 0
}
```
---
## 5. 语音 STT / TTS
### POST /voice/transcribe — 语音转文字
Content-Type: `multipart/form-data`。字段 `audio` (file)。最大 10MB。接受 PCM/WAV/MP3/OGG/FLAC/WebM 等格式。
```json
// 响应 200
{ "success": true, "text": "识别结果文本" }
// 错误
{ "success": false, "error": "语音识别失败: ..." }
```
### POST /voice/tts — 文字转语音
Content-Type: `application/json`
代理到 Voice-Service `POST /api/v1/tts/synthesize`
### GET /voice/tts/voices — 发音人列表
代理到 Voice-Service `GET /api/v1/tts/voices`
### GET /voice/tts/status — TTS 状态
### GET /voice/status — 语音服务完整状态 (STT + TTS)
Voice-Service 不可达时返回 502
```json
{"error":"Voice-Service 不可达: ...","errorType":"voice_service_unreachable"}
```
---
## 6. 文件管理
### POST /files/upload — 上传
Content-Type: `multipart/form-data`。字段 `file`。最大 20MB。
允许 MIME 类型:`image/jpeg,png,gif,webp,svg+xml` | `application/pdf,msword,vnd.openxmlformats-officedocument.wordprocessingml.document` | `text/plain,markdown` | `audio/mpeg,wav,ogg,webm` | `video/mp4,webm`
```json
// 响应 201
{
"id": "uuid",
"filename": "string (脱敏后)",
"mime_type": "string",
"size": 1024,
"url": "/api/v1/files/<id>/download"
}
```
错误: 400 `{"error":"文件大小超过限制 (最大 20MB)","errorType":"file_too_large"}`, 400 `{"error":"不支持的文件类型: ...","errorType":"unsupported_type"}`
> **文件在 AI 对话中的传递链路(推荐新流程)**:
> 1. **客户端**先调用 [`POST /api/v1/files/upload`](#post-filesupload) 上传图片,获得 `file_id` 和 `thumbnail_url`
> 2. **客户端**发送消息时,`attachments` 中携带 `file_id`(轻量引用,不再内嵌 base64)
> 3. **Gateway** 收到 `file_id` 后,从 `FileStore` 解析为本地下载 URL`http://127.0.0.1:{port}/api/v1/files/{id}/download`
> 4. **AI-Core** 下载该 URL 并转为 base64 data URL,以多模态格式传给外部 LLM API
>
> **向后兼容**`attachments[].url` 仍支持 base64 data URI或相对路径,Gateway 会将相对路径补全为绝对 URL。
### GET /files — 列表
`?page=1&limit=20`
```json
// 响应 200
{
"files": [
{
"id": "uuid", "user_id": "string", "filename": "string",
"mime_type": "string", "size": 1024, "hash": "sha256hex",
"is_public": false, "created_at": 1717000000000,
"url": "string", "thumbnail_url": "string (仅图片)"
}
],
"total": 100, "page": 1, "limit": 20
}
```
### GET /files/:id — 元数据
返回单个 File 对象。
### GET /files/:id/download — 下载
文件流下载。404 → `{"error":"文件实体不存在(可能已被清理)","errorType":"file_missing"}`
### GET /files/:id/thumbnail — 缩略图
图片:JPEG 缩略图 (max 300x300)。非图片:SVG 占位图标。`Cache-Control: public, max-age=86400`
### DELETE /files/:id — 删除
`{"status":"deleted"}`
---
## 7. 图片分析
### POST /images/analyze — 分析
两种方式:
- **JSON:** `{"file_id": "uuid"}` — 分析已上传文件
- **Multipart:** 字段 `file``image` — 直接上传分析 (max 10MB),支持 JPEG/PNG/GIF
```json
// 响应 200
{
"format": "jpeg",
"width": 1920,
"height": 1080,
"file_size": 204800,
"description": "AI 生成的图片描述",
"top_colors": [ { "hex": "#4A90D9", "percent": 35.2 } ],
"exif": { "Make": "Canon" },
"analyzed_by": "openai_vision|local"
}
```
### GET /images/analyze/:file_id — 按已上传文件分析
同上。
---
## 8. 知识库
### POST /knowledge/bases — 创建知识库
```json
// 请求
{
"name": "string (必填)",
"description": "string"
}
// 响应 201
{
"id": "uuid", "user_id": "string", "name": "string",
"description": "string", "created_at": "...", "updated_at": "...", "doc_count": 0
}
```
### GET /knowledge/bases — 列表
```json
{ "knowledge_bases": [ KnowledgeBase ], "total": 10 }
```
### GET /knowledge/bases/:id — 详情(含文档列表)
```json
{ "knowledge_base": KnowledgeBase, "documents": [ KnowledgeDocument ] }
```
### PUT /knowledge/bases/:id — 更新
```json
{ "name": "string (必填)", "description": "string" }
{ "status": "updated" }
```
### DELETE /knowledge/bases/:id — 删除
`{"status":"deleted"}`
---
### POST /knowledge/bases/:id/documents — 添加文档
```json
// 请求
{
"title": "string (必填)",
"content": "string (text 类型时)",
"source_type": "text|file|url (默认 text)",
"file_id": "string (file 类型必填)"
}
// 响应 201
{
"id": "uuid", "kb_id": "string", "user_id": "string",
"title": "string", "source_type": "string", "source_ref": "string",
"content_type": "string", "raw_content": "string",
"chunk_count": 5, "created_at": "...", "updated_at": "..."
}
```
注意:`source_type=file` 时 gateway 读取文件内容(仅支持文本类型和 `application/json`)。
### GET /knowledge/bases/:id/documents — 文档列表
`{"documents": [ KnowledgeDocument ], "total": 10}`
### GET /knowledge/documents/:id — 文档详情(含分块)
```json
{
"document": KnowledgeDocument,
"chunks": [ KnowledgeChunk ]
}
```
### DELETE /knowledge/documents/:id — 删除
`{"status":"deleted"}`
---
### POST /knowledge/search — 语义搜索
```json
// 请求
{
"query": "string (必填)",
"kb_ids": ["uuid"], // 可选,空=搜索全部
"limit": 5 // 1-50
}
// 响应 200
{
"chunks": [ SearchChunkResult ],
"total": 5,
"query": "string"
}
```
---
## 9. 记忆管理
代理到 Memory-Service。
### GET /memory/search — 搜索
`?q=<keyword>&user_id=xxx` → 代理到 Memory-Service `POST /memories/query`
### GET /memory — 列表
`?user_id=xxx` → 代理到 Memory-Service `GET /memories`
### POST /memory — 添加
```json
{
"content": "string (必填)",
"category": "string (默认 other)",
"priority": 1
}
```
### DELETE /memory — 删除
`?id=<memory_id>`
Memory-Service 不可达时返回 502
```json
{"error":"Memory-Service 不可达: ...","errorType":"memory_service_unreachable"}
```
---
## 10. 自动化规则与场景
### 规则
#### GET /automation/rules — 列表
```json
{
"rules": [
{
"id": "32位hex", "user_id": "string", "name": "string",
"description": "string", "trigger_type": "string",
"trigger_config": {}, "conditions": {}, "actions": {},
"enabled": true, "created_at": "..."
}
],
"count": 5
}
```
#### POST /automation/rules — 创建
```json
// 请求
{
"name": "string (必填)",
"description": "string",
"trigger_type": "string (必填)",
"trigger_config": {},
"conditions": {},
"actions": {} (),
"enabled": true
}
// 响应 201 → {"success": true, "rule": Rule}
```
#### GET /automation/rules/:id — 详情
`{"rule": Rule}` | 404 → `{"error":"规则不存在"}`
#### PUT /automation/rules/:id — 更新
所有字段可选(partial update)。`{"success": true, "rule": Rule}`
#### DELETE /automation/rules/:id — 删除
`{"success": true}`
#### POST /automation/rules/:id/trigger — 手动触发
`{"success": true, "message": "规则已触发"}`
---
### 场景
#### GET /automation/scenes — 列表
`{"scenes": [ Scene ], "count": 5}`
#### POST /automation/scenes — 创建
```json
{
"name": "string (必填)",
"icon": "string",
"rule_ids": ["rule_id_1", "rule_id_2"]
}
{"success": true, "scene": Scene}
```
#### GET /automation/scenes/:id — 详情
`{"scene": Scene}`
#### PUT /automation/scenes/:id — 更新
所有字段可选。`{"success": true, "scene": Scene}`
#### DELETE /automation/scenes/:id — 删除
`{"success": true}`
#### POST /automation/scenes/:id/execute — 执行
`{"success": true, "message": "场景已执行"}`
---
## 11. 通知推送
### POST /notifications/push — 推送通知
```json
// 请求
{
"user_id": "string (必填)",
"type": "info|warning|success|thinking|reminder (必填)",
"title": "string (必填)",
"body": "string (必填)",
"data": {}
}
// 响应 200
{
"success": true,
"notification": {
"id": "uuid",
"type": "info",
"title": "...",
"user_id": "...",
"timestamp": "2024-01-01T12:00:00Z",
"delivered": true
}
}
```
delivered = true 表示目标用户有活跃 WebSocket 连接。
---
## 12. 提醒管理
### GET /reminders — 列表
`?user_id=xxx&status=pending|completed|cancelled&limit=50&offset=0`
```json
// 响应 200
{
"reminders": [
{
"id": "string", "user_id": "string", "title": "string",
"description": "string", "remind_at": "2024-01-01T15:00:00Z",
"status": "pending",
"repeat_type": "none|daily|weekly|monthly",
"session_id": "string", "notified": false,
"created_at": "...", "completed_at": null
}
],
"count": 5
}
```
### POST /reminders — 创建
```json
// 请求
{
"title": "string (必填)",
"description": "string",
"remind_at": "2024-01-01T15:00:00Z (必填, ISO 8601)",
"repeat_type": "none|daily|weekly|monthly (默认 none)",
"session_id": "string"
}
// 响应 201
{ "success": true, "reminder": Reminder }
```
错误: 400 `{"error":"时间格式无效,请使用 ISO 8601 格式 (例如 2024-01-01T15:00:00Z)"}`
### PUT /reminders/:id — 更新
所有字段可选 (partial update)。设置 `status=completed|cancelled` 会自动写入 `completed_at`
`{"success":true, "reminder":Reminder}` | 404 `{"error":"提醒不存在"}`
### DELETE /reminders/:id — 删除
`{"success":true}`
---
## 13. Webhook 第三方接入
Auth: `X-Webhook-Key` header。
### POST /webhook/generic — 通用 Webhook
```json
// 请求
{
"message": "string (必填)",
"user_id": "string (默认 webhook_generic,自动加 ext_ 前缀)",
"session_id": "string (默认 webhook_ + 12 随机字符)",
"mode": "text|voice_msg (默认 text)",
"platform": "string (默认 generic)"
}
// 响应 200
{
"reply": "AI 回复文本",
"session_id": "string",
"message_id": "string",
"finish_reason": "stop"
}
```
错误: 400 `{"error":"消息不能为空"}`, 502 `{"error":"AI 服务暂不可用: ..."}`
### POST /webhook/discord — Discord 交互
接收标准 Discord Interaction
- `type=1` (PING) → `{"type":1}`
- `type=2` (APPLICATION_COMMAND) → 处理 `/chat message:` 命令
```json
// 响应 200
{ "type": 4, "data": { "content": "AI 回复" } }
```
---
## 14. Admin 管理
需要 JWT + admin 权限(`user_id == "admin"`)。非 admin 返回 **403**
### 会话管理
#### GET /admin/sessions — 活跃会话列表
```json
{ "sessions": [ SessionState ], "total": 5 }
```
#### GET /admin/sessions/active — 按用户分组
```json
{ "users": { "user_id": [ SessionState ] } }
```
#### GET /admin/sessions/:id — 指定会话详情
404 → `{"error":"会话不存在","errorType":"session_not_found"}`
---
### 客户端管理
#### GET /admin/clients — 已知客户端
`?user_id=admin`
```json
{
"clients": [
{
"client_id": "string", "device_name": "string",
"user_agent": "string", "note": "string",
"last_seen": 1717000000000, "online": true
}
],
"total": 3
}
```
#### PUT /admin/clients/:id/note — 设置备注
```json
{ "note": "string" }
{ "status": "ok", "client_id": "string", "note": "string" }
```
404 → `{"error":"客户端未找到"}`
---
### 模型配置管理
#### GET /admin/models/providers — Provider 列表
```json
{
"providers": [
{ "name": "string", "base_url": "string", "api_key": "string" }
],
"total": 2
}
```
#### POST /admin/models/providers/:name — 创建/更新 Provider
```json
{ "name": "...", "base_url": "...", "api_key": "..." }
{ "status": "saved", "name": "string" }
```
#### DELETE /admin/models/providers/:name — 删除
`{"status":"deleted","name":"string"}`
#### GET /admin/models/models — Model 列表
```json
{
"models": [
{
"id": "string", "name": "string", "provider": "string",
"description": "string", "priority": 1, "tags": [],
"params": { "temperature": 0.7 }, "enabled": true,
"updated_at": "..."
}
],
"total": 5
}
```
#### POST /admin/models/models/:id — 创建/更新 Model
```json
{ "id": "...", "name": "...", "provider": "...", "params": {}, ... }
{ "status": "saved", "id": "string" }
```
#### DELETE /admin/models/models/:id — 删除
`{"status":"deleted","id":"string"}`
#### GET /admin/models/routing — 路由规则列表
#### POST /admin/models/routing/:purpose — 创建/更新路由规则
```json
{ "purpose": "...", "fallback_chain": ["model1", "model2"], "required": true }
{ "status": "saved", "purpose": "string" }
```
#### DELETE /admin/models/routing/:purpose — 删除
#### POST /admin/models/health-check — 测试 Provider 连接
```json
{ "provider": "string (必填)" }
{ "provider": "string", "message": "Provider 配置已保存,连接测试请通过实际 LLM 调用验证" }
```
#### GET /admin/models/fetch-models/:name — 代理获取远程模型列表
`?url=<provider_models_api_url>` → 返回该 Provider 支持的模型列表。
---
## 15. 健康检查
### GET /health — 健康检查
Auth: 无。
```json
{
"status": "ok",
"service": "cyrene-gateway",
"ws_connections": 3
}
```
---
## 附录:错误格式
所有 API 错误统一为:
```json
{
"error": "错误描述",
"errorType": "error_code (可选,客户端可据此判断)",
"hint": "解决建议 (可选)"
}
```
### errorType 列表
| errorType | 含义 |
|-----------|------|
| `session_not_found` | 会话不存在 |
| `file_too_large` | 文件超过大小限制 |
| `unsupported_type` | 不支持的 MIME 类型 |
| `file_missing` | 文件实体已清理 |
| `memory_service_unreachable` | Memory-Service 不可达 |
| `voice_service_unreachable` | Voice-Service 不可达 |
| `admin_only` | 仅限管理员操作 |
### HTTP 状态码
| 状态码 | 场景 |
|--------|------|
| 200 | 成功 |
| 201 | 创建成功 |
| 400 | 请求参数错误 |
| 401 | 未认证 / Token 无效 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 409 | 资源冲突 (如用户名已存在) |
| 413 | 请求体过大 |
| 502 | 下游服务不可达 |