Files
CamTalk/docs/02-接口文档.md

558 lines
13 KiB
Markdown
Raw Normal View History

# 接口文档
## 概述
前后端通信接口契约。WebSocket 承载实时对话REST API 支撑基础运维。
**设计原则**
- WebSocket 为主:所有对话数据走 WebSocket
- REST 为辅:仅用于健康检查、认证、对话管理等低频操作
- 接口先行:先定义契约,再填充实现——前后端可并行开发
## 接口全景
```
浏览器 Go Gateway :8080
WebSocket Client <--> /ws?token=<jwt> (实时对话,需 JWT 认证)
HTTP Client --> GET /api/health (健康检查)
HTTP Client <--> POST /api/auth/* (注册/登录/刷新/登出)
HTTP Client <--> GET/POST/PATCH/DELETE (对话 CRUD
/api/conversations/*
HTTP Client <--> GET /api/conversations/:id (历史消息)
/messages
```
---
## 一、WebSocket 协议
连接地址:`ws://localhost:8080/ws?token=<access_token>&conversation_id=<uuid>`
| 参数 | 必填 | 说明 |
|------|------|------|
| `token` | 是 | JWT access_token缺失或无效时返回 401 |
| `conversation_id` | 否 | 恢复已有对话;省略则创建新对话 |
### 消息格式约定
所有 WebSocket 消息均为 JSON 文本帧,统一结构:
```typescript
interface WsMessage {
type: string; // 消息类型,必填
request_id?: string; // 可选,用于请求-响应关联
timestamp?: number; // 可选,毫秒时间戳
[key: string]: any; // 类型特定字段
}
```
### 客户端 → 服务端消息
#### `query` — 发起一次视觉对话
```typescript
interface QueryMessage {
type: "query";
request_id: string; // 客户端生成的 UUID
image: string; // Base64 编码的 JPEG 图像(不含 data: 前缀)
audio: string; // Base64 编码的音频片段PCM 16kHz文本输入时为空字符串
text?: string; // 用户手动输入的文本(有值时跳过 STT直接使用此文本
mime_type?: string; // 音频格式,默认 "audio/pcm"
}
```
#### `config` — 更新会话配置
```typescript
interface ConfigMessage {
type: "config";
payload: {
tts_enabled?: boolean; // 是否开启语音合成,默认 true
detail_level?: "low" | "high"; // 图像精度,默认 "low"
language?: string; // 交互语言,默认 "zh-CN"
scenario?: string; // 场景模式free_chat / interviewer / english_teacher / debate / interpreter
};
}
```
#### `interrupt` — 打断当前回复
```typescript
interface InterruptMessage {
type: "interrupt";
request_id?: string; // 可选,当前实现不使用此字段,服务端始终取消当前活跃请求
}
```
#### `ping` — 心跳保活
```typescript
interface PingMessage {
type: "ping";
}
```
### 服务端 → 客户端消息
#### `connected` — 连接建立确认
```typescript
interface ConnectedMessage {
type: "connected";
session_id: string;
conversation_id: string;
config: {
tts_enabled: boolean;
detail_level: "low" | "high";
language: string;
scenario: string;
};
}
```
#### `stt_result` — 语音识别结果
```typescript
interface SttResultMessage {
type: "stt_result";
request_id: string;
text: string; // 识别出的文本
is_final: boolean; // 当前实现始终为 true
}
```
#### `llm_chunk` — LLM 流式响应片段
```typescript
interface LlmChunkMessage {
type: "llm_chunk";
request_id: string;
content: string; // 当前 token 片段
}
```
#### `llm_done` — LLM 响应完成
```typescript
interface LlmDoneMessage {
type: "llm_done";
request_id: string;
full_text: string; // 完整响应文本
}
```
#### `tts_audio` — TTS 音频片段
音频流式推送,每个消息携带一个句子的音频数据。
```typescript
interface TtsAudioMessage {
type: "tts_audio";
request_id: string;
audio: string; // Base64 编码的音频数据
format: string; // 音频格式
sample_rate: number; // 采样率Hz
sequence: number; // 句子序号,从 0 开始递增
is_final: boolean; // 是否为最后一个句子
}
```
**音频格式约束**
| 字段 | 值 | 说明 |
|------|-----|------|
| `format` | `"pcm"` | 线性 PCM小端序 |
| `sample_rate` | `24000` | 24kHz 采样率 |
| 位深度 | 16-bit | 单声道 |
| 句子划分 | 按标点符号(。!?;:)分割 | 服务端按句分割 LLM 响应,并行合成 |
#### `error` — 错误通知
```typescript
interface ErrorMessage {
type: "error";
request_id?: string; // 关联的请求 ID全局错误时为空
code: string; // 错误码,见下文错误码表
message: string; // 人类可读的错误描述
details?: any; // 可选的详细错误信息
}
```
#### `pong` — 心跳响应
```typescript
interface PongMessage {
type: "pong";
}
```
### 连接管理
- **心跳机制**:客户端每 30 秒发送 `ping`,服务端回复 `pong`60 秒无活动则服务端断开连接
- **重连策略**客户端断线后指数退避重连1s → 2s → 4s → ... → 最大 30s
- **并发控制**:同一连接同时只能有一个活跃的 `query` 请求;新请求到来时自动取消旧请求
---
## 二、REST API
所有 REST 端点均使用 JSON 格式。
### 2.1 健康检查
#### `GET /api/health`
检查服务健康状态。
**响应**
```json
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00Z",
"dependencies": {
"database": "healthy",
"redis": "healthy"
}
}
```
### 2.2 认证 API
#### `POST /api/auth/register` — 用户注册
**请求**
```json
{
"username": "alice",
"email": "alice@example.com",
"password": "SecurePass123!"
}
```
**响应**200 OK
```json
{
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"username": "alice",
"email": "alice@example.com",
"created_at": "2024-01-15T10:30:00Z"
},
"access_token": "eyJhbGc...",
"refresh_token": "eyJhbGc...",
"expires_in": 7200
}
```
**错误**
- `400 INVALID_INPUT`: 参数验证失败
- `409 USER_EXISTS`: 用户名或邮箱已存在
#### `POST /api/auth/login` — 用户登录
**请求**
```json
{
"username": "alice",
"password": "SecurePass123!"
}
```
**响应**200 OK
```json
{
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"username": "alice",
"email": "alice@example.com"
},
"access_token": "eyJhbGc...",
"refresh_token": "eyJhbGc...",
"expires_in": 7200
}
```
**错误**
- `400 INVALID_INPUT`: 参数缺失
- `401 INVALID_CREDENTIALS`: 用户名或密码错误
#### `POST /api/auth/refresh` — 刷新 Access Token
**请求头**
```
Authorization: Bearer <refresh_token>
```
**响应**200 OK
```json
{
"access_token": "eyJhbGc...",
"refresh_token": "eyJhbGc...",
"expires_in": 7200
}
```
**错误**
- `401 INVALID_TOKEN`: Refresh Token 无效或过期
#### `POST /api/auth/logout` — 用户登出
**请求头**
```
Authorization: Bearer <access_token>
```
**响应**200 OK
```json
{
"message": "Logged out successfully"
}
```
### 2.3 对话管理 API
所有端点均需 JWT 认证(`Authorization: Bearer <access_token>`)。
#### `GET /api/conversations` — 获取对话列表
**查询参数**
- `page`: 页码,从 1 开始,默认 1
- `page_size`: 每页条数,默认 20最大 100
**响应**200 OK
```json
{
"conversations": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "关于植物的对话",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T11:45:00Z",
"message_count": 12
}
],
"total": 42,
"page": 1,
"page_size": 20
}
```
#### `POST /api/conversations` — 创建新对话
**请求**
```json
{
"title": "新的对话"
}
```
**响应**201 Created
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "新的对话",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"message_count": 0
}
```
#### `GET /api/conversations/:id` — 获取对话详情
**响应**200 OK
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "关于植物的对话",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T11:45:00Z",
"message_count": 12
}
```
**错误**
- `404 NOT_FOUND`: 对话不存在或无权访问
#### `PATCH /api/conversations/:id` — 更新对话
**请求**
```json
{
"title": "修改后的标题"
}
```
**响应**200 OK
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "修改后的标题",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T12:00:00Z",
"message_count": 12
}
```
#### `DELETE /api/conversations/:id` — 删除对话
**响应**204 No Content无响应体
**错误**
- `404 NOT_FOUND`: 对话不存在或无权访问
#### `GET /api/conversations/:id/messages` — 获取对话消息
**查询参数**
- `page`: 页码,从 1 开始,默认 1
- `page_size`: 每页条数,默认 50最大 100
**响应**200 OK
```json
{
"messages": [
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"conversation_id": "550e8400-e29b-41d4-a716-446655440000",
"role": "user",
"content": "这是什么植物?",
"image_url": "/api/images/abc123.jpg",
"created_at": "2024-01-15T10:30:00Z"
},
{
"id": "770e8400-e29b-41d4-a716-446655440000",
"conversation_id": "550e8400-e29b-41d4-a716-446655440000",
"role": "assistant",
"content": "这是一株向日葵...",
"created_at": "2024-01-15T10:30:15Z"
}
],
"total": 12,
"page": 1,
"page_size": 50
}
```
**消息字段说明**
- `role`: `"user"``"assistant"`
- `image_url`: 仅 `user` 消息可能包含,指向存储的图像
- `content`: 消息文本内容
---
## 三、错误码表
所有错误均使用以下格式:
```json
{
"code": "ERROR_CODE",
"message": "Human-readable error description",
"details": {}
}
```
### WebSocket 错误码
| 错误码 | 说明 | HTTP 状态码(若适用)|
|--------|------|---------------------|
| `INVALID_MESSAGE` | 消息格式错误或缺少必填字段 | - |
| `SESSION_NOT_FOUND` | 会话不存在 | - |
| `RATE_LIMITED` | 请求频率过高 | 429 |
| `IMAGE_TOO_LARGE` | 图像超过大小限制5MB| - |
| `AUDIO_TOO_LARGE` | 音频超过大小限制10MB| - |
| `STT_ERROR` | 语音识别服务错误 | - |
| `LLM_ERROR` | LLM 服务错误 | - |
| `LLM_TIMEOUT` | LLM 响应超时60 秒)| - |
| `TTS_ERROR` | 语音合成服务错误 | - |
| `CONCURRENT_REQUEST` | 同一连接已有进行中的请求 | - |
| `INTERNAL_ERROR` | 服务器内部错误 | 500 |
### REST API 错误码
| 错误码 | 说明 | HTTP 状态码 |
|--------|------|-------------|
| `INVALID_INPUT` | 请求参数验证失败 | 400 |
| `INVALID_TOKEN` | JWT Token 无效或过期 | 401 |
| `INVALID_CREDENTIALS` | 用户名或密码错误 | 401 |
| `UNAUTHORIZED` | 未认证或认证失败 | 401 |
| `FORBIDDEN` | 无权访问资源 | 403 |
| `NOT_FOUND` | 资源不存在 | 404 |
| `USER_EXISTS` | 用户名或邮箱已存在 | 409 |
| `RATE_LIMITED` | 请求频率过高 | 429 |
| `INTERNAL_ERROR` | 服务器内部错误 | 500 |
| `SERVICE_UNAVAILABLE` | 依赖服务不可用 | 503 |
---
## 四、数据模型
### 用户User
```typescript
interface User {
id: string; // UUID
username: string; // 用户名,唯一
email: string; // 邮箱,唯一
created_at: string; // ISO 8601 时间戳
updated_at: string; // ISO 8601 时间戳
}
```
### 对话Conversation
```typescript
interface Conversation {
id: string; // UUID
user_id: string; // 所属用户 ID
title: string; // 对话标题
created_at: string; // ISO 8601 时间戳
updated_at: string; // ISO 8601 时间戳
message_count: number; // 消息数量
}
```
### 消息Message
```typescript
interface Message {
id: string; // UUID
conversation_id: string; // 所属对话 ID
role: "user" | "assistant";
content: string; // 消息文本内容
image_url?: string; // 可选,用户消息的关联图像 URL
created_at: string; // ISO 8601 时间戳
}
```
### JWT Token 载荷
**Access Token**(有效期 120 分钟):
```json
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "alice",
"type": "access",
"exp": 1705318200,
"iat": 1705311000
}
```
**Refresh Token**(有效期 7 天):
```json
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "refresh",
"exp": 1705915800,
"iat": 1705311000
}
```
---
## 附录:版本历史
- **v1.0**2024-01-15初始版本定义 WebSocket 协议和 REST API
- **v1.1**2024-01-20新增文本输入模式`query.text` 字段)
- **v1.2**2024-01-25新增场景模式配置`config.scenario` 字段)
- **v2.0**2026-06-21重构为纯接口契约规范移除实现细节