v1

package
v0.0.0-...-8902c11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: AGPL-3.0 Imports: 27 Imported by: 0

Documentation

Index

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

func (h *TaskHandler) Delete(c *web.Context, req domain.IDReq[uuid.UUID]) error

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

func (h *TaskHandler) Info(c *web.Context, req domain.IDReq[uuid.UUID]) error

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

func (h *TaskHandler) PublicStream(c *web.Context, req domain.IDReq[uuid.UUID]) error

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

func (h *TaskHandler) Stop(c *web.Context, req domain.IDReq[uuid.UUID]) error

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]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL