Documentation
¶
Index ¶
- type TaskHandler
- func (h *TaskHandler) Control(c *web.Context, req domain.TaskControlReq) error
- func (h *TaskHandler) Create(c *web.Context, req domain.CreateTaskReq) error
- func (h *TaskHandler) Delete(c *web.Context, req domain.IDReq[uuid.UUID]) error
- func (h *TaskHandler) Info(c *web.Context, req domain.IDReq[uuid.UUID]) error
- func (h *TaskHandler) List(c *web.Context, req domain.TaskListReq) error
- func (h *TaskHandler) PublicStream(c *web.Context, req domain.IDReq[uuid.UUID]) error
- func (h *TaskHandler) SpeechToText(c *web.Context) error
- func (h *TaskHandler) SpeechToTextStream(c *web.Context) error
- func (h *TaskHandler) Stop(c *web.Context, req domain.IDReq[uuid.UUID]) error
- func (h *TaskHandler) Stream(c *web.Context, req domain.TaskStreamReq) error
- func (h *TaskHandler) TaskTurns(c *web.Context, req domain.TaskRoundsReq) error
- func (h *TaskHandler) TaskUserInputs(c *web.Context, req domain.TaskUserInputsReq) error
- func (h *TaskHandler) Update(c *web.Context, req domain.UpdateTaskReq) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type TaskHandler ¶
type TaskHandler struct {
// contains filtered or unexported fields
}
TaskHandler 任务处理器
func NewTaskHandler ¶
func NewTaskHandler(i *do.Injector) (*TaskHandler, error)
NewTaskHandler 创建任务处理器
func (*TaskHandler) Control ¶
func (h *TaskHandler) Control(c *web.Context, req domain.TaskControlReq) error
Control 任务控制流 WebSocket 端点
@Summary 任务控制流 WebSocket
@Description 数据格式约定:当前仅支持文本帧透传。服务端将 Agent 的原始文本数据包装为如下结构返回给前端(对应 domain.TaskStream):
@Description ```json
@Description { "type": "string", "data": "string", "kind": "string", "timestamp": 0 }
@Description ```
@Description 独立于 stream 的长生命周期 WebSocket 连接,用于处理 call/call-response(文件浏览、diff 查看等同步请求)。
@Description task 结束后连接不断开,仍可用于文件操作。
@Description 支持同一 taskID 多 tab 并发连接。
@Description
@Description ## 上行消息
@Description
@Description ### Type=call, Kind=repo_file_diff — 获取文件 diff
@Description 请求 Data:
@Description ```json
@Description {"request_id":"string","path":"string","unified":true,"context_lines":3}
@Description ```
@Description 响应 Data:
@Description ```json
@Description {"request_id":"string","path":"string","diff":"string","success":true,"error":"string?"}
@Description ```
@Description
@Description ### Type=call, Kind=repo_file_list — 列出目录文件
@Description 请求 Data:
@Description ```json
@Description {"request_id":"string","path":"string","glob_pattern":"string?","include_hidden":false}
@Description ```
@Description 响应 Data:
@Description ```json
@Description {"request_id":"string","path":"string","files":[{"name":"string","path":"string","entry_mode":0,"size":0,"modified_at":0}],"success":true,"error":"string?"}
@Description ```
@Description
@Description ### Type=call, Kind=repo_read_file — 读取文件内容
@Description 请求 Data:
@Description ```json
@Description {"request_id":"string","path":"string","offset":0,"length":0}
@Description ```
@Description 响应 Data:
@Description ```json
@Description {"request_id":"string","path":"string","content":"bytes","total_size":0,"offset":0,"length":0,"is_truncated":false,"success":true,"error":"string?"}
@Description ```
@Description
@Description ### Type=call, Kind=repo_file_changes — 查询变更文件列表
@Description 请求 Data:
@Description ```json
@Description {"request_id":"string"}
@Description ```
@Description 响应 Data:
@Description ```json
@Description {"request_id":"string","changes":[{"path":"string","status":"string","additions":0,"deletions":0,"old_path":"string?"}],"branch":"string?","commit_hash":"string?","success":true,"error":"string?"}
@Description ```
@Description
@Description ### Type=call, Kind=port_forward_list — 获取端口转发列表
@Description 请求 Data:
@Description ```json
@Description {"request_id":"string"}
@Description ```
@Description 响应 Data:
@Description ```json
@Description {"request_id":"string","ports":[{"port":0,"status":"string","process":"string","forward_id":"string?","access_url":"string?","label":"string?","error_message":"string?","whitelist_ips":["string"]}]}
@Description ```
@Description
@Description ### Type=call, Kind=restart — 重启任务
@Description 请求 Data:
@Description ```json
@Description {"request_id":"string","load_session":true}
@Description ```
@Description 响应 Data:
@Description ```json
@Description {"id":"uuid","request_id":"string?","success":true,"message":"string","session_id":"string"}
@Description ```
@Description
@Description ### Type=call, Kind=switch_model — 切换运行中任务模型
@Description 请求 Data:
@Description ```json
@Description {"request_id":"string","model_id":"uuid","load_session":true}
@Description ```
@Description 响应 Data:
@Description ```json
@Description {"id":"uuid","request_id":"string?","success":true,"message":"string","session_id":"string","model":{}}
@Description ```
@Description
@Description ### Type=sync-my-ip — 同步 Web 客户端真实 IP
@Description 请求 Data:
@Description ```json
@Description {"client_ip":"string"}
@Description ```
@Description
@Description ## 下行消息
@Description
@Description - Type=call-response: 同步请求响应(Kind 与请求一致)。失败时 Data 为:
@Description ```json
@Description {"request_id":"string","success":false,"error":"string"}
@Description ```
@Description - Type=task-event: 任务事件(从 TaskLive 订阅转发)
@Description - Type=ping: 心跳(无 Data)
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param id query string true "任务 ID"
@Success 200 {object} web.Resp{} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks/control [get]
func (*TaskHandler) Create ¶
func (h *TaskHandler) Create(c *web.Context, req domain.CreateTaskReq) error
Create 创建任务
@Summary 创建任务
@Description 创建任务
@Description `attachments` 为可选附件列表,最多 10 个;每项包含 `url` 和 `filename`,URL 需要匹配后端配置的附件白名单前缀。创建任务后,首轮 user-input 日志会按 `{ "content": "base64文本", "attachments": [] }` 结构返回。
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param param body domain.CreateTaskReq true "请求参数"
@Success 200 {object} web.Resp{data=domain.ProjectTask} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks [post]
func (*TaskHandler) Delete ¶
Delete 删除任务
@Summary 删除任务
@Description 删除任务。任务处于运行中(pending/processing)或虚拟机仍在线时不允许删除。
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param id path string true "任务 ID"
@Success 200 {object} web.Resp{} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks/{id} [delete]
func (*TaskHandler) Info ¶
Info 任务详情
@Summary 任务详情
@Description 任务详情
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param id path string true "任务 ID"
@Success 200 {object} web.Resp{data=domain.Task} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks/{id} [get]
func (*TaskHandler) List ¶
func (h *TaskHandler) List(c *web.Context, req domain.TaskListReq) error
List 任务列表
@Summary 任务列表
@Description 获取属于该用户的所有任务,仅支持普通分页
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param req query domain.TaskListReq true "分页参数(page/size)"
@Success 200 {object} web.Resp{data=domain.ListTaskResp} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks [get]
func (*TaskHandler) PublicStream ¶
PublicStream 公开的任务数据流 WebSocket
@Summary 公开的任务数据流 WebSocket
@Description 数据格式约定参考任务数据流 WebSocket 接口
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param id query string true "任务 ID"
@Success 200 {object} web.Resp{} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks/public-stream [get]
func (*TaskHandler) SpeechToText ¶
func (h *TaskHandler) SpeechToText(c *web.Context) error
SpeechToText 语音转文字
@Summary 语音转文字
@Description 上传音频数据进行语音识别,返回Server-Sent Events流式文字结果。响应格式为SSE,每个事件包含event和data字段。
@Tags 【用户】任务管理
@Accept application/octet-stream
@Produce text/event-stream
@Security MonkeyCodeAIAuth
@Success 200 {object} domain.SpeechRecognitionEvent "Server-Sent Events流,包含recognition(识别结果)、end(结束)和error(错误)事件"
@Failure 400 {object} web.Resp "参数错误"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks/speech-to-text [post]
func (*TaskHandler) SpeechToTextStream ¶
func (h *TaskHandler) SpeechToTextStream(c *web.Context) error
SpeechToTextStream 实时语音转写(WebSocket 流式)
@Summary 实时语音转写(WebSocket 流式)
@Description 通过 WebSocket 上传实时音频流并实时返回识别结果。
@Description 后端使用豆包流式语音识别 2.0 (bigmodel_async)。完整协议见 docs/speech-to-text-stream.md。
@Description
@Description ## 帧类型约定
@Description | 方向 | 帧类型 | 用途 |
@Description |---|---|---|
@Description | C → S | Text(JSON) | 控制消息:start / stop |
@Description | C → S | Binary | 音频字节流,服务端封装为豆包帧透传 |
@Description | S → C | Text(JSON) | 所有事件(ready / partial / final / done / error) |
@Description
@Description ## 客户端 → 服务端
@Description
@Description ### 1) start (第一帧必须是它)
@Description ```json
@Description {
@Description "type": "start",
@Description "format": "pcm",
@Description "disfluency": false
@Description }
@Description ```
@Description - `format` 可选,默认 `pcm`。支持 `pcm` / `wav` / `ogg` / `mp3`,单声道、16-bit、采样率固定 16000Hz
@Description - `pcm` / `wav` 内部音频流必须是 `pcm_s16le`;`ogg` 必须为 `opus` 编码;`mp3` 由远端解码
@Description - `disfluency` 可选,默认 `false`。`true` 时启用语义顺滑(过滤"嗯/啊"等口头禅、语义重复词)
@Description - 服务端校验通过 → 与豆包建立 WSS → 收到首个响应后向客户端下发 `ready` 事件(带 `logid`)
@Description - **客户端必须在收到 `ready` 之后才能发 Binary 音频帧**
@Description - 以下能力默认开启:中间结果(双向流式天然有)、标点预测、ITN(中文数字转阿拉伯数字)
@Description
@Description ### 2) Binary 音频帧
@Description `ready` 之后,客户端持续发 Binary 帧。**建议每帧 200ms**(豆包推荐值,过碎会影响性能)。
@Description
@Description ### 3) stop (主动结束)
@Description ```json
@Description { "type": "stop" }
@Description ```
@Description 服务端收到后向豆包发送"最后一包"标志,等待豆包最终响应后下发 `done` 事件并关闭 WS。客户端直接 close WS 亦可。
@Description
@Description ## 服务端 → 客户端
@Description
@Description 所有事件统一外层结构:
@Description ```json
@Description { "type": "<event_type>", "timestamp": 1733299200000, ... }
@Description ```
@Description
@Description ### ready — 远端已就绪
@Description ```json
@Description { "type": "ready", "logid": "202407261553070FACFE6D19421815D605", "timestamp": 1733299200000 }
@Description ```
@Description 仅推送一次。客户端收到后开始发 Binary 音频帧。`logid` 是豆包返回的 `X-Tt-Logid`,排障必备,
@Description 建议前端在 session 期间一直打印它,跟后续 error 事件可以关联。
@Description
@Description ### partial — 中间结果(实时滚动,会反复推送)
@Description ```json
@Description { "type": "partial", "index": 1, "text": "今天天气真", "timestamp": 1733299201500 }
@Description ```
@Description - 同一 `index` 的 `partial` 会反复推送,`text` 通常逐渐变长
@Description - **客户端必须用 `text` 覆盖该句显示内容,不要追加**
@Description
@Description ### final — 一句话定稿
@Description ```json
@Description { "type": "final", "index": 1, "text": "今天天气真不错。", "timestamp": 1733299202800 }
@Description ```
@Description - 该句识别完成、内容固化,后续不会再变;客户端用 `text` 覆盖 `index` 对应位置
@Description - `final` 之后可能立刻有下一句的 `partial`(`index+1`)
@Description
@Description ### done — 整个 session 结束
@Description ```json
@Description { "type": "done", "logid": "...", "timestamp": 1733299210000 }
@Description ```
@Description - 服务端已收到豆包最后一包响应、释放资源,即将关闭 WS;`done` 之后不会再有任何事件
@Description
@Description ### error — 错误
@Description 豆包远端错误:
@Description ```json
@Description {
@Description "type": "error",
@Description "logid": "202407261553070FACFE6D19421815D605",
@Description "error": {
@Description "code": 45000001,
@Description "message": "请求参数无效",
@Description "request_id": "67ee89ba-7050-4c04-a3d7-ac61a63499b3",
@Description "logid": "202407261553070FACFE6D19421815D605"
@Description },
@Description "timestamp": 1733299205000
@Description }
@Description ```
@Description 本服务前置校验错误(远端连接前):`code=0`,在 `message` 描述原因:
@Description ```json
@Description {
@Description "type": "error",
@Description "error": { "code": 0, "message": "first message must be a 'start' control message" },
@Description "timestamp": 1733299205000
@Description }
@Description ```
@Description
@Description ## 豆包常见错误码速查
@Description | code | 含义 | 典型原因 |
@Description |---|---|---|
@Description | `45000001` | 请求参数无效 | 缺字段 / 字段值无效 / 重复请求 |
@Description | `45000002` | 空音频 | 录音未采集到声音 |
@Description | `45000081` | 等包超时 | 前端没在期限内连续发送音频帧 |
@Description | `45000151` | 音频格式不正确 | `format` 与实际音频不匹配 |
@Description | `55000031` | 服务器繁忙 | 退避重试 |
@Description | `550xxxxx` | 服务内部错误 | 直接重试,持续失败时带 `logid` 联系运维 |
@Description
@Description ## 时序示例
@Description ```
@Description C → S: WS upgrade (带鉴权)
@Description C → S: {"type":"start","format":"pcm"}
@Description S → C: {"type":"ready","logid":"..."}
@Description C → S: <binary 200ms> <binary 200ms> ...
@Description S → C: {"type":"partial","index":1,"text":"今天"}
@Description S → C: {"type":"partial","index":1,"text":"今天天气真"}
@Description S → C: {"type":"final","index":1,"text":"今天天气真不错。"}
@Description C → S: {"type":"stop"}
@Description S → C: {"type":"done","logid":"..."}
@Description WS close
@Description ```
@Description
@Description ## 与 POST /speech-to-text 的差异
@Description - POST /speech-to-text:整段录音 → SSE 单段结果,适合短语音 ≤60s
@Description - 本接口:WS 双向实时流,支持长语音、句级 final、可被打断,适合 Web/移动端边说边显示
@Description
@Tags 【用户】任务管理
@Security MonkeyCodeAIAuth
@Param start body domain.SpeechStreamStartReq false "[WS 协议] 客户端连接后首帧 JSON Text 控制消息 schema;不是 HTTP body,仅供前端代码生成 TS 类型,实际通过 WS Text 帧发送"
@Success 101 {object} domain.SpeechStreamEvent "WebSocket 升级成功;此后通过 WS 帧通信,事件结构见上方说明"
@Failure 401 {object} web.Resp "未授权"
@Failure 500 {object} web.Resp "服务器内部错误(ASR 服务未配置等)"
@Router /api/v1/users/tasks/speech-to-text-stream [get]
func (*TaskHandler) Stop ¶
Stop 停止任务
@Summary 停止任务
@Description 停止任务
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param id body domain.IDReq[uuid.UUID] true "任务 id"
@Success 200 {object} web.Resp{} "成功回包"
@Router /api/v1/users/tasks/stop [put]
func (*TaskHandler) Stream ¶
func (h *TaskHandler) Stream(c *web.Context, req domain.TaskStreamReq) error
Stream 任务数据流 WebSocket
@Summary 任务数据流 WebSocket
@Description 功能定位:该接口通过 WebSocket 转发任务运行数据。任务对话继续输入使用 `type=user-input`。
@Description 数据格式约定:当前仅支持文本帧透传。服务端将 Agent 的原始文本数据包装为如下结构返回给前端(对应 domain.TaskStream):
@Description ```json
@Description { "type": "string", "data": "string", "kind": "string", "timestamp": 0 }
@Description ```
@Description user-input 上行新格式:
@Description ```json
@Description { "type": "user-input", "data": "{\"content\":\"57un57ut5aSE55CG6L+Z5Liq6Zeu6aKY\",\"attachments\":[{\"url\":\"https://example-bucket.oss-cn-hangzhou.aliyuncs.com/temp/a.txt\",\"filename\":\"a.txt\"}]}" }
@Description ```
@Description user-input 上行旧格式仍兼容:
@Description ```json
@Description { "type": "user-input", "data": "继续处理这个问题" }
@Description ```
@Description user-input 下行和历史返回统一使用新 JSON payload 字符串:
@Description ```json
@Description { "type": "user-input", "data": "{\"content\":\"57un57ut5aSE55CG6L+Z5Liq6Zeu6aKY\",\"attachments\":[]}", "timestamp": 0 }
@Description ```
@Description `attachments` 为可选附件列表,最多 10 个;每项包含 `url` 和 `filename`,URL 需要匹配后端配置的附件白名单前缀。
@Description type 字段说明:
@Description - task-started: 本轮任务启动
@Description - task-ended: 本轮任务结束
@Description - task-error: 本轮任务发生错误
@Description - task-running: 任务正在运行
@Description - task-event: 任务临时事件, 不持久化
@Description - file-change: 文件变动事件
@Description - permission-resp: 用户的权限响应
@Description - auto-approve: 开启自动批准
@Description - disable-auto-approve: 关闭自动批准
@Description - user-input: 用户输入
@Description - user-cancel: 取消当前操作,不会终止任务
@Description - reply-question: 回复 AI 的提问
@Description - cursor: 历史游标,用于通过 /rounds 接口加载更早的轮次
@Description
@Description cursor 消息结构:
@Description ```json
@Description { "type": "cursor", "data": { "cursor": "<nextCursor>", "has_more": true }, "timestamp": 0 }
@Description ```
@Description - cursor: 当前分页游标,作为 GET /rounds 接口的 cursor 参数向前翻页
@Description - has_more: 是否存在更早的轮次。为 false 时表示当前轮次即为第一轮,无需再翻页
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param id query string true "任务 ID"
@Param mode query string false "模式:new(等待用户输入)|attach(仅拉取当前轮次),默认 new"
@Success 200 {object} web.Resp{} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks/stream [get]
func (*TaskHandler) TaskTurns ¶
func (h *TaskHandler) TaskTurns(c *web.Context, req domain.TaskRoundsReq) error
TaskTurns 查询任务历史轮次(原始 TaskChunk,双向翻页)
@Summary 查询任务历史轮次
@Description 根据 cursor 翻页查询任务的历史轮次。limit 为轮次数(非条目数),
@Description limit=2 表示返回 2 轮的完整消息。direction=backward(默认)从 cursor 往更早翻,轮间倒序(最新轮在前);
@Description direction=forward 往更新翻,轮间正序(最旧轮在前);轮内消息始终按时间正序。
@Description 即响应中最后一轮总是与 next_cursor 相邻的那一轮。前端应以每条 chunk 的 seq 做分组排序,不依赖数组顺序。
@Description 日志存储为 ClickHouse 时 cursor 即轮次号 seq,可配合 inclusive=true 实现"跳转到第 seq 轮";
@Description Loki 仅支持 backward 且不支持 inclusive,违反时返回 err-task-rounds-direction-unsupported。
@Description 返回的 user-input.data 统一为 JSON payload 字符串,例如 `{"content":"57un57ut5aSE55CG","attachments":[]}`;content 为用户输入文本的 base64 编码,旧历史裸文本也会按该结构包装返回。
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param id query string true "任务 ID"
@Param cursor query string false "分页游标(ClickHouse 下即轮次号 seq)"
@Param limit query int false "轮次数(默认 2,上限 10)"
@Param direction query string false "翻页方向:backward(默认)/ forward"
@Param inclusive query bool false "是否包含 cursor 指向的那一轮(跳转定位用)"
@Success 200 {object} web.Resp{data=domain.TaskRoundsResp} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks/rounds [get]
func (*TaskHandler) TaskUserInputs ¶
func (h *TaskHandler) TaskUserInputs(c *web.Context, req domain.TaskUserInputsReq) error
TaskUserInputs 查询任务的所有 user-input 列表(侧边栏快速跳转用)
@Summary 查询任务用户输入列表
@Description 查询任务的 user-input 消息(倒序,最新在前),根据 cursor 向更早翻页,用于聊天页侧边栏快速跳转到指定一轮对话。
@Description 单条返回的 id 形如 `user-input-{timestamp_ns}`,与前端聊天页消息列表中的 `data-message-id` 对齐。
@Description content 已解码为明文,超出 500 字符会截断并将 truncated 置为 true。
@Description 日志存储为 ClickHouse 时每条带 seq(轮次号),可作为 /rounds 接口的 cursor 配合 inclusive=true 跳转定位;Loki 下无 seq。
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param id query string true "任务 ID"
@Param cursor query string false "分页游标,第一页留空"
@Param limit query int false "返回条数(默认 20,上限 100)"
@Success 200 {object} web.Resp{data=domain.TaskUserInputsResp} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks/user-inputs [get]
func (*TaskHandler) Update ¶
func (h *TaskHandler) Update(c *web.Context, req domain.UpdateTaskReq) error
Update 更新任务
@Summary 更新任务
@Description 更新任务信息(如标题)
@Tags 【用户】任务管理
@Accept json
@Produce json
@Security MonkeyCodeAIAuth
@Param id path string true "任务 ID"
@Param param body domain.UpdateTaskReq true "请求参数"
@Success 200 {object} web.Resp{} "成功"
@Failure 500 {object} web.Resp "服务器内部错误"
@Router /api/v1/users/tasks/{id} [put]
Click to show internal directories.
Click to hide internal directories.