fix(organize): 展示智能体处理状态并刷新恢复结果
This commit is contained in:
1 parent
86351f20f3
commit
b306d27d08
9 files changed
+172
-12
No files matched your search
@@ -1,8 +1,15 @@
|
||||
import type { Decision, EpisodeMappingPreview } from "@umt/contracts";
|
||||
import { createElement } from "react";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { MemoryRouter } from "react-router";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { agentSimulationText, EpisodeDecisionBody, episodeDecisionView, simulationText } from "./decision.tsx";
|
||||
import {
|
||||
agentSimulationText,
|
||||
EpisodeDecisionBody,
|
||||
EpisodeDecisionStatus,
|
||||
episodeDecisionView,
|
||||
simulationText,
|
||||
} from "./decision.tsx";
|
||||
|
||||
const ENTITY = "7d3f7f5e-8a8c-4b8e-9f0a-1b2c3d4e5f60";
|
||||
const decision = (over: Partial<Decision> = {}): Decision =>
|
||||
@@ -155,3 +162,27 @@ describe("待处理「集数待确认」", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("季集映射调查状态", () => {
|
||||
const renderStatus = (d: Decision) =>
|
||||
renderToStaticMarkup(createElement(MemoryRouter, null, createElement(EpisodeDecisionStatus, { d })));
|
||||
it("调查中显示状态与已有会话入口,没有会话也能显示进度", () => {
|
||||
const html = renderStatus(decision({ status: "investigating", agentRunId: "agent-run" }));
|
||||
expect(html).toContain("智能体正在核对季集");
|
||||
expect(html).toContain("/console/agent/agent-run");
|
||||
expect(renderStatus(decision({ status: "investigating", agentRunId: null }))).toContain("智能体正在核对季集");
|
||||
});
|
||||
it("交还人工显示恢复建议,并保留排查入口", () => {
|
||||
const html = renderStatus(
|
||||
decision({
|
||||
status: "awaiting_user",
|
||||
agentRunId: "agent-run",
|
||||
evidence: { autoRecover: { suggestion: "请补充资源站分季详情" } },
|
||||
}),
|
||||
);
|
||||
expect(html).toContain("需要你确认");
|
||||
expect(html).toContain("请补充资源站分季详情");
|
||||
expect(html).toContain("查看排查过程");
|
||||
expect(renderStatus(decision())).toBe("");
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
EpisodeResultBrief,
|
||||
EpisodeSimulationSummary,
|
||||
} from "@umt/contracts";
|
||||
import { BellOff, Bot, Check, PencilLine } from "lucide-react";
|
||||
import { BellOff, Bot, Check, LoaderCircle, PencilLine } from "lucide-react";
|
||||
import { useId, useState } from "react";
|
||||
import { Link } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
@@ -14,9 +14,11 @@ import { useResolveDecision } from "~/api/decisions.ts";
|
||||
import { useEntityWords, useEpisodeMappingPreview } from "~/api/episode-mapping.ts";
|
||||
import { ConfirmDialog } from "~/components/console/confirm-dialog.tsx";
|
||||
import { consolePath } from "~/components/console/nav.ts";
|
||||
import { StatusBadge } from "~/components/console/status-badge.tsx";
|
||||
import { Button } from "~/components/ui/button.tsx";
|
||||
import { Switch } from "~/components/ui/switch.tsx";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
import { humanizeReason } from "~/lib/reasons.ts";
|
||||
import { canonicalText, episodeCode, mappingHref, scopeText, sourceText } from "./model.ts";
|
||||
|
||||
// 作品页集数映射里的「集数待确认」(episode_mapping 内部事项):来源季集、原因、建议的那一行作品识别词与它的模拟结果;
|
||||
@@ -234,6 +236,33 @@ function IgnoreButton({ d, view }: { d: Decision; view: EpisodeDecisionView }) {
|
||||
);
|
||||
}
|
||||
|
||||
/** 在原来的映射事项里显示智能体进度和交还人工的原因。 */
|
||||
export function EpisodeDecisionStatus({ d }: { d: Decision }) {
|
||||
const investigating = d.status === "investigating";
|
||||
const awaiting = d.status === "awaiting_user";
|
||||
if (!investigating && !awaiting) return null;
|
||||
const recovery = d.evidence.autoRecover as { suggestion?: unknown } | undefined;
|
||||
const suggestion = awaiting && typeof recovery?.suggestion === "string" ? recovery.suggestion : d.summary;
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<StatusBadge tone={awaiting ? "warning" : "neutral"}>
|
||||
{investigating ? "智能体正在核对季集" : "需要你确认"}
|
||||
</StatusBadge>
|
||||
{investigating ? <LoaderCircle className="h-3.5 w-3.5 animate-spin" aria-hidden /> : null}
|
||||
{d.agentRunId ? (
|
||||
<Button asChild variant="ghost" size="sm">
|
||||
<Link to={consolePath("agent", d.agentRunId)}>查看排查过程</Link>
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
{suggestion ? (
|
||||
<p className="break-words text-caption text-muted-foreground">{humanizeReason(suggestion)}</p>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 集数待确认事项的证据与操作。onEdit 给了(作品页集数映射里)时「修改」把这一行放进编辑器草稿,否则跳到作品页。
|
||||
*/
|
||||
@@ -260,8 +289,9 @@ export function EpisodeDecision({ d, open, onEdit }: { d: Decision; open: boolea
|
||||
const modifyHref = view.entityId ? mappingHref(consolePath("library", view.entityId), editLine ?? undefined) : null;
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<EpisodeDecisionStatus d={d} />
|
||||
<EpisodeDecisionBody view={view} simulation={sug ? simulationText(preview.data) : null} />
|
||||
{open ? (
|
||||
{open && d.status !== "investigating" ? (
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{view.confirmAgent && view.agentSuggestion ? (
|
||||
<ConfirmDialog
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import type { Decision } from "@umt/contracts";
|
||||
import { useDecisions } from "~/api/decisions.ts";
|
||||
import { humanizeReason } from "~/lib/reasons.ts";
|
||||
import { useSse } from "~/lib/sse.ts";
|
||||
import { EpisodeDecision } from "./decision.tsx";
|
||||
|
||||
// 集数待确认的原因(2026-10-06 去掉待处理中心后):不再作为逐条要处理的条目,
|
||||
@@ -16,6 +18,12 @@ function entityOf(d: Decision): string | null {
|
||||
|
||||
/** 某作品(可选按来源季)未关闭的集数待确认事项 */
|
||||
export function useEpisodeIssues(entityId: string | null | undefined, season?: number | null) {
|
||||
const qc = useQueryClient();
|
||||
useSse(["decision.created", "decision.updated"], () => {
|
||||
if (!entityId) return;
|
||||
void qc.invalidateQueries({ queryKey: ["decisions"] });
|
||||
void qc.invalidateQueries({ queryKey: ["episode-mapping"] });
|
||||
});
|
||||
const q = useDecisions({ kind: "episode_mapping" }, !!entityId);
|
||||
const items = (q.data?.pages.flatMap((p) => p.items) ?? []).filter(
|
||||
(d) =>
|
||||
|
||||
Reference in new issue
Block a user