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:
@@ -448,6 +448,38 @@ export type InsightScope =
|
||||
| 'health' | 'sleep' | 'exercise' | 'trends' | 'daily'
|
||||
| 'body' | 'race' | 'bodyAge' | 'challenges' | 'activity';
|
||||
|
||||
/** One entry in the coach's work queue. */
|
||||
export interface AiJob {
|
||||
kind: string;
|
||||
subject: string;
|
||||
status: 'pending' | 'running' | 'failed' | 'done';
|
||||
priority: number;
|
||||
/** Queued because a screen is open, rather than as post-sync backfill. */
|
||||
interactive: boolean;
|
||||
attempts: number;
|
||||
maxAttempts: number;
|
||||
error: string | null;
|
||||
updatedAt: string | null;
|
||||
}
|
||||
|
||||
export interface AiQueue {
|
||||
pending: number;
|
||||
jobs: AiJob[];
|
||||
insights: Array<{
|
||||
kind: string; subject: string; model: string | null;
|
||||
upstream: string | null; generatedAt: string | null;
|
||||
}>;
|
||||
/** Scope name -> its Chinese label, so the UI need not duplicate the list. */
|
||||
scopes: Record<string, string>;
|
||||
settings: {
|
||||
enabled: boolean;
|
||||
concurrency: number;
|
||||
gapSeconds: number;
|
||||
maxAttempts: number;
|
||||
retryAfterSeconds: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CopilotTurn {
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
@@ -818,10 +850,16 @@ class ApiClient {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** How much the coach still has to generate — for a progress hint. */
|
||||
/** The coach's queue: running, waiting, given up, and already generated. */
|
||||
async getInsightQueue() {
|
||||
const { data } = await this.client.get<{ pending: number; enabled: boolean }>(
|
||||
'/analysis/insight/queue'
|
||||
const { data } = await this.client.get<AiQueue>('/analysis/insight/queue');
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Re-queue everything that gave up, without waiting out the cooldown. */
|
||||
async retryInsightQueue() {
|
||||
const { data } = await this.client.post<{ pending: number }>(
|
||||
'/analysis/insight/queue/retry'
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user