Merge remote-tracking branch 'origin/master' into ji

# Conflicts:
#	.gitignore
#	frontend-web/src/components/workspace/artifacts/context.tsx
#	frontend-web/src/components/workspace/messages/message-group.tsx
#	offline-backend-20260512/backend/CLAUDE.md
#	offline-backend-20260512/backend/README.md
#	offline-backend-20260512/backend/app/gateway/app.py
#	offline-backend-20260512/backend/app/gateway/deps.py
#	offline-backend-20260512/backend/app/gateway/routers/llmwiki.py
This commit is contained in:
吉鑫升 committed 2026-08-24 14:21:19 +08:00
commit e818c24769
262 files changed
+29527 -5236

No files matched your search

@@ -0,0 +1,174 @@
# 外部系统智能体会话与消息 API
Base URL 为 Gateway 的 `/api`(部署若由反向代理配置了前缀,以网关实际暴露地址为准)。所有接口沿用现有 `Authorization: Bearer <token>` 鉴权;无效或过期 token 返回 `401`。
## 会话隔离键
外部系统创建和检索会话时使用以下**精确拼写**的 metadata 键,三个值均为字符串:
| 键 | 类型 | 约定 |
| --- | --- | --- |
| `agent_id` | string | 智能体 ID |
| `task_id` | string | 任务 ID;数字也必须传字符串 |
| `chat_type` | string | 页面决定的隔离标识;UTF-8 原样保存、原样比较、原样返回,不做 trim、大小写转换或枚举校验 |
`chat_type` 的可用长度大于 64 字符(metadata 存为 JSON 对象)。存量未带 `chat_type` 的线程保持原有行为;只有检索条件中显式提供 `chat_type` 时才会要求该字段精确相等。
## 1. 创建与按隔离键查找会话
### `POST /api/threads`
| 参数 | 位置 | 必填 | 类型 | 说明 |
| --- | --- | --- | --- | --- |
| `metadata.agent_id` | body | 是(外部集成约定) | string | 智能体 ID |
| `metadata.task_id` | body | 是(外部集成约定) | string | 任务 ID |
| `metadata.chat_type` | body | 是(新会话) | string | 页面隔离键 |
| `thread_id` | body | 否 | string | 不传则服务端生成 UUID |
| `assistant_id` | body | 否 | string | 现有兼容字段 |
```json
{
"metadata": {
"agent_id": "c353429a73e146ae9e6926054b2d155a",
"task_id": "108",
"chat_type": "测试分析"
}
}
```
```json
{
"thread_id": "c7cefe92-84f8-4f31-aed0-a16f7b151cc2",
"status": "idle",
"created_at": "2026-08-22T10:00:00+00:00",
"updated_at": "2026-08-22T10:00:00+00:00",
"metadata": {
"agent_id": "c353429a73e146ae9e6926054b2d155a",
"task_id": "108",
"chat_type": "测试分析"
},
"values": {},
"interrupts": {}
}
```
### `POST /api/threads/search`
| 参数 | 位置 | 必填 | 类型 | 默认/范围 | 说明 |
| --- | --- | --- | --- | --- | --- |
| `metadata` | body | 否 | object | `{}` | 所有提供的键均做精确 AND 匹配 |
| `limit` | body | 否 | integer | `100`,`1..1000` | 最多返回条数 |
| `offset` | body | 否 | integer | `0`,`>=0` | 结果偏移量 |
```json
{
"metadata": {
"agent_id": "c353429a73e146ae9e6926054b2d155a",
"task_id": "108",
"chat_type": "测试分析"
},
"limit": 1
}
```
```json
[
{
"thread_id": "c7cefe92-84f8-4f31-aed0-a16f7b151cc2",
"status": "idle",
"created_at": "2026-08-22T10:00:00+00:00",
"updated_at": "2026-08-22T10:05:10+00:00",
"metadata": {
"agent_id": "c353429a73e146ae9e6926054b2d155a",
"task_id": "108",
"chat_type": "测试分析"
},
"values": {},
"interrupts": {}
}
]
```
匹配结果按 `updated_at` **倒序**,并以 `thread_id` 倒序作同时间稳定排序。响应返回完整 metadata。
## 2. 读取会话消息
### `GET /api/threads/{thread_id}/messages`
| 参数 | 位置 | 必填 | 类型 | 默认/范围 | 说明 |
| --- | --- | --- | --- | --- | --- |
| `thread_id` | path | 是 | string | - | 会话 ID |
| `limit` | query | 否 | integer | `200`,`1..1000` | 返回消息数 |
| `offset` | query | 否 | integer | `0`,`>=0` | 从最早消息起跳过的消息数 |
消息按照会话内实际写入顺序(时间正序)返回,`total` 为分页前总条数。接口读取最新 LangGraph checkpoint,并使用与 `state`/流式消息相同的序列化路径:不按展示用途裁剪 `additional_kwargs`、`tool_calls`、隐藏消息或供应商扩展字段。
```json
{
"total": 2,
"messages": [
{
"type": "human",
"id": "msg-1",
"content": [{"type": "text", "text": "隐藏上下文"}],
"additional_kwargs": {"hide_from_ui": true, "client_ts": "2026-08-22T18:00:00+08:00"}
},
{
"type": "ai",
"id": "msg-2",
"content": "这是回答",
"tool_calls": [{"name": "lookup", "args": {"keyword": "测试"}, "id": "call-1"}],
"additional_kwargs": {"reasoning_content": "完整思考字段", "hide_from_ui": false}
}
]
}
```
`hide_from_ui` 位于消息的 `additional_kwargs.hide_from_ui`(若该消息实际带有此字段)。`tool_calls[].args` 不被转换:LangChain 已解析的标准 `tool_calls` 通常是 JSON 对象;供应商原始工具调用若保留在 `additional_kwargs`,其原始字符串/对象形态也保持不变。
## 3. 删除一轮对话
### `DELETE /api/threads/{thread_id}/messages/{message_id}`
`message_id` 应取自消息列表中用户消息(`type: "human"`)的 `id`。删除该问题及其后的 AI、工具、中间消息,直到下一条用户消息之前;若传入的是非用户消息 ID,仅删除该消息。删除会写入新的 checkpoint,随后读取消息列表即可获得已删除后的结果。
成功示例:
```json
{
"ok": true,
"deleted_ids": ["question-1", "answer-1", "tool-1"],
"remaining": 2
}
```
## 4. 删除整个会话
### `DELETE /api/threads/{thread_id}`
清理该线程的 checkpoint(含消息)、线程 metadata 与该线程目录下的上传文件/工作区/生成产物。会话检索不再返回该线程。
```json
{"success": true, "message": "Deleted local thread data for c7cefe92-84f8-4f31-aed0-a16f7b151cc2"}
```
## HTTP 错误
| 状态码 | 触发条件 | 响应体示例 |
| --- | --- | --- |
| 401 | 缺少、无效或过期的 Bearer token | `{"detail":"Authentication required"}` |
| 403 | token 没有对应线程权限 | `{"detail":"Permission denied: threads:read"}` |
| 404 | 线程不存在、无权访问、或 `message_id` 不存在 | `{"detail":"Thread xxx not found"}` |
| 409 | 上述接口当前不使用 409 | 不适用 |
| 422 | 分页参数越界,或请求体字段类型不合法 | FastAPI 标准校验错误对象 |
| 429 | 上述接口本身未配置限流;如部署侧网关限流,由部署网关返回其配置的响应体 | 不适用 |
| 500 | checkpoint 或持久化读取/写入失败 | `{"detail":"Failed to get thread messages"}` 等 |
时间字段 `created_at` 与 `updated_at` 使用 ISO 8601、带 UTC 偏移量;不是毫秒时间戳。
## 兼容性与生命周期
- 既有 `/threads`、`/runs/stream`、`/models` 调用不改动;仅新增消息查询,并修复单轮删除的 checkpoint 持久化。
- 普通线程未配置通用自动过期策略、单任务会话数上限或 `chat_type` 枚举;保留至调用方删除或运维侧另行清理。AI 写作等独立业务的专用清理策略不适用于本接口。
- 外部系统的联调 task、预置线程、发布窗口和联系人属于部署运维信息,不在代码中硬编码;请在目标测试环境创建后补入联调单。
- Swagger 地址为 `<Gateway Base URL>/docs`;OpenAPI JSON 为 `<Gateway Base URL>/openapi.json`。