优化只能创作里的图片创作

This commit is contained in:
Azmat@qq.com
2026-09-14 18:56:07 +08:00
parent 3fed9b9435
commit f15fe21b1a
16 changed files with 540 additions and 106 deletions
+82 -13
View File
@@ -41,6 +41,39 @@ type PromptBlock =
| { type: "shot"; heading: string; fields: Array<{ label: string; value: string }>; text: string }
| { type: "text"; heading: string; text: string };
type ReplyOption = { label: string; text: string };
function replyOptionsForMessage(payload: Record<string, unknown>, isVideo: boolean): ReplyOption[] {
const stored = payload.reply_options;
if (Array.isArray(stored)) {
const options = stored
.filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object")
.map((item) => ({ label: String(item.label || "").trim(), text: String(item.text || "").trim() }))
.filter((item) => item.label && item.text)
.slice(0, 3);
if (options.length) return options;
}
return isVideo
? [
{ label: "继续完善方案", text: "继续完善方案" },
{ label: "换个场景", text: "我想换个场景" },
{ label: "改卖点", text: "我想改卖点" },
]
: [
{ label: "按这个方向出图", text: "按这个方向出图" },
{ label: "换个场景", text: "我想换个场景" },
{ label: "改人物状态", text: "我想改人物状态" },
];
}
function replyHintForMessage(payload: Record<string, unknown>, isVideo: boolean): string {
const stored = String(payload.reply_hint || "").trim();
if (stored) return stored;
return isVideo
? "可以回复「继续完善方案」,也可以选下方想调整的方向。"
: "可以回复「按这个方向出图」,也可以选下方想调整的方向。";
}
function parseMarkdownTable(lines: string[]): { headers: string[]; rows: string[][] } | null {
if (lines.length < 2) return null;
const splitRow = (line: string) =>
@@ -296,6 +329,8 @@ function MentionChips({
/** 生成中的消息靠轮询转成结果。视频 5–10 分钟,间隔别设太密。 */
const POLL_INTERVAL_MS = 4000;
const AGENT_POLL_INTERVAL_MS = 1500;
// 请求已发出、但 Celery 还没把会话切到 planning 时,不能让旧 idle 快照把 loading 收掉。
const AGENT_START_GRACE_MS = 20_000;
// ────────────────────────────────────────────────────────── 卡片
@@ -1540,6 +1575,13 @@ export function OmniSessionPage({
}
// 刷新/重进时若仍在整理方案,直接恢复轮询 UI —— 不要 abort 杀 Celery
const planning = detail.agent_status === "planning";
const waitingForFreshTurn =
!planning
&& streamingRef.current
&& Date.now() < holdPlanningUntilRef.current
&& !turnLooksSettled(detail);
// 刚发送时这里可能拿到请求之前的 idle 快照。保留 loading,等本轮真正落消息。
if (waitingForFreshTurn) return;
streamingRef.current = planning;
setStreaming(planning);
})
@@ -1869,6 +1911,11 @@ export function OmniSessionPage({
}
stalePlanningNotifiedRef.current = false;
userCancelledRef.current = false;
// 先占住 loading;服务端初始 202 / 旧 idle 快照回来时不能把它闪没。
holdPlanningUntilRef.current = Math.max(
holdPlanningUntilRef.current,
Date.now() + AGENT_START_GRACE_MS,
);
streamingRef.current = true;
setStreaming(true);
setLiveText("");
@@ -1912,16 +1959,17 @@ export function OmniSessionPage({
const planning = result.agent_status === "planning";
if (planning) {
// 发出后短时间忽略轮询里的陈旧 idle,避免 thinking 闪灭
holdPlanningUntilRef.current = Date.now() + 6000;
holdPlanningUntilRef.current = Math.max(
holdPlanningUntilRef.current,
Date.now() + AGENT_START_GRACE_MS,
);
streamingRef.current = true;
setStreaming(true);
} else {
holdPlanningUntilRef.current = 0;
streamingRef.current = false;
setStreaming(false);
setLiveText("");
setLiveReasoning("");
setActiveTool("");
// creationSend 可能先回 idle、Celery 随后才切 planning。此处不抢着收起,
// 紧接着的详情快照一旦落到助手消息/确认卡就会通过 mergeCreationDetail 正常收起。
streamingRef.current = true;
setStreaming(true);
}
api
.getCreation(conversationId)
@@ -2170,6 +2218,13 @@ export function OmniSessionPage({
const refs = message.refs || [];
const body = stripMentionText(message.text, refs);
const pending = message.id === pendingUserId;
const showReplyGuide =
message.role === "assistant"
&& message.kind === "text"
&& messages[messages.length - 1]?.id === message.id
&& !streaming;
const replyHint = showReplyGuide ? replyHintForMessage(message.payload, isVideo) : "";
const replyOptions = showReplyGuide ? replyOptionsForMessage(message.payload, isVideo) : [];
return (
<div
className={`omni-chat-row ${message.role === "user" ? "is-user" : "agent"}${pending ? " is-pending" : ""}`}
@@ -2185,12 +2240,26 @@ export function OmniSessionPage({
{(body || message.role !== "user") ? (
<div className="omni-chat-bubble" aria-busy={pending || undefined}>
{body ? <p className="omni-chat-text">{body}</p> : null}
{message.role === "assistant" && typeof message.payload?.reply_hint === "string" && message.payload.reply_hint.trim() ? (
<p className="omni-reply-hint">
<span className="omni-reply-hint-label">下一步可以这样回</span>
{String(message.payload.reply_hint)}
</p>
) : null}
</div>
) : null}
{showReplyGuide ? (
<div className="omni-reply-guide" aria-label="下一步引导">
<p>
<span>下一步</span>
{replyHint}
</p>
<div className="omni-reply-guide-options">
{replyOptions.map((option, index) => (
<button
type="button"
className={index === 0 ? "primary" : ""}
key={option.text}
onClick={() => void send({ kind: "text", text: option.text })}
>
{option.label}
</button>
))}
</div>
</div>
) : null}
{message.role === "user" && !pending ? (