713 lines
16 KiB
Markdown
713 lines
16 KiB
Markdown
|
|
# ChatScope API Contract
|
|||
|
|
|
|||
|
|
> CHAT-002 | 版本 v1.0 | 2026-06-06
|
|||
|
|
>
|
|||
|
|
> 本文档是 ChatScope 前后端接口的**唯一权威契约**。
|
|||
|
|
> 所有响应 shape、错误码、SSE 格式以本文档为准。
|
|||
|
|
> 设计逻辑参见 [ChatScope 设计文档](./chat-scope-design.md)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 基础信息
|
|||
|
|
|
|||
|
|
| 项目 | 值 |
|
|||
|
|
|------|----|
|
|||
|
|
| Base Path | `/rag-chat` |
|
|||
|
|
| Auth | Bearer JWT (所有端点需要) |
|
|||
|
|
| Content-Type (Request) | `application/json` |
|
|||
|
|
| Content-Type (Response) | `application/json` (除 SSE 端点) |
|
|||
|
|
| Date Format | ISO 8601 (`2026-06-06T12:00:00.000Z`) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 枚举 & 常量
|
|||
|
|
|
|||
|
|
### 2.1 ChatScopeType
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
type ChatScopeType =
|
|||
|
|
| "knowledge_base"
|
|||
|
|
| "folder"
|
|||
|
|
| "material"
|
|||
|
|
| "knowledge_item"
|
|||
|
|
| "global";
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.2 CreatedFrom
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
type CreatedFrom =
|
|||
|
|
| "knowledge_base_detail"
|
|||
|
|
| "material_detail"
|
|||
|
|
| "material_reader"
|
|||
|
|
| "knowledge_item_detail"
|
|||
|
|
| "folder_detail"
|
|||
|
|
| "global_ai_entry"
|
|||
|
|
| "legacy_migration";
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.3 ModelMode
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
type ModelMode = "normal" | "deep_think" | "web_search";
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 端点清单
|
|||
|
|
|
|||
|
|
| Method | Path | 说明 | Issue |
|
|||
|
|
|--------|------|------|-------|
|
|||
|
|
| POST | `/rag-chat/sessions` | 创建/打开会话 (open-or-create) | #81 #76 |
|
|||
|
|
| GET | `/rag-chat/sessions` | 会话列表(支持 scope 过滤) | #75 |
|
|||
|
|
| GET | `/rag-chat/sessions/:id/messages` | 获取消息历史 | (已有) |
|
|||
|
|
| POST | `/rag-chat/sessions/:id/messages` | 发送消息(同步) | (已有) |
|
|||
|
|
| POST | `/rag-chat/sessions/:id/stream` | 发送消息(SSE 流式) | (已有) |
|
|||
|
|
| PATCH | `/rag-chat/sessions/:id` | 更新会话属性 | NEW |
|
|||
|
|
| DELETE | `/rag-chat/sessions/:id` | 软删除会话 | (已有) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 端点详细定义
|
|||
|
|
|
|||
|
|
### 4.1 POST /rag-chat/sessions — open-or-create
|
|||
|
|
|
|||
|
|
创建新会话,或在匹配的 scope 下返回已有会话。
|
|||
|
|
|
|||
|
|
**Request Body:**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"scopeType": "material",
|
|||
|
|
"scopeId": "clx7abc123",
|
|||
|
|
"parentKnowledgeBaseId": "clx7kb456",
|
|||
|
|
"createdFrom": "material_detail",
|
|||
|
|
"title": "新对话"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 字段 | 类型 | 必需 | 默认值 | 说明 |
|
|||
|
|
|------|------|------|--------|------|
|
|||
|
|
| scopeType | ChatScopeType | **是** | — | 会话绑定范围 |
|
|||
|
|
| scopeId | string \| null | 否 | null | scope 对应的实体 ID |
|
|||
|
|
| parentKnowledgeBaseId | string \| null | 否 | null | 所属知识库 |
|
|||
|
|
| createdFrom | CreatedFrom | 否 | `"global_ai_entry"` | 入口标识 |
|
|||
|
|
| title | string | 否 | `"新对话"` | 会话标题 |
|
|||
|
|
|
|||
|
|
**Validation Rules:**
|
|||
|
|
|
|||
|
|
- `scopeType` 必须是合法枚举值,否则 400
|
|||
|
|
- `scopeType === "global"` 时,`scopeId` 必须为 null
|
|||
|
|
- `scopeType !== "global"` 时,`scopeId` 不能为空(404 或 400)
|
|||
|
|
|
|||
|
|
**Behavior (open-or-create):**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
IF 存在 userId + scopeType + scopeId 完全匹配 + isDeleted=false 的会话
|
|||
|
|
→ 返回该会话 (HTTP 200)
|
|||
|
|
ELSE
|
|||
|
|
→ 创建新会话并返回 (HTTP 201)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Response 200 (已有会话):**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"id": "clx7sess001",
|
|||
|
|
"userId": "clx7user001",
|
|||
|
|
"scopeType": "material",
|
|||
|
|
"scopeId": "clx7abc123",
|
|||
|
|
"parentKnowledgeBaseId": "clx7kb456",
|
|||
|
|
"title": "数据库事务讨论",
|
|||
|
|
"createdFrom": "material_detail",
|
|||
|
|
"modelMode": "normal",
|
|||
|
|
"modelId": null,
|
|||
|
|
"isPinned": false,
|
|||
|
|
"isArchived": false,
|
|||
|
|
"isDeleted": false,
|
|||
|
|
"lastMessageAt": "2026-06-05T15:30:00.000Z",
|
|||
|
|
"createdAt": "2026-06-01T08:00:00.000Z",
|
|||
|
|
"updatedAt": "2026-06-05T15:30:00.000Z"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Response 201 (新建会话):**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"id": "clx7sess002",
|
|||
|
|
"userId": "clx7user001",
|
|||
|
|
"scopeType": "material",
|
|||
|
|
"scopeId": "clx7abc123",
|
|||
|
|
"parentKnowledgeBaseId": "clx7kb456",
|
|||
|
|
"title": "新对话",
|
|||
|
|
"createdFrom": "material_detail",
|
|||
|
|
"modelMode": "normal",
|
|||
|
|
"modelId": null,
|
|||
|
|
"isPinned": false,
|
|||
|
|
"isArchived": false,
|
|||
|
|
"isDeleted": false,
|
|||
|
|
"lastMessageAt": null,
|
|||
|
|
"createdAt": "2026-06-06T10:00:00.000Z",
|
|||
|
|
"updatedAt": "2026-06-06T10:00:00.000Z"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Errors:**
|
|||
|
|
|
|||
|
|
| Code | 条件 |
|
|||
|
|
|------|------|
|
|||
|
|
| 400 | scopeType 不合法 |
|
|||
|
|
| 400 | scopeType !== "global" 且 scopeId 为空 |
|
|||
|
|
| 401 | JWT 缺失或过期 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4.2 GET /rag-chat/sessions — 会话列表
|
|||
|
|
|
|||
|
|
**Query Parameters:**
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 必需 | 默认值 | 说明 |
|
|||
|
|
|------|------|------|--------|------|
|
|||
|
|
| scopeType | ChatScopeType | 否 | — | 过滤 scope 类型 |
|
|||
|
|
| scopeId | string | 否 | — | 过滤 scope ID(需配合 scopeType) |
|
|||
|
|
| parentKnowledgeBaseId | string | 否 | — | 知识库下所有会话 |
|
|||
|
|
| isArchived | boolean | 否 | false | 是否返回已归档 |
|
|||
|
|
| page | number | 否 | 1 | 页码 |
|
|||
|
|
| limit | number | 否 | 20 | 每页数量(最大 50) |
|
|||
|
|
|
|||
|
|
**过滤逻辑:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
IF scopeType + scopeId → 精确匹配(同一 scope 的历史)
|
|||
|
|
ELIF parentKnowledgeBaseId → 该 KB 下所有会话(不区分 scope)
|
|||
|
|
ELSE → 全局列表(所有 scope,排除 isDeleted)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Response 200:**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"data": [
|
|||
|
|
{
|
|||
|
|
"id": "clx7sess001",
|
|||
|
|
"userId": "clx7user001",
|
|||
|
|
"scopeType": "material",
|
|||
|
|
"scopeId": "clx7abc123",
|
|||
|
|
"parentKnowledgeBaseId": "clx7kb456",
|
|||
|
|
"title": "数据库事务讨论",
|
|||
|
|
"createdFrom": "material_detail",
|
|||
|
|
"modelMode": "normal",
|
|||
|
|
"modelId": null,
|
|||
|
|
"isPinned": true,
|
|||
|
|
"isArchived": false,
|
|||
|
|
"isDeleted": false,
|
|||
|
|
"lastMessageAt": "2026-06-05T15:30:00.000Z",
|
|||
|
|
"createdAt": "2026-06-01T08:00:00.000Z",
|
|||
|
|
"updatedAt": "2026-06-05T15:30:00.000Z"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"meta": {
|
|||
|
|
"page": 1,
|
|||
|
|
"limit": 20,
|
|||
|
|
"total": 42
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**排序规则:**
|
|||
|
|
|
|||
|
|
- `isPinned === true` 优先
|
|||
|
|
- 同优先级按 `lastMessageAt DESC`(最近活跃在前)
|
|||
|
|
- 无消息的会话按 `createdAt DESC`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4.3 GET /rag-chat/sessions/:id/messages — 消息历史
|
|||
|
|
|
|||
|
|
**Response 200:**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"id": "clx7msg001",
|
|||
|
|
"sessionId": "clx7sess001",
|
|||
|
|
"role": "user",
|
|||
|
|
"content": "TCP 三次握手的过程是什么?",
|
|||
|
|
"tokens": 0,
|
|||
|
|
"scopeSnapshot": {
|
|||
|
|
"scopeType": "material",
|
|||
|
|
"scopeId": "clx7abc123",
|
|||
|
|
"parentKnowledgeBaseId": "clx7kb456"
|
|||
|
|
},
|
|||
|
|
"createdAt": "2026-06-06T10:00:00.000Z",
|
|||
|
|
"citations": []
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"id": "clx7msg002",
|
|||
|
|
"sessionId": "clx7sess001",
|
|||
|
|
"role": "assistant",
|
|||
|
|
"content": "TCP 三次握手的过程如下:...",
|
|||
|
|
"tokens": 245,
|
|||
|
|
"scopeSnapshot": {
|
|||
|
|
"scopeType": "material",
|
|||
|
|
"scopeId": "clx7abc123",
|
|||
|
|
"parentKnowledgeBaseId": "clx7kb456"
|
|||
|
|
},
|
|||
|
|
"createdAt": "2026-06-06T10:00:05.000Z",
|
|||
|
|
"citations": [
|
|||
|
|
{
|
|||
|
|
"id": "clx7cit001",
|
|||
|
|
"messageId": "clx7msg002",
|
|||
|
|
"chunkId": "clx7chk042",
|
|||
|
|
"sourceId": "clx7abc123",
|
|||
|
|
"sourceTitle": "计算机网络自顶向下.pdf",
|
|||
|
|
"excerptText": "TCP 连接建立需要三次握手...",
|
|||
|
|
"pageNumber": 156,
|
|||
|
|
"lineStart": 12,
|
|||
|
|
"lineEnd": 18,
|
|||
|
|
"createdAt": "2026-06-06T10:00:05.000Z"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
排序: `createdAt ASC`(时间正序)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4.4 POST /rag-chat/sessions/:id/messages — 发送消息 (同步)
|
|||
|
|
|
|||
|
|
**Request Body:**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"content": "TCP 三次握手的过程是什么?"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 字段 | 类型 | 必需 | 说明 |
|
|||
|
|
|------|------|------|------|
|
|||
|
|
| content | string | **是** | 消息正文(最长 10000 字符) |
|
|||
|
|
|
|||
|
|
**Response 200:**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"id": "clx7msg002",
|
|||
|
|
"role": "assistant",
|
|||
|
|
"content": "TCP 三次握手的过程如下:...",
|
|||
|
|
"tokens": 245,
|
|||
|
|
"blocked": false,
|
|||
|
|
"message": {
|
|||
|
|
"id": "clx7msg002",
|
|||
|
|
"sessionId": "clx7sess001",
|
|||
|
|
"role": "assistant",
|
|||
|
|
"content": "TCP 三次握手的过程如下:...",
|
|||
|
|
"tokens": 245,
|
|||
|
|
"createdAt": "2026-06-06T10:00:05.000Z",
|
|||
|
|
"citations": [...]
|
|||
|
|
},
|
|||
|
|
"citations": [...]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Response (被拦截):**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"blocked": true,
|
|||
|
|
"message": "输入包含违规内容,请修改后重试"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Errors:**
|
|||
|
|
|
|||
|
|
| Code | 条件 |
|
|||
|
|
|------|------|
|
|||
|
|
| 404 | 会话不存在 |
|
|||
|
|
| 403 | 会话不属于当前用户 |
|
|||
|
|
| 400 | content 为空或超过 10000 字符 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4.5 POST /rag-chat/sessions/:id/stream — SSE 流式
|
|||
|
|
|
|||
|
|
**Request Body:** 同 4.4
|
|||
|
|
|
|||
|
|
**Response:** `text/event-stream; charset=utf-8`
|
|||
|
|
|
|||
|
|
**SSE 事件格式:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
data: {"type":"thinking","content":"我们来看看..."}
|
|||
|
|
|
|||
|
|
data: {"type":"content","content":"TCP "}
|
|||
|
|
|
|||
|
|
data: {"type":"content","content":"三次握手是"}
|
|||
|
|
|
|||
|
|
data: {"type":"content","content":"..."}
|
|||
|
|
|
|||
|
|
data: {"type":"citations","citations":[{"sourceTitle":"...","excerptText":"..."}]}
|
|||
|
|
|
|||
|
|
data: {"type":"done"}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Chunk 类型定义:**
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
interface SSEChunk {
|
|||
|
|
type: "thinking" | "content" | "citations" | "done" | "error";
|
|||
|
|
content?: string;
|
|||
|
|
citations?: ChatCitation[];
|
|||
|
|
error?: string;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| type | 说明 | content | citations | error |
|
|||
|
|
|------|------|---------|-----------|-------|
|
|||
|
|
| thinking | 思考过程片段 | 思考文本 | — | — |
|
|||
|
|
| content | 回答正文片段 | 增量文本 | — | — |
|
|||
|
|
| citations | 引用信息(流末尾) | — | 引用数组 | — |
|
|||
|
|
| done | 流结束 | — | — | — |
|
|||
|
|
| error | 流错误 | — | — | 错误信息 |
|
|||
|
|
|
|||
|
|
**调用方必须:**
|
|||
|
|
|
|||
|
|
1. 逐行读取 `data: {...}\n\n`
|
|||
|
|
2. JSON.parse 每行 `data:` 后的内容
|
|||
|
|
3. 累积 `thinking` chunk → 思考过程
|
|||
|
|
4. 累积 `content` chunk → 回答正文
|
|||
|
|
5. `done` 或 `error` → 结束流
|
|||
|
|
|
|||
|
|
**Headers:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Content-Type: text/event-stream; charset=utf-8
|
|||
|
|
Cache-Control: no-cache
|
|||
|
|
Connection: keep-alive
|
|||
|
|
X-Accel-Buffering: no
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4.6 PATCH /rag-chat/sessions/:id — 更新会话 (NEW)
|
|||
|
|
|
|||
|
|
**Request Body (全部可选):**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"title": "三次握手深度讨论",
|
|||
|
|
"isPinned": true,
|
|||
|
|
"isArchived": false,
|
|||
|
|
"modelMode": "deep_think",
|
|||
|
|
"modelId": "deepseek-v4-pro"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 字段 | 类型 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| title | string | 新标题(最长 200 字符) |
|
|||
|
|
| isPinned | boolean | 置顶 |
|
|||
|
|
| isArchived | boolean | 归档 |
|
|||
|
|
| modelMode | ModelMode | 模型模式 |
|
|||
|
|
| modelId | string \| null | 模型 ID |
|
|||
|
|
|
|||
|
|
**不可变字段:**
|
|||
|
|
|
|||
|
|
- `scopeType` — 创建后不可修改
|
|||
|
|
- `scopeId` — 创建后不可修改
|
|||
|
|
- `parentKnowledgeBaseId` — 创建后不可修改(由后端推导)
|
|||
|
|
- `createdFrom` — 创建后不可修改
|
|||
|
|
|
|||
|
|
尝试修改这些字段 → 字段被静默忽略(不报错)
|
|||
|
|
|
|||
|
|
**Response 200:** 更新后的 ChatSession 对象
|
|||
|
|
|
|||
|
|
**Errors:**
|
|||
|
|
|
|||
|
|
| Code | 条件 |
|
|||
|
|
|------|------|
|
|||
|
|
| 404 | 会话不存在 |
|
|||
|
|
| 403 | 会话不属于当前用户 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4.7 DELETE /rag-chat/sessions/:id — 软删除
|
|||
|
|
|
|||
|
|
**Behavior:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
UPDATE ChatSession SET isDeleted = true WHERE id = :id AND userId = :userId
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
消息不物理删除。
|
|||
|
|
|
|||
|
|
**Response 200:**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"success": true,
|
|||
|
|
"message": "会话已删除"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. TypeScript 类型 (后端)
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// === Request DTOs ===
|
|||
|
|
|
|||
|
|
interface CreateSessionDto {
|
|||
|
|
scopeType: ChatScopeType;
|
|||
|
|
scopeId?: string | null;
|
|||
|
|
parentKnowledgeBaseId?: string | null;
|
|||
|
|
createdFrom?: CreatedFrom;
|
|||
|
|
title?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface ListSessionsQuery {
|
|||
|
|
scopeType?: ChatScopeType;
|
|||
|
|
scopeId?: string;
|
|||
|
|
parentKnowledgeBaseId?: string;
|
|||
|
|
isArchived?: boolean;
|
|||
|
|
page?: number;
|
|||
|
|
limit?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface SendMessageDto {
|
|||
|
|
content: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface UpdateSessionDto {
|
|||
|
|
title?: string;
|
|||
|
|
isPinned?: boolean;
|
|||
|
|
isArchived?: boolean;
|
|||
|
|
modelMode?: ModelMode;
|
|||
|
|
modelId?: string | null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// === Response Types ===
|
|||
|
|
|
|||
|
|
interface ChatSessionResponse {
|
|||
|
|
id: string;
|
|||
|
|
userId: string;
|
|||
|
|
scopeType: ChatScopeType;
|
|||
|
|
scopeId: string | null;
|
|||
|
|
parentKnowledgeBaseId: string | null;
|
|||
|
|
title: string;
|
|||
|
|
createdFrom: CreatedFrom;
|
|||
|
|
modelMode: ModelMode;
|
|||
|
|
modelId: string | null;
|
|||
|
|
isPinned: boolean;
|
|||
|
|
isArchived: boolean;
|
|||
|
|
isDeleted: boolean;
|
|||
|
|
lastMessageAt: string | null;
|
|||
|
|
createdAt: string;
|
|||
|
|
updatedAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface ChatMessageResponse {
|
|||
|
|
id: string;
|
|||
|
|
sessionId: string;
|
|||
|
|
role: "user" | "assistant";
|
|||
|
|
content: string;
|
|||
|
|
tokens: number;
|
|||
|
|
scopeSnapshot: ChatScope | null;
|
|||
|
|
createdAt: string;
|
|||
|
|
citations: ChatCitationResponse[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface ChatCitationResponse {
|
|||
|
|
id: string;
|
|||
|
|
messageId: string;
|
|||
|
|
chunkId: string | null;
|
|||
|
|
sourceId: string | null;
|
|||
|
|
sourceTitle: string | null;
|
|||
|
|
excerptText: string | null;
|
|||
|
|
pageNumber: number | null;
|
|||
|
|
lineStart: number | null;
|
|||
|
|
lineEnd: number | null;
|
|||
|
|
createdAt: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface PaginatedResponse<T> {
|
|||
|
|
data: T[];
|
|||
|
|
meta: {
|
|||
|
|
page: number;
|
|||
|
|
limit: number;
|
|||
|
|
total: number;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface SendMessageResponse {
|
|||
|
|
id?: string;
|
|||
|
|
role?: string;
|
|||
|
|
content?: string;
|
|||
|
|
tokens?: number;
|
|||
|
|
blocked?: boolean;
|
|||
|
|
message?: ChatMessageResponse;
|
|||
|
|
citations?: ChatCitationResponse[];
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Swift 类型 (iOS)
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
// MARK: - Request DTOs
|
|||
|
|
|
|||
|
|
struct CreateChatSessionRequest: Codable {
|
|||
|
|
let scopeType: String
|
|||
|
|
let scopeId: String?
|
|||
|
|
let parentKnowledgeBaseId: String?
|
|||
|
|
let createdFrom: String
|
|||
|
|
let title: String?
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct SendMessageRequest: Codable {
|
|||
|
|
let content: String
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct UpdateChatSessionRequest: Codable {
|
|||
|
|
var title: String?
|
|||
|
|
var isPinned: Bool?
|
|||
|
|
var isArchived: Bool?
|
|||
|
|
var modelMode: String?
|
|||
|
|
var modelId: String?
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - Response Types
|
|||
|
|
|
|||
|
|
struct ChatSessionResponse: Codable, Identifiable {
|
|||
|
|
let id: String
|
|||
|
|
let userId: String?
|
|||
|
|
let scopeType: String
|
|||
|
|
let scopeId: String?
|
|||
|
|
let parentKnowledgeBaseId: String?
|
|||
|
|
let title: String?
|
|||
|
|
let createdFrom: String?
|
|||
|
|
let modelMode: String?
|
|||
|
|
let modelId: String?
|
|||
|
|
let isPinned: Bool?
|
|||
|
|
let isArchived: Bool?
|
|||
|
|
let isDeleted: Bool?
|
|||
|
|
let lastMessageAt: String?
|
|||
|
|
let createdAt: String?
|
|||
|
|
let updatedAt: String?
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct ChatMessageResponse: Codable, Identifiable {
|
|||
|
|
let id: String
|
|||
|
|
let sessionId: String?
|
|||
|
|
let role: String
|
|||
|
|
let content: String
|
|||
|
|
let tokens: Int?
|
|||
|
|
let scopeSnapshot: ChatScopeSnapshot?
|
|||
|
|
let createdAt: String?
|
|||
|
|
let citations: [ChatCitationResponse]?
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct ChatScopeSnapshot: Codable {
|
|||
|
|
let scopeType: String?
|
|||
|
|
let scopeId: String?
|
|||
|
|
let parentKnowledgeBaseId: String?
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct ChatCitationResponse: Codable, Identifiable {
|
|||
|
|
let id: String
|
|||
|
|
let messageId: String?
|
|||
|
|
let chunkId: String?
|
|||
|
|
let sourceId: String?
|
|||
|
|
let sourceTitle: String?
|
|||
|
|
let excerptText: String?
|
|||
|
|
let pageNumber: Int?
|
|||
|
|
let lineStart: Int?
|
|||
|
|
let lineEnd: Int?
|
|||
|
|
let createdAt: String?
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct SendMessageResponse: Codable {
|
|||
|
|
let id: String?
|
|||
|
|
let role: String?
|
|||
|
|
let content: String?
|
|||
|
|
let tokens: Int?
|
|||
|
|
let blocked: Bool?
|
|||
|
|
let message: ChatMessageResponse?
|
|||
|
|
let citations: [ChatCitationResponse]?
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct PaginatedChatSessions: Codable {
|
|||
|
|
let data: [ChatSessionResponse]
|
|||
|
|
let meta: PaginationMeta
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - SSE Chunk
|
|||
|
|
|
|||
|
|
struct SSEChunk: Decodable {
|
|||
|
|
let type: String // "thinking" | "content" | "citations" | "done" | "error"
|
|||
|
|
let content: String?
|
|||
|
|
let citations: [ChatCitationResponse]?
|
|||
|
|
let error: String?
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 错误响应格式
|
|||
|
|
|
|||
|
|
所有错误统一格式:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"statusCode": 400,
|
|||
|
|
"message": "scopeType must be one of: knowledge_base, folder, material, knowledge_item, global",
|
|||
|
|
"error": "Bad Request"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| HTTP Code | 场景 |
|
|||
|
|
|-----------|------|
|
|||
|
|
| 400 | 参数校验失败 |
|
|||
|
|
| 401 | JWT 缺失/过期 |
|
|||
|
|
| 403 | 会话不属于当前用户 |
|
|||
|
|
| 404 | 会话不存在 |
|
|||
|
|
| 413 | content 超过 10000 字符 |
|
|||
|
|
| 500 | 服务端未知错误 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. 版本兼容性
|
|||
|
|
|
|||
|
|
| 版本 | 日期 | 变更 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| v1.0 | 2026-06-06 | 初始版本 — 完整 ChatScope API |
|
|||
|
|
|
|||
|
|
向后兼容策略:
|
|||
|
|
- 新增字段:前端未传 → 使用默认值
|
|||
|
|
- 新增响应字段:前端忽略未知字段(Codable 默认行为)
|
|||
|
|
- 不可变字段:PATCH 时静默忽略(不报错)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. 依赖
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
本文档依赖:
|
|||
|
|
chat-scope-design.md (CHAT-001) — ChatScope 类型定义、规则、决策表、open-or-create 算法
|
|||
|
|
|
|||
|
|
本文档被依赖:
|
|||
|
|
#81 M7-05 — createSession 实现
|
|||
|
|
#76 M7-08 — open-or-create 实现
|
|||
|
|
#75 M7-07 — listSessions 实现
|
|||
|
|
#45-#50 — iOS AI Chat View
|
|||
|
|
#39-#44 — iOS 入口接入
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
> **本文档是前后端接口的唯一权威契约。如有冲突,以本文档为准。**
|