优化只能创作里的图片创作
This commit is contained in:
@@ -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 ? (
|
||||
|
||||
Reference in New Issue
Block a user