feat(ai): 设置里加「AI 生成队列」,看得见后台在算什么

队列本来是完全不可见的:页面上一句「排队生成中」说不出自己是下一个、第二十
个,还是已经放弃了——网关挂掉的时候,「还在生成」和「永远不会好」长得一模
一样。今天排查就是这么排的。

- GET /analysis/insight/queue 返回队列(running 在前,其次按优先级和年龄,
  和 worker 实际取任务的顺序一致)、已生成的解读、scope 名到中文标签的映射
  (前端不必再抄一份),以及消费者的限流配置
- POST /analysis/insight/queue/retry:手动把「已放弃」的重新排队,不等冷却。
  自动重试要等冷却是为了不去捶一个正在抽风的上游;人按下重试是他自己判断值得
  再试一次
- 页面在 设置 → AI 生成队列。插队的任务标「插队」——这是整个界面最想让人看见
  的一件事:为什么是它排在最前面
- 「已生成」单独列:队列空了意味着「没有待办」,不是「什么都没生成过」,
  没有这一节这两件事在界面上没法区分

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ericwyuan
2026-09-01 16:02:05 +08:00
parent a2d6a5c57f
commit 05f55e695b
9 changed files with 551 additions and 6 deletions

View File

@@ -168,8 +168,23 @@ def insight():
@bp.route("/insight/queue", methods=["GET"])
@require_auth
def insight_queue():
"""What the coach still has to generate — for a progress indicator."""
"""The coach's queue: what is running, what is waiting, what gave up.
Also lists what has already been generated, because "nothing queued" and
"nothing generated" look the same from the queue alone and mean opposite
things.
"""
return jsonify({
"pending": ai_jobs.pending_count(g.user_id),
"enabled": ai_jobs.ENABLED,
"jobs": ai_jobs.list_jobs(g.user_id),
"insights": analysis_svc.list_insights(g.user_id),
"scopes": {name: scope.label for name, scope in scopes.SCOPES.items()},
"settings": ai_jobs.settings(),
})
@bp.route("/insight/queue/retry", methods=["POST"])
@require_auth
def insight_queue_retry():
"""Re-queue everything that gave up, without waiting out the cooldown."""
return jsonify({"pending": ai_jobs.retry_failed(g.user_id)})