内容
# <img src="https://raw.githubusercontent.com/xsip/pure-liquid-local-ai-client/refs/heads/main/apps/ui/public/logo-cropped.png" alt="Logo" width="30"/> 纯液体本地 AI 客户端 | [在 YouTube 上预览](https://www.youtube.com/watch?v=_UhKke10JzY)
一个全栈 AI 聊天客户端,连接到任何兼容 OpenAI 的本地推理服务器(LM Studio、Ollama、llama.cpp、vLLM 等),通过标准的 `/v1/chat/completions` 端点。 使用 Angular、NestJS 和 MongoDB 构建,支持一流的 MCP(模型上下文协议)工具,支持 AI 图像生成(通过 [InvokeAI](https://invoke-ai.github.io/InvokeAI/)),支持图像上传到聊天中,支持语音消息录制(作为 `input_audio` 发送给支持音频的模型),以及可选的端到端 AES 消息加密。
> **⚠️ 破坏性变更:** LM Studio 的原生 `/api/v1/chat` API 和兼容 OpenAI 的 `/v1/responses/create`(响应 API)端点已被**移除** — 支持这些端点的模块、路由和 UI 代码已不再此存储库中。有关原因和替代方案,请参见下面的 [聊天完成 API(当前默认)](#聊天完成-api当前默认)。
---

---
## 目录
- [概述](#概述)
- [聊天完成 API(当前默认)](#聊天完成-api当前默认)
- [弹性后台生成](#弹性后台生成)
- [架构](#架构)
- [功能](#功能)
- [技术栈](#技术栈)
- [项目结构](#项目结构)
- [先决条件](#先决条件)
- [环境变量](#环境变量)
- [开始使用](#开始使用)
- [MCP 工具集成](#mcp-工具集成)
- [自定义 MCP 服务器](#自定义-mcp-服务器)
- [工具审批](#工具审批)
- [自定义 MCP 进度报告](#自定义-mcp-进度报告)
- [图像生成(InvokeAI)](#图像生成invokeai)
- [图像上传](#图像上传)
- [语音输入](#语音输入)
- [语音转录](#语音转录)
- [消息加密](#消息加密)
- [身份验证和授权](#身份验证和授权)
- [令牌使用和速率限制](#令牌使用和速率限制)
- [管理员 CMS](#管理员-cms)
- [API 概述](#api-概述)
---
## 概述
该 monorepo 托管两个应用程序:
| 应用 | 位置 | 描述 |
|-----|----------|-------------|
| `api` | `apps/api` | NestJS REST 后端,MCP 服务器/客户端和聊天完成代理 |
| `ui` | `apps/ui` | Angular 21 单页聊天界面 |
后端作为 Angular 前端和本地推理服务器之间的身份验证代理。所有聊天会话都存储在 MongoDB 中,每个用户的令牌使用情况都受到跟踪,后端运行自己的 **MCP 客户端**,按需调用工具 — 而不是依赖推理服务器自己编排工具调用。MCP 工具集包括一个图像生成工具,该工具连接到本地 InvokeAI 实例,上传的图像存储在 MongoDB 中作为二进制大对象,并通过 API 提供。
---
## 聊天完成 API(当前默认)
LM Studio 最近禁用了连接到本地 MCP 服务器,这破坏了该项目的原始架构:响应 API(`/v1/responses/create`) 通过给予 LM Studio 一个指向此后端 MCP 服务器的 `type: 'mcp'` 工具来工作,并依赖 LM Studio 自己连接、列出工具、调用它们,并将结果反馈给模型。 由于每个请求的本地 MCP 连接被禁用,该流程不再工作 — 而且它从来都不是可移植到其他后端,因为 `type: 'mcp'` 工具直通是响应 API 独有的便利功能,几乎没有其他兼容 OpenAI 的服务器实现。
解决方法是完全停止依赖推理服务器进行 MCP 编排,而是自己完成:
- 后端现在运行自己的 MCP 客户端(`apps/api/src/modules/mcp-client`),直接连接到 MCP 工具服务器,列出可用工具,并将它们转换为普通的 OpenAI **函数工具** 定义。
- 聊天请求通过标准的 **聊天完成 API**(`/v1/chat/completions`)发送,并附带这些函数工具 — 不涉及 `type: 'mcp'` 直通。
- 当模型返回 `tool_calls` 响应时,后端通过 MCP 客户端执行调用,将结果作为 `tool` 角色消息附加,并循环返回模型,直到产生最终答案。
因为这只需要标准的 OpenAI 函数调用支持,所以它适用于 **任何** 兼容 OpenAI 的后端 — LM Studio、Ollama、llama.cpp、vLLM 等 — 而不仅仅是 LM Studio。
**因此:**
- LM Studio 的原生 `/api/v1/chat` API 和其 NestJS 代理模块已从该项目中**移除**,转而使用兼容 OpenAI 的 `/v1/chat/completions` 路由,这是与本地推理服务器通信的更广泛标准化的方式。
- OpenAI 响应 API(`/v1/responses/create`)及其所有后端/UI 代码已**移除**,因为它完全依赖于现在已损坏的 `type: 'mcp'` 直通。
- **聊天完成(`/v1/chat/completions`)是唯一受支持的聊天路径。** 在旧响应/本机 LM Studio 流程下创建的任何聊天会话都不能通过 UI 读取 — 只有滚动聊天完成消息数组格式在未来受支持。
聊天完成路径的一个已知限制:文件附件仅限于纯文本友好(图像内联为视觉内容;其他文件类型通过 ID 引用并按需通过 `get-content-from-file-ids` 工具获取)— 支持推理努力和 AI 决定的聊天命名,与旧响应 API 体验匹配。
---
## 弹性后台生成
聊天生成不再与启动它的 HTTP 连接绑定。刷新页面、关闭选项卡或在 AI 仍在响应时切换到另一个聊天,不会丢失或损坏任何内容 — 后端在后台继续生成,前端可以重新连接它。
### 工作原理
1. **聊天 ID 立即发送给客户端** — 在任何模型输出开始之前创建 `ChatMetadata`(并决定其名称),并立即触发 `created_chat` SSE 事件,以便浏览器在第一个令牌到达之前更新其 URL。之后刷新页面会重新打开同一个聊天,而不是丢失跟踪并创建重复的聊天。
2. **写入不会中止生成** — `OpenAiService.safeWrite()` 抑制来自死客户端套接字的写入错误,而不是抛出错误,因此断开连接不会缩短工具调用/完成循环。交换仅在服务器端实际完成(或抛出)时解锁并视为完成 — 在 `finally` 块中 — 而不是在 `res.on('close')` 上。
3. **进行中的生成按聊天跟踪**(`ActiveGenerationService`)— 发送的每个 SSE 块都缓冲并广播,按 `internalChatId` 键入。由于当前回合的用户消息尚未存储在持久历史记录中(仅在整个交换完成时才会发生),因此通过 `user_message_echo` 事件将其回声到缓冲区中,以便重新连接的客户端可以渲染它。
4. **`GET /openai/completions-stream/resume?internalChatId=`** 允许客户端重新连接 — 它重放到目前为止缓冲的所有内容,然后流式传输实时块,直到生成完成。如果该聊天没有进行中的生成,它将立即结束而不会有任何数据。
5. **前端自动恢复** — 如果聊天的元数据在打开时返回 `locked: true`(最常见的情况:页面在响应中刷新),`ChatCompletionsService.resumeStreaming()` 重新连接并继续渲染,就像新提交的消息一样,包括工具调用横幅和推理。共享聊天会轻度轮询(每 3 秒),以便所有者在开始新生成时注意到并以相同的方式连接。
6. **不同的“生成中”状态** 避免误导性消息:观看他人的(或您自己的恢复)生成会显示 *"AI 正在生成响应…"*,而不是旧的 *"锁定 — 其他人正在生成响应"* 文本,后者保留用于轮询实际附加到流的短窗口。
7. **聊天侧边栏标记进行中的聊天**,带有细微的动画波浪背景和行上的脉动点,由相同的 `locked` 标志和 5 秒自我修复轮询驱动,该轮询可以捕获在您导航离开后完成的生成(因此指示器不会卡住)。
### 恢复 API
| 方法 | 路径 | 描述 |
|--------|------|--------------|
| `GET` | `/openai/completions-stream/resume?internalChatId=` | 重新连接飞行中的生成为 SSE — 重放缓冲的块,然后流式传输实时块 |
---
## 架构
```
┌──────────────────────────────────────────────────────────┐
│ Angular UI (端口 4200) │
│ OpenAI 聊天完成路由 │
└────────────────────────────┬──────────────────────────────┘
│ SSE 流
┌────────────────────────────▼──────────────────────────────┐
│ NestJS API (端口 8888) │
│ │
│ ┌──────────────┐ ┌────────────────┐ │
│ │ OpenAI │ │ 身份验证 / JWT │ │
│ │ 模块 │ │ 模块 │ │
│ └──────┬───────┘ └────────────────┘ │
│ │ 工具调用循环 │
│ ┌──────▼───────┐ ┌────────────────┐ ┌────────────────┐│
│ │ McpClient │ │ 聊天 / │ │ MCP 服务器 ││
│ │ 模块 │ │ 聊天元数据 │ │ (@rekog/mcp- ││
│ │ (MCP 客户端)│◄─┤ MongoDB │ │ nest) ││
│ └──────┬───────┘ │ 持久化 │ └────────┬───────┘│
│ │ └────────────────┘ ▲ │
│ └──────────── 连接为 MCP 客户端 ─────┘ │
│ ┌─────────────────────────────────────────┐ │
│ │ 资产模块 │ │
│ │ MongoDB 中存储的图像 blob │ │
│ └─────────────────────────────────────────┘ │
└────────────────────────────┬──────────────────────────────┘
│ /v1/chat/completions
┌────────────────────────────▼──────────────────────────────┐
│ 任何兼容 OpenAI 的推理服务器 │
│ LM Studio │ Ollama │ llama.cpp │ vLLM │ ... │
└──────────────────────────────────────────────────────────┘
│ 生成图像工具触发
┌──────────────────────────▼───────────────────────────────┐
│ InvokeAI (端口 9090) │
│ /api/v2/models │ /api/v1/queue/default/enqueue │
│ Socket.IO (/ws/socket.io/) │
└──────────────────────────────────────────────────────────┘
```
与旧的响应 API 流程不同 — 其中 LM Studio 自己连接到 MCP 服务器在推理过程中 — NestJS 后端现在充当 MCP 客户端:它列出自己的 MCP 服务器工具,将它们作为普通的函数工具附加到聊天完成请求中,并在模型返回任何 `tool_calls` 时执行它们,然后循环返回模型。推理服务器从不直接与 MCP 通信,因此这适用于支持标准 OpenAI 函数调用的任何后端。
---
## 功能
- **兼容 OpenAI 的聊天完成** — 与实现 `/v1/chat/completions` 的任何后端通信(LM Studio、Ollama、llama.cpp、vLLM、...);唯一受支持的聊天路径 — LM Studio 的原生 API 和 OpenAI 响应 API 已被移除(参见 [上文](#聊天完成-api当前默认))
- **客户端 MCP 编排** — 后端运行自己的 MCP 客户端,将 MCP 工具转换为 OpenAI 函数工具定义,并在循环中执行 `tool_calls` — 不依赖推理服务器自己的 MCP 支持
- **实时 SSE 流式传输** — 响应按令牌流式传输到浏览器,包括推理/“思考”增量(当模型提供时)
- **持久聊天历史记录** — 每个交换都存储在 MongoDB 中作为滚动消息数组,并在需要时重新加载,包括重建的工具调用横幅和图像附件
- **分支到新聊天** — 克隆任何聊天的设置(模型、MCP 工具、加密、Invoke 配置等)到一个新的聊天中,并带有历史记录,历史记录可追溯到您分支的助手回复 — 生成和上传的文件在历史记录中被引用,因此分支聊天可以在源聊天被删除后继续工作
- **弹性后台生成** — 响应在客户端断开连接后仍在服务器端生成;刷新页面或在响应过程中切换聊天会重新连接实时流,而不是丢失它(参见 [弹性后台生成](#弹性后台生成))
- **自定义 MCP 服务器** — 用户可以在其帐户上注册自己的 MCP 服务器(端点自动发现名称 + 工具列表),在帐户范围内切换服务器或单个工具,并按需重新发现;每个聊天覆盖允许特定聊天选择退出服务器/工具而不影响帐户默认值(参见 [自定义 MCP 服务器](#自定义-mcp-服务器))
- **工具审批** — 每个聊天选择加入,暂停工具调用进行审批,然后再运行 — 无需中止 SSE 流;审批一次,始终允许,或拒绝 — “始终允许”的决定按聊天存储,可以单独撤销(参见 [工具审批](#工具审批))
- **MCP 工具服务器** — 后端将自己注册为 MCP 服务器,也调用自己作为 MCP 客户端
- `get-token-usage-tool` — 返回经过身份验证的用户的当前令牌使用情况和限制
- `get-content-from-file-ids` — 按需获取上传的文件内容,文件 ID
- `generate-file-from-content-tool` / `generate-zip-from-file-ids` — 允许模型生成可下载的文件,并将它们捆绑到 ZIP 中
- `get-image-tool` — 从 URL 获取图像
- `decrypt-message-tool` — 在推理时解密 AES 加密的用户消息
- `greeting-tool` — 示例工具,具有进度报告
- `generate-image-tool` — 通过 InvokeAI 从自然语言提示生成图像,并将其注入聊天中作为持久资产
- **AI 图像生成** — 模型可以在推理过程中调用 `generate-image-tool`;后端将 txt2img 作业提交给 InvokeAI,下载结果,将其存储在 MongoDB 中,并返回聊天的 Markdown 图像引用
- **图像上传** — 用户可以在发送消息之前附加一个或多个图像;图像通过 Assets API 上传到 MongoDB,并作为视觉内容转发给模型
- **语音输入** — 专用麦克风模式将文本编辑器替换为录音面板(实时条形视觉器,播放,重录/删除),基于原始 Web Audio API;录音被编码为 WAV,并作为 `input_audio` 内容部分发送,带有自动系统提示模型将其视为用户消息(参见 [语音输入](#语音输入))
- **语音转录** — 每个聊天选择加入,通过单独的、未跟踪的 LLM 调用转录录制的语音消息,并用纯文本替换音频,然后运行主回合,因此工具调用、推理和令牌会计与键入的消息完全相同(参见 [语音转录](#语音转录))
- **端到端 AES 消息加密** — 每个聊天选择加入;消息在离开浏览器时使用 CryptoJS AES 加密,模型在推理时解密(参见 [消息加密](#消息加密))
- **JWT 身份验证** — 使用 bcrypt 哈希密码登录/注册;令牌在 1 小时后过期
- **基于角色的访问控制** — 通过 `RolesGuard` 实现 `User` 和 `Admin` 角色
- **基于订阅的令牌速率限制** — 可配置的令牌预算,每个订阅层级,自动重置间隔;层级不限于 `free`/`basic` — 可以动态创建新的层级(参见 [管理员 CMS](#管理员-cms))
- **管理员 CMS** — 基于角色的 `/admin` UI,用于管理用户(角色,订阅,激活,密码,令牌使用重置)和令牌限制配置(包括定义全新的订阅层级),使用 Angular 响应式表单构建
- **模型选择器** — 动态获取正在运行的 LM Studio 实例的可用模型
- **推理模式** — 将推理努力(`off` / `low` / `medium` / `high`)传递给支持的模型
- **Swagger UI** — 可选的 OpenAPI 文档(通过 `USE_SWAGGER=true` 启用)
- **Markdown 渲染** — 助手响应在聊天 UI 中渲染为格式化的 Markdown
---
## 技术栈
| 层 | 技术 |
|-------|-----------|
| 前端 | Angular 21,TailwindCSS 4,RxJS 7 |
| 后端 | NestJS 11,TypeScript 5.9 |
| 数据库 | MongoDB via Mongoose |
| MCP | `@rekog/mcp-nest`(可流式 HTTP + SSE 传输)|
| 图像生成 | InvokeAI(本地,通过 REST API + Socket.IO)|
| 图像存储 | MongoDB(`image_blobs` 集合,二进制 Buffer)|
| 文件上传 | `@nestjs/platform-express` / Multer(内存存储)|
| 加密 | `crypto-js`(AES)|
| 身份验证 | JWT (`@nestjs/jwt`),bcrypt |
| HTTP 客户端 | `@nestjs/axios` |
| OpenAI SDK | `openai` npm 包(指向 LM Studio 基本 URL)|
| API 文档 | Swagger / OpenAPI (`@nestjs/swagger`) |
## 项目结构
```
apps/
├── api/ # NestJS 后端
│ └── src/
│ ├── app.module.ts # 根模块 — MCP、Mongo、守卫
│ ├── main.ts # 启动引导、CORS、Swagger、body-parser
│ ├── tools/
│ │ └── api.tools.ts # MCP 服务器工具(token 使用量、文件生成/压缩、图像、解密等)
│ └── modules/
│ ├── auth/ # JWT 认证、守卫、用户模式
│ ├── admin/ # 仅限管理员的用户 CRUD + 订阅类型发现(角色控制)
│ ├── assets/ # 图像存储和检索(MongoDB)
│ ├── chats/ # 聊天消息持久化(滚动聊天完成消息数组)
│ ├── chat-metadata/ # 会话元数据(模型、加密配置、共享等)
│ ├── invoke/ # InvokeAI 集成(图像生成)
│ ├── mcp-client/ # MCP 客户端 — 连接到 MCP 服务器,执行聊天完成的 tool_calls
│ ├── openai/ # OpenAI 兼容聊天完成代理(仅支持聊天路径)
│ └── token-limit/ # token 预算跟踪和速率限制强制(管理员控制的 CRUD)
└── ui/ # Angular 前端
└── src/app/
├── app.ts # 根组件 — JWT 过期守卫
├── admin.guard.ts # 路由守卫 — 仅允许角色为 'admin' 的用户访问 /admin
├── client/ # 自动生成的 API 客户端 DTO
├── shared/
│ └── components/ # 跨路由 UI:聊天消息、聊天侧边栏、信息面板、mcp-config-dialog、markdown 管道等
└── routes/
├── login.ts # 登录/注册页面
├── admin.ts # 管理员 CMS — 用户和 token 限制配置管理(仅限管理员角色)
├── admin/
│ └── admin.service.ts # 手动编写的客户端,用于管理员专属后端端点
└── openai-api/ # OpenAI 聊天完成端点 UI(唯一聊天路由)
├── chat-input.component.ts # 包括图像/文件附件按钮
├── chat-completions.service.ts # 聊天完成聊天状态
├── completions-openai-stream.service.ts # SSE 客户端,用于聊天完成
├── openai-stream-events.model.ts # 共享 SSE 事件类型定义
└── model-selector.component.ts
```
---
## 先决条件
- **Node.js** 18+
- **MongoDB** 在本地运行(默认为 `mongodb://localhost:27017/lmStudioWrapper`)或远程 URI
- **兼容 OpenAI 的推理服务器** 在本地运行,其 `/v1` API 已启用(LM Studio、Ollama、llama.cpp、vLLM 等)— 默认假设在 `http://localhost:1234`
- 加载的模型支持 MCP 功能的工具/函数调用
- **InvokeAI** (可选)— 仅需用于 AI 图像生成;默认:`http://127.0.0.1:9090`
---
## 环境变量
在 `apps/api/` 中创建 `.env` 文件(或在 shell 中设置变量):
```env
# MongoDB 连接 URI
MONGODB_URI=mongodb://localhost:27017/lmStudioWrapper
# 兼容 OpenAI 的推理服务器基准 URL(LM Studio、Ollama、llama.cpp、vLLM 等)
LM_STUDIO_BASE_URL=http://localhost:1234
LM_STUDIO_API_TOKEN= # 可选 — 如果后端需要令牌,请设置
# JWT 签名密钥 — 在生产环境中使用长随机字符串
JWT_SECRET=your-very-secret-key
# 后端 MCP 客户端连接到的 URL,以便调用 MCP 工具。
# 这是后端调用自身 — 可以保留为本机的 localhost/LAN IP
SELF_MCP_URL=http://192.168.0.34:8888/tools/mcp
# 要连接的其他外部 MCP 服务器,逗号分隔(可选)
# MCP_SERVER_URLS=http://example.com/mcp,http://another-host:9000/mcp
# 本后端公共基准 URL — 用于构造由 generate-image-tool 返回的资产 URL
# 必须能从浏览器访问(例如 http://localhost:8888 或您的 LAN IP)
SELF_URL=http://localhost:8888
# 后端 HTTP 端口(默认:8888)
PORT=8888
# 设置为非空值以在 /api 启用 Swagger UI
USE_SWAGGER=true
```
> **注意 `SELF_MCP_URL`:** 由于后端的 `McpClientService` 现在是调用 MCP 工具的对象(参见 [聊天完成 API](#聊天完成-api-当前默认)),因此仅需从后端进程本身可访问 — 不再需要从 LM Studio 可访问。
> **注意 `SELF_URL`:** 由 `generate-image-tool` 用于构建返回给聊天的公共图像 URL。将其设置为浏览器访问 NestJS API 的地址。如果省略,资产链接将损坏。
> **注意 InvokeAI 基准 URL:** InvokeAI 基准 URL 目前在 `app.module.ts` 中硬编码为 `http://127.0.0.1:9090`(`InvokeModule.forRoot(...)`)。如果您的 InvokeAI 实例运行在其他地方,请直接更改此值。
---
## 入门指南
### 1. 安装依赖
```bash
# 从 monorepo 根目录
npm install
```
### 2. 启动 OpenAI 服务器(LM Studio、Ollama、llama.cpp、vLLM 等)
```bash
# 使用 llama.cpp 示例
llama-server.exe -m "gemma-4-E4B-it-Q4_K_M.gguf" --mmproj "mmproj-gemma-4-E4B-it-BF16.gguf" --host 0.0.0.0 --port 1234 -ngl 999 -c 8192 -ub 1024 -b 1024
```
### 3. 启动 API
```bash
nx serve api
```
API 将在 `http://localhost:8888` 提供服务。
如果设置了 `USE_SWAGGER=true`,Swagger UI 将在 `http://localhost:8888/api` 提供服务。
### 4. 启动 UI
```bash
nx serve ui
```
UI 将在 `http://localhost:4200` 提供服务。
### 5. 同时启动两者
```bash
npm start
# 运行:nx run-many --target=serve --projects=api,ui
```
### 6. 注册用户
使用 Swagger UI 或发送 `POST /auth/register` 请求:
```bash
curl -X POST http://localhost:8888/auth/register \
-H "Content-Type: application/json" \
-d '{"username": "alice", "password": "s3cret"}'
```
然后登录到 `POST /auth/login` 以接收 JWT。
---
## MCP 工具集成

NestJS 后端同时扮演 **MCP 服务器** 和 **MCP 客户端** 的角色:
- **MCP 服务器**(`apps/api/src/tools/api.tools.ts`)— 通过 `@rekog/mcp-nest` 注册,在 `http://localhost:8888/tools/mcp` 提供可流式传输的 HTTP + SSE 传输。
- **MCP 客户端**(`apps/api/src/modules/mcp-client`)— 连接到同一个 MCP 服务器(和通过 `MCP_SERVER_URLS` 配置的任何其他服务器),列出其工具,并代表模型调用它们。
对于每个聊天完成请求,后端:
1. 调用 `McpClientService.listTools()` 以获取当前的工具列表,并将每个工具转换为普通的 OpenAI 函数工具定义。
2. 通过 `/v1/chat/completions` 发送聊天请求,并将这些工具附加到请求上。
3. 如果模型响应 `tool_calls`,则通过 `McpClientService.callTool()` 执行每个工具调用,将认证的用户 JWT 和当前 `chatId` 作为 MCP 请求头转发,以便工具可以完全访问用户的上下文。
4. 将工具结果作为 `tool` 角色消息附加,并循环返回到模型,直到模型生成最终答案(上限为 8 次迭代,作为安全保护)。
这意味着 MCP 工具适用于 **任何** OpenAI 兼容的后端 — 推理服务器本身不需要知道 MCP 的存在。
### 可用工具
| 工具 | 描述 |
|------|-------------|
| `get-token-usage-tool` | 返回用户的当前 token 消耗、订阅层、限制和下一个重置时间 |
| `get-content-from-file-ids` | 返回以前上传/生成的 base64 文件内容,按文件 ID |
| `generate-file-from-content-tool` | 从提供的内容生成可下载的文件,并将其存储为资产 |
| `generate-zip-from-file-ids` | 将多个以前生成的/上传的文件捆绑到一个可下载的 ZIP 存档中 |
| `get-image-tool` | 从 URL 获取图像并将其存储为资产 |
| `decrypt-message-tool` | 使用存储在 `chat_metadata` 中的每个聊天加密密钥解密 AES 加密的用户消息 |
| `greeting-tool` | 示例工具 — 返回带有流式进度更新的问候 |
| `generate-image-tool` | 通过 InvokeAI 从文本提示生成图像,将其存储在 MongoDB 中,并返回可渲染的聊天图像 URL |
要添加新工具,请在 `apps/api/src/tools/` 中创建一个 `@Injectable()` 类,用 `@Tool(...)` 装饰方法,从 `@rekog/mcp-nest` 注册类作为 `AppModule` 中的提供者,并将其名称添加到 `OpenAiService.chatStreamCompletions` 中的 `allowedTools` 列表中,以便模型实际提供它。
---
## 自定义 MCP 服务器

除了后端的内置 MCP 服务器/客户端外,每个用户都可以注册自己的 **外部 MCP 服务器**,并控制哪些工具可用于其帐户 — 全局或每个聊天。
### 工作原理
1. **注册服务器** — 从帐户信息面板,打开 **MCP 服务器** → 粘贴端点 URL。后端通过 `McpClientService.discoverServer()` 连接到它,读取其名称(`getServerVersion()`)和完整的工具列表(`listTools()`),并将其存储在用户文档(`User.customMcps`)中 — 默认情况下处于活动状态,并允许所有已发现的工具。
2. **切换服务器/工具** — 可以在同一对话框中切换每个注册服务器的开启/关闭状态,以及允许/拒绝个别工具。更改立即通过 `PATCH /auth/mcp-servers/:id` 持久化。
3. **刷新** — 点击服务器旁边的刷新按钮会重新运行发现(`POST /auth/mcp-servers/:id/refresh`):远程服务器上新添加的工具默认允许,消失的工具被删除,现有工具的允许/拒绝选择被保留。
4. **每个聊天覆盖** — 新聊天对话框和每个聊天的设置对话框允许您选择特定聊天退出服务器(或其工具的子集),而无需触及全局配置。这些覆盖存储在 `ChatMetadata.mcpOverrides` 中,仅在有偏差时需要 — 默认情况下具有覆盖的服务器简单地“启用,所有帐户允许的工具”。
5. **实时同步** — 管理对话框、聊天设置对话框和新建聊天对话框都读取/写入相同的帐户级列表,因此从其中任何一个添加、刷新或编辑服务器都会立即反映在其他对话框中,无需刷新页面。
6. **请求时间合并** — 对于每个聊天完成请求,`OpenAiService` 读取用户的活动 `customMcps`,应用当前聊天的任何 `mcpOverrides`,并针对每个服务器的端点(及其自定义头)调用 `McpClientService.listTools()`,将结果合并到内置工具集中。工具调用通过端点路由回正确的服务器。
### 数据模型
| 字段 | 位置 | 描述 |
|-------|----------|-------------|
| `User.customMcps[]` | `users` 集合 | `{ id, name, endpoint, active, availableTools, allowedTools, headers? }` — 每个注册服务器一个条目 |
| `ChatMetadata.mcpOverrides[]` | `chat_metadata` 集合 | `{ mcpId, active, allowedTools }` — 特定聊天的覆盖 — 仅当有偏差时需要 |
### MCP 服务器 API 路由
| 方法 | 路径 | 描述 |
|--------|------|--------------|
| `POST` | `/auth/mcp-servers` | 注册自定义 MCP 服务器(自动发现名称 + 工具) |
| `PATCH` | `/auth/mcp-servers/:id` | 切换服务器开启/关闭或编辑其允许的工具 |
| `POST` | `/auth/mcp-servers/:id/refresh` | 重新发现服务器的工具列表 |
| `DELETE` | `/auth/mcp-servers/:id` | 从帐户中删除服务器 |
---
## 工具审批

每个聊天可选的工具审批(`ChatMetadata.toolsRequireApproval`)在工具/MCP 调用被批准后再运行,而不是在模型请求时自动执行。
### 工作原理
1. **每个聊天开启** — 在新建聊天对话框或聊天的设置对话框中切换“需要工具审批”。存储为 `ChatMetadata.toolsRequireApproval` — 默认关闭。
2. **流暂停,而不是中止** — 当模型返回工具调用时,`OpenAiService.chatStreamCompletions` 发出 `response.tool_approval.required` SSE 事件,并等待来自 `ToolApprovalService` 的承诺,然后再调用 `McpClientService.callTool()`。SSE 连接保持打开和流式传输 — 没有中止任何操作。
3. **聊天输入上方的横幅** — `ToolApprovalBannerComponent` 在输入上方渲染待处理调用的名称和参数,带有三个操作:**允许一次**、**始终允许**、**拒绝**。
4. **决策后发送** — 选择的决策发送到 `POST /openai/tool-approval/:requestId`,后者解析匹配的待处理承诺 `ToolApprovalService`。**拒绝** 跳过 `callTool()` 并向模型提供 `"用户拒绝此工具调用"` 工具结果消息,而不是工具的实际输出 — 生成循环正常继续。
5. **始终允许在聊天中持久化** — 选择 **始终允许** 将工具名称添加到 `ChatMetadata.alwaysAllowedTools` MongoDB 中。后续对同一聊天中同一工具的调用跳过审批门槛(无 SSE 事件,无等待),而允许列表在服务器重启/重新部署时存活。
6. **在聊天设置中单独撤销** — 聊天设置对话框列出其 `alwaysAllowedTools` 列表中的每个工具作为芯片;单击一个工具发送 `PATCH /chat-metadata/:id`,删除该工具,重新启用对其的审批提示,而不会影响列表中的其他工具。

> **注意:** 由于 SSE 流和 `resumeStream`/`ActiveGenerationService` 缓冲(参见 [弹性后台生成](#弹性后台生成)),在待处理工具审批请求时刷新页面会重放 `response.tool_approval.required` 事件,横幅重新出现 — 后端仍在等待相同的承诺。
---
## 自定义 MCP 进度报告

MCP 规范具有标准 `notifications/progress` 机制,但 LM Studio 和 llama.cpp 都没有将其转发到浏览器 — 由于该项目自己的后端是 MCP 客户端(参见 [聊天完成 API](#聊天完成-api-当前默认)),因此没有将运行的 MCP 工具调用的进度传回聊天 UI 的传输。`ToolsHelperService`(`apps/api/src/tools/tools-helper.service.ts`)是一个自定义解决方法,使用与聊天响应流式传输的相同 SSE 连接填充该间隙。
### 工作原理
1. **工具报告进度** — `apps/api/src/tools/` 中的任何 `@Tool()` 方法都可以调用 `this.toolsHelperService.emitApiEvent(request, ApiEvent.MCP_PROGRESS, { progress, total, message })` 在其执行期间的任何时候(参见 `api.tools.ts` 中的 `greeting-tool` 示例)。
2. **按请求 ID 查找,而不是 MCP 进度令牌** — `emitApiEvent` 读取工具自己的传入请求的 `requestid` 头(由 `McpClientService` 作为 MCP 请求头转发),并使用它通过 `OpenAiResponseService.get(requestId)` 查找活动的 SSE `Response` 对象 — 与 `OpenAiService` 用于跟踪飞行中的生成的相同注册表。
3. **直接写入聊天 SSE 流** — 如果找到匹配的响应,它将事件直接写入 SSE 流:
```
event: api_report_mcp_progress
data: {"type":"api_report_mcp_progress","progressToken":"<requestId>","progress":"42","total":"100","message":"..."}
```
这与聊天完成块和 `response.mcp_call.*` 事件在同一连接上传输 — 无需单独的通道,无需额外的客户端连接。
4. **前端消费** — `OpenAiStreamService`(`apps/ui/src/app/routes/openai-api/completions-openai-stream.service.ts`)像任何其他 SSE 事件一样解析 `api_report_mcp_progress`,并将其转发到 `events$`。`ChatCompletionsService` 更新当前正在流式传输的 `tool_calls` 聊天气泡的 `progress` / `total` / `progressMessage` 字段,`chat-messages.component.ts` 在飞行中将这些字段渲染到工具调用横幅下。
> **为什么存在此功能:** 它不是 MCP 规范的进度通知流的一部分 — 它是一个定制的 SSE 侧通道,专门为该项目的推理服务器无关的 MCP 客户端架构而构建,没有其他方法将工具的进度更新实时传回浏览器。
---
## 图像生成(InvokeAI)

`generate-image-tool` MCP 工具允许语言模型在对话过程中按需生成图像。它需要本地运行 [InvokeAI](https://invoke-ai.github.io/InvokeAI/) 实例。
… (其他部分翻译略)
### 工作原理
1. **工具调用** — 模型调用 `generate-image-tool` 并附上自然语言 `提示` 字符串。
2. **模型查找** — `InvokeService` 查询 InvokeAI 的 `/api/v2/models/` 端点,并找到第一个名称包含请求模型名称的模型(默认为 `"Dreamshaper 8"`)。
3. **作业提交** — 构造一个 txt2img 管道图(512 × 512,30 步,`dpmpp_3m_k` 调度器,CFG 7.5)并通过 `POST /api/v1/queue/default/enqueue_batch` 提交到 InvokeAI 的队列中。
4. **Socket.IO 监听器** — 服务端打开一个 Socket.IO 连接到 InvokeAI(`/ws/socket.io/`)并订阅 `default` 队列。它等待一个包含生成图像名称的 `invocation_complete` 事件。
5. **下载和持久化** — 从 InvokeAI 的 `/api/v1/images/i/{name}/full` 端点下载生成的图像,并通过 `AssetsService` 存储为 MongoDB 中的二进制数据块。
6. **URL 构造** — 以 `{SELF_URL}/assets/filequery/{filename}?userId=...&chatId=...` 形式返回一个公共资产 URL 到模型中。模型在聊天中将其渲染为 Markdown 图像引用。
### InvokeAI 配置
InvokeAI 的基准 URL 默认为 `http://127.0.0.1:9090` 并在 `AppModule` 中设置:
```typescript
InvokeModule.forRoot('http://127.0.0.1:9090')
```
请更改此值以匹配您的 InvokeAI 安装。确保 InvokeAI 至少加载了一个 SD1.x 兼容的模型(例如 Dreamshaper 8)以使默认管道正常工作。
---
## 图片上传
用户可以在发送聊天消息之前将图片附加到消息上。上传的图片存储在 MongoDB 中,并作为视觉内容转发给模型。
### 工作原理
1. **前端** — 聊天输入组件(OpenAI API 路线)包含一个 **附件** 按钮,该按钮打开一个本地文件选择器。可以在单个消息中附加多个图片。附件文件在文本区域下方列出,显示文件名和大小;可以在发送之前单独删除文件。
2. **上传** — 发送时,每个附件图片都会以 `multipart/form-data` 的形式发送到 `POST /assets/:chatId`。后端验证 MIME 类型(JPEG、PNG、WebP、GIF、AVIF),将文件大小限制为 **10 MB**,并将原始二进制数据存储在 `image_blobs` MongoDB 集合中。
3. **检索** — 图片通过以下方式提供服务:
- `GET /assets/:chatId/:filename` — 身份验证端点(使用请求用户的 JWT)
- `GET /assets/filequery/:filename?chatId=...&userId=...` — 由 `generate-image-tool` 返回值使用的公共端点,以便浏览器可以在没有额外身份验证标头的情况下显示 AI 生成的图像
4. **聊天中渲染** — 聊天消息组件在用户的消息气泡中内联渲染上传的用户图片。AI 生成的图片以 Markdown 图片标签形式返回,由模型以 Markdown 管道渲染。
### 支持的格式
| 格式 | MIME 类型 |
|--------|-----------|
| JPEG | `image/jpeg`,`image/jpg` |
| PNG | `image/png` |
| WebP | `image/webp` |
| GIF | `image/gif` |
| AVIF | `image/avif` |
最大文件大小:**10 MB** 每文件。
---
## 语音输入

聊天输入旁边的模式切换按钮在输入和录音之间切换 - 默认情况下不需要单独的语音转文本步骤;推理服务器本身(llama.cpp 等)通过其自己的音频输入支持处理转录/理解。
### 工作原理
1. **切换模式** — 操作行中的麦克风/铅笔切换按钮(`apps/ui/src/app/routes/openai-api/chat-input.component.ts`)将 Markdown 编辑器替换为语音录音面板,并带有淡入/缩放过渡(Angular 动画);保存并在切换回时恢复输入的草稿。
2. **录音** — 点击面板中的麦克风按钮,通过 Web Audio API(`AudioContext` + `ScriptProcessorNode`,而非 `MediaRecorder`)捕获麦克风音频,并在停止时直接编码为 16 位 PCM **WAV** - `MediaRecorder` 的默认 webm/opus 输出无法被 llama.cpp 的音频输入解码,因此直接从原始 PCM 样本生成 WAV(`apps/ui/src/app/shared/utils/audio-recorder.utils.ts`)。
3. **实时可视化** — 在录音过程中,与录音处理器并行连接的 `AnalyserNode`(无需额外依赖)驱动一个实时条形可视化器,显示在 `<canvas>` 上,并在每个动画帧中根据 `getByteFrequencyData()` 重新绘制。
4. **发送前查看** — 停止后,面板显示录音内容,使用与聊天其他部分相同的自定义音频播放器气泡,带有 **重新录音** (丢弃并重新开始)和 **删除** (丢弃它并返回到空闲麦克风面板)控件。附加录音时仍然 **可选** 发送文本消息 - 可以仅发送音频,从任一模式发送。
5. **发送** — 提交时,录音内容进行 base64 编码,并作为 OpenAI Chat Completions 的 `input_audio` 内容部分发送,标记为 `userRecorded: true`,以便后端可以将其与其他音频来源区分开来:
```json
{ "type": "input_audio", "input_audio": { "data": "<base64 WAV>", "format": "wav" }, "userRecorded": true }
```
6. **系统注入** (关闭转录) — 每当请求的消息包含未转录的 `input_audio` 部分时,`OpenAiService` (`apps/api/src/modules/openai/openai.service.ts`)注入一个额外的系统消息,指示模型将录音内容视为用户的实际消息,与对输入文本的响应相同。
7. **播放** — 录制的语音消息以自定义音频播放器气泡(播放/暂停,可寻进的进度条,已用/总时间)渲染,匹配聊天 UI 的其余部分(`apps/ui/src/app/shared/components/audio-player.component.ts`),用于新发送的消息和重新加载聊天历史记录时。
> **需要支持音频理解的模型** (例如,具有音频能力的 llama.cpp 版本/模型),除非为聊天启用了 [语音转录](#voice-transcription)。
---
## 语音转录

每个聊天可选择开启(`ChatMetadata.transcribeAudio`),将录制的语音消息转换为普通的输入文本 **在模型看到聊天上下文中的音频之前** - 有助于从技术上接受 `input_audio` 但处理效果不佳的模型中获取更可靠的工具调用/推理。
### 工作原理
1. **每个聊天开启** — 在聊天设置对话框中切换“转录音频”(或在创建聊天时)。存储为 `ChatMetadata.transcribeAudio`;默认为关闭。
2. **仅限麦克风录音** — 后端仅转录标记为 `userRecorded: true` 的 `input_audio` 部分(即通过描述的麦克风面板捕获)。这将功能范围限定为用户实际所说的内容,而不是其他音频来源。
3. **单独的、未跟踪的转录调用** — 在主回合运行之前,`OpenAiService.transcribeUserRecordedAudio()` (`apps/api/src/modules/openai/openai.service.ts`)进行自己的 `stream: false` Chat Completions 调用:一个 `system` 消息,指示模型仅作为纯转录引擎(永不回答,永不根据内容采取行动),以及一个包含 **仅** 音频的用户回合 - 该回合中的其他内容为空,以防止模型误认为请求需要履行。 这与现有的“让 AI 决定聊天名称”调用类似,并且与之类似,**其令牌永不会添加到用户的用法计数器中**。
4. **原位替换** — 返回的转录文本用纯 `{ "type": "text", "text": "<transcript>" }` 部分替换 `input_audio` 内容部分,就在即将发送的主回合中。从这一点开始,该回合与用户输入的回合无区别 - 相同的工具调用路径,相同的推理,相同的历史记录持久性。
5. **实时 UI 更新** — 一旦转录完成,就会触发 `audio_transcript` SSE 事件,因此刚刚发送的音频气泡在屏幕上会立即切换到标记为 *“转录”* 的文本气泡,而无需等待响应其余部分。
6. **上传/剩余音频仍仅被聆听** - 任何未标记为 `userRecorded` 的 `input_audio` 部分(或聊天未启用转录)都会回落到 [语音输入](#voice-input) 的普通“聆听此音频”系统 - 此路径不会发生任何更改。
> **为什么使用单独的调用而不是组合的提示?** 早期版本尝试通过 JSON 信封系统将转录和回答合并到一个请求中,但小型本地模型经常会破坏工具调用,将 JSON 结构泄漏到聊天中,或直接回答音频请求而不是转录它。将转录分离到自己的未跟踪、仅限音频的调用中,可以避免这三种故障模式。
---
## 消息加密
每个聊天的 AES-256 消息加密可以在创建新的聊天会话时选择开启。目标是将明文消息内容从推理服务器自己的消息存储/日志中排除 - 只有密文会被转发到模型服务器。
### 工作原理
加密由两端使用 [CryptoJS](https://github.com/brix/crypto-js) (`crypto-js ^4.2`)提供,使用 CBC 模式的 AES 和每个聊天一个独立的密钥,该密钥仅存储在 MongoDB 中。
**端到端流程:**
1. **会话创建** - 当用户为新聊天启用加密时,会生成一个 `cryptoKey` 并存储在 `chat_metadata` 文档中,同时 `useCrypto: true`。密钥永远不会离开服务器。
2. **消息加密(后端)** - 在将每个请求转发到推理服务器之前,相关消息内容会被替换为 AES 密文:
```
CryptoJS.AES.encrypt(plaintext, cryptoKey).toString()
```
这意味着推理服务器只会接收和存储不透明的密文。
3. **系统注入** - 与加密消息一起,后端会注入一个系统指令,指示模型:
- **始终** 调用 `decrypt-message-tool`,传递完整的未经修改的用户消息
- 在接收工具响应后完全忽略原始加密输入
- 将解密的文本视为真实的用户消息并直接回答
- 在响应中从不提及解密步骤
4. **推理时 MCP 解密** - 模型通过 `decrypt-message-tool` 调用回 NestJS MCP 服务器。工具接收密文,根据 `chatId` 头部查找 `cryptoKey`,并将明文返回给模型:
```
CryptoJS.AES.decrypt(ciphertext, cryptoKey).toString(CryptoJS.enc.Utf8)
```
5. **透明响应** - 模型正常回答解密的问题。从用户的角度来看,对话流程与平常一样;加密/解密周期是不可见的。
### 密钥存储和安全边界
| 对象 | 存储位置 | 明文? |
|------|-------|-----------|
| `cryptoKey` | `chat_metadata` MongoDB 文档 | ✅ 是 - 将 MongoDB 视为受信任的边界 |
| 发送到 LM Studio 的消息 | LM Studio 消息存储 | ❌ 否 - 始终为密文 |
| 浏览器中的消息 → API 请求 | HTTP 正文到 NestJS | ✅ 是 - 生产环境使用 HTTPS |
| 转发到 MCP 工具的 `chatId` | `chatId` 请求头 | ✅ 是 - 仅用于密钥查找 |
> **安全注意:** 加密旨在保护消息内容不被 LM Studio 自己的对话存储读取。安全边界是您的 NestJS API + MongoDB - 如果这些被泄露,密钥可访问。请在任何非本地部署中使用 HTTPS。
---
## 身份验证和授权
- **注册** - `POST /auth/register` 创建一个具有 bcrypt 哈希密码的用户。新账户在激活链接使用前处于非活动状态。
- **登录** - `POST /auth/login` 返回一个签名 JWT(1 小时过期)。
- **守卫** - `JwtAuthGuard` 在全局范围内作为 `APP_GUARD` 应用。可以为 `@Public()` 装饰器选择退出。
- **角色** - `RolesGuard` 强制执行 `@Roles(Role.Admin)` / `@Roles(Role.User)` 装饰器。
- **令牌自动刷新** - Angular `App` 根组件在每次导航时检查 JWT 过期,并在过期时重定向到 `/login`。
---
## 令牌使用和速率限制
令牌消耗按用户跟踪,并在 `token_limit_configs` MongoDB 集合中配置的订阅层限制范围内强制执行。
| 字段 | 描述 |
|-------|-------------|
| `tokensPerInterval` | 在一个重置窗口内允许的最大令牌数 |
| `minutesTillReset` | 计数器重置前的时间(分钟) |
| `subscription` | 此配置适用的层级 - **自由格式字符串**,而非固定枚举。`free` 和 `basic` 只是内置默认值;创建具有任何其他名称的配置(例如 `pro`、`enterprise`)定义一个全新的层级 |
每次完成推理后,`TokenLimitService.updateUsedTokens()` 会增加用户的 `usedTokens` 计数器。如果达到限制,则会向客户端发出 `api.info` SSE 事件,并附上重置时间戳。计数器在 `tokenCountResetDate` 过期时自动重置。
令牌限制仅通过 [Admin CMS](#admin-cms) 的 `TokenLimitModule` 控制器(`/token-limit-configs`)进行管理,该控制器受到 `@Roles(Role.Admin)` 的保护。
---
## Admin CMS


一个角色门控的 `/admin` 路线(整个 Angular 反应式表单 - 没有 `ngModel`),用于管理和用户令牌限制配置,无需手动触摸 MongoDB。
### 访问权限
- 仅对具有 `role: 'admin'` 的用户可见/可访问。链接仅当登录用户是管理员时才出现在 `app-info` 面板(从聊天工具栏打开的侧出面板)中。
- 强制执行两次:`adminGuard`(Angular 路由守卫,检查 `GET /auth/me` 后才允许导航)和每个后端端点上的 `@Roles(Role.Admin)`。
### 用户管理
- 列出所有具有角色、订阅、激活状态和当前令牌使用情况的用户。
- 创建新用户(用户名、密码、角色、订阅、激活标志)- 跳过正常的注册/激活电子邮件流程,有助于预设管理员或测试帐户。
- 编辑现有用户:更改角色、订阅、激活状态或重置密码。创建后用户名不可更改。
- 需按需重置用户的令牌使用计数器(调用与自动过期流程相同的 `TokenLimitService.resetTokenLimit()`)。
- 删除用户(管理员不能删除自己的帐户,以避免意外锁定)。
### 令牌限制配置管理
- 列出、创建、编辑和删除 `token_limit_configs` 文档。
- **创建具有全新层级名称的配置** 是定义新订阅类型的方式 - 没有单独的“添加订阅类型”步骤。层级名称是自由文本(验证为 `^[a-z0-9_-]{2,32}$`),在创建时验证,创建后锁定(每个层级一个配置,由唯一索引强制执行)。
- “分配订阅”下拉框在用户编辑对话框中从 `GET /admin/users/subscription-types` 中获取,该接口联合了:两个内置默认值(`free`、`basic`)、每个层级都有配置,以及已经分配给用户的任何层级(以便用户的层级保持可选择,即使其配置被删除)。
### Admin API 路线
| 方法 | 路径 | 描述 |
|--------|------|--------------|
| `GET` | `/admin/users` | 列出所有用户 |
| `GET` | `/admin/users/subscription-types` | 列出系统当前已知的订阅层级名称 |
| `GET` | `/admin/users/:id` | 获取单个用户 |
| `POST` | `/admin/users` | 创建用户 |
| `PATCH` | `/admin/users/:id` | 更新用户(角色、订阅、激活、密码) |
| `DELETE` | `/admin/users/:id` | 删除用户(不是您自己的帐户) |
| `POST` | `/admin/users/:id/reset-tokens` | 重置用户的令牌使用计数器 |
| `GET` / `POST` | `/token-limit-configs` | 列出 / 创建令牌限制配置 |
| `GET` | `/token-limit-configs/:id` | 按 ID 获取配置 |
| `PUT` | `/token-limit-configs/:id` | 更新配置 |
| `DELETE` | `/token-limit-configs/:id` | 删除配置 |
以上所有操作都需要有效的 JWT 和 `role: 'admin'`。
## API 概述
| 方法 | 路径 | 描述 |
|--------|------|-------------|
| `POST` | `/auth/register` | 创建新用户账户 |
| `POST` | `/auth/login` | 身份验证并获取 JWT |
| `POST` | `/auth/mcp-servers` | 注册自定义 MCP 服务器(参见 [自定义 MCP 服务器](#自定义-mcp-服务器)) |
| `PATCH` | `/auth/mcp-servers/:id` | 启用/禁用自定义 MCP 服务器或编辑其允许的工具 |
| `POST` | `/auth/mcp-servers/:id/refresh` | 重新发现自定义 MCP 服务器的工具列表 |
| `DELETE` | `/auth/mcp-servers/:id` | 删除自定义 MCP 服务器 |
| `GET` | `/openai/models` | 通过 OpenAI SDK 列出模型 |
| `POST` | `/openai/completions-stream` | 通过聊天补全 API 进行流式 SSE,带有客户端 MCP 工具编排 — 唯一支持的聊天路径 |
| `GET` | `/openai/completions-stream/resume` | 重新附加到 `internalChatId` 的生成中(参见 [弹性后台生成](#弹性后台生成)) |
| `POST` | `/openai/tool-approval/:requestId` | 批准、拒绝或始终允许待处理的工具调用(参见 [工具审批](#工具审批)) |
| `GET` | `/chat-metadata` | 列出用户的聊天会话 |
| `GET` | `/chat-metadata/:id` | 获取单个聊天会话 |
| `POST` | `/chat-metadata` | 创建聊天会话 |
| `PATCH` | `/chat-metadata/:id` | 更新聊天会话 |
| `DELETE` | `/chat-metadata/:id` | 删除聊天会话 |
| `GET` | `/chats/:chatId` | 检索会话的消息 |
| `POST` | `/assets/:chatId` | 上传聊天会话的图像 |
| `GET` | `/assets/:chatId/:filename` | 检索上传的图像(所有者或共享聊天访问) |
| `GET` | `/assets/filequery/:filename?chatId=` | 通过查询参数检索图像(身份验证,用于 AI 生成的图像引用) |
| `GET` | `/invoke/test` | 测试端点 — 通过 InvokeAI 生成示例图像 |
| `GET` `/POST` | `/tools/mcp` | MCP 服务器端点(SSE + 流式 HTTP) |
| `GET`/`POST`/`PATCH`/`DELETE` | `/admin/users[/...]` | 管理后台 — 用户管理(`role: admin` 仅限,参见 [管理后台](#管理后台)) |
| `GET`/`POST`/`PUT`/`DELETE` | `/token-limit-configs[/...]` | 管理后台 — token 限制配置管理(`role: admin` 仅限) |
完整的交互式文档可在 `http://localhost:8888/api` 访问,当 `USE_SWAGGER=true` 时。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器