fix(web): keep rectification stages aligned and stop jump overlay covering choices
Progress and spoken text shared a 24px consultation-report gap, and the jump chip covered option D. Put D-chart names back on the activity strip from evidence rescore methods. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5547,6 +5547,22 @@
|
||||
- 复发自:BUG-379(邻近年学业存在性探针已跳过,但仍用整窗观察和剩余 D24 题干继续问入学/高考)
|
||||
- 修复版本:待发布
|
||||
|
||||
## BUG-381 | 生时纠正进度条与点选卡重叠、分盘名消失
|
||||
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-08-25
|
||||
- 最近更新:2026-08-25
|
||||
- 影响面:生时纠正聊天气泡、阶段进度、点选卡、「回到最新」
|
||||
- 用户现象:有工具进度的回复看起来比上一句更往里缩;阶段和正文之间空一大截;进度条只剩「读取校正记录 / 整理证据」,看不到 D 盘对照;点选卡最下面的 D 和「先这样」被「回到最新」挡住。
|
||||
- 触发条件:本轮有公开工具进度;证据写入触发了重算但比较工具没单独出场;用户略微离开底部看点选卡。
|
||||
- 根因:(1) 有进度又有正文时套了咨询页 `consultation-thinking-report`,格子间距 24px,进度条自己还有下边距。(2) 已落盘回合不回放工具进度,最新一轮有勾选列、上一轮没有,看起来像缩进。(3) 证据批处理把 `executed_methods` 放在 `rescore` 里,公开活动只读顶层字段,技法句又写在口语后面。(4) 「回到最新」贴在输入框上方居中,离开底部 96px 就出现,正好盖住点选卡下沿。
|
||||
- 修复:进度和正文改用 8px 紧凑叠放。已落盘回执回放工具步骤。证据重算的分盘名并进进度条,不写进口语。点选卡仍在视口下沿时不显示「回到最新」,按钮改到末行外侧。不改 Skill `10.0.11`。
|
||||
- 验证:`frontend/tests/chat-stream-layout.test.ts`、`frontend/tests/rectification-agentic-entry.test.ts`、`frontend/tests/rectification-answer-choice.test.ts`、`frontend/tests/rectification-v9-stream.test.ts`、`frontend/tests/agent-activity-progress.test.ts`。
|
||||
- 防复发:有进度的纠正气泡不得用咨询页 24px 报告间距。技法句属于进度条,不得再进 `answer.delta`。点选卡仍可见时不得用居中浮层挡住 D / 「先这样」。
|
||||
- 相关记录:BUG-373、BUG-376、BUG-377
|
||||
- 复发自:BUG-376(关掉思考正文后,进度条只剩工具名,技法句不再出现;回到最新仍按咨询页浮层)
|
||||
- 修复版本:待发布
|
||||
|
||||
## BUG-379 | 生时纠正已记入学后仍编造高考年并再问入学
|
||||
|
||||
- 状态:resolved
|
||||
|
||||
@@ -333,6 +333,17 @@ button:disabled { cursor: default; opacity: .45; }
|
||||
}
|
||||
.agent-activity-status + .message-answer,
|
||||
.agent-thinking-panel + .message-answer { margin-top: var(--space-2); }
|
||||
.message-stage-and-answer {
|
||||
display: grid;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.message-stage-and-answer .agent-thinking-panel,
|
||||
.message-stage-and-answer .agent-activity-status {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.message-stage-and-answer .consultation-report-analysis .message-answer {
|
||||
margin-top: 0;
|
||||
}
|
||||
.composer textarea::placeholder { color: var(--color-ink-tertiary); }
|
||||
.composer button svg { width: 19px; height: 19px; }
|
||||
.dialog-close svg { width: 19px; height: 19px; }
|
||||
@@ -2366,11 +2377,24 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
}
|
||||
.rectification-workspace__chat .conversation {
|
||||
--rectification-jump-clearance: calc(44px + var(--space-3));
|
||||
padding-bottom: var(--space-4);
|
||||
}
|
||||
.rectification-workspace__chat .message-list {
|
||||
width: 100%;
|
||||
max-width: 720px;
|
||||
padding-bottom: var(--rectification-jump-clearance);
|
||||
}
|
||||
.conversation.is-rectification .message-assistant .agent-thinking-step {
|
||||
grid-template-columns: 14px minmax(0, 1fr);
|
||||
gap: 6px;
|
||||
}
|
||||
.conversation.is-rectification .message-assistant .agent-thinking-marker {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.conversation.is-rectification .message-actions {
|
||||
margin-inline-start: var(--assistant-content-inset);
|
||||
}
|
||||
.rectification-workspace__chat .composer-wrap {
|
||||
position: relative;
|
||||
@@ -2382,8 +2406,9 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
|
||||
bottom: 100%;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: var(--space-2);
|
||||
padding-inline-end: var(--space-4);
|
||||
pointer-events: none;
|
||||
}
|
||||
.rectification-jump-latest button {
|
||||
|
||||
@@ -66,6 +66,17 @@ function MessageThinkingTrace({
|
||||
);
|
||||
}
|
||||
|
||||
function VargaTraceStep({ sentence }: Readonly<{ sentence: string }>) {
|
||||
return (
|
||||
<li className="agent-thinking-step is-done">
|
||||
<span className="agent-thinking-marker" aria-hidden="true">
|
||||
<Check />
|
||||
</span>
|
||||
<span>{sentence}</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function AgentActivityStatus({
|
||||
state,
|
||||
label = labels[state],
|
||||
@@ -73,6 +84,7 @@ export function AgentActivityStatus({
|
||||
completedTrail,
|
||||
thinkingText,
|
||||
activityTrace,
|
||||
vargaSentence,
|
||||
hasAnswer = false,
|
||||
showLive = true,
|
||||
}: Readonly<{
|
||||
@@ -82,12 +94,15 @@ export function AgentActivityStatus({
|
||||
completedTrail?: string;
|
||||
thinkingText?: string;
|
||||
activityTrace?: readonly AgentActivityTraceItem[];
|
||||
vargaSentence?: string | null;
|
||||
hasAnswer?: boolean;
|
||||
showLive?: boolean;
|
||||
}>) {
|
||||
const completedSteps = activityCompletedSteps(completedTrail);
|
||||
const live = showLive && !hasAnswer;
|
||||
const trace = activityTrace ?? [];
|
||||
const varga = vargaSentence?.trim() ?? "";
|
||||
const showVarga = Boolean(varga) && !trace.some((item) => item.label === varga);
|
||||
if (trace.length > 0) {
|
||||
return (
|
||||
<div className="agent-thinking-panel agent-activity-status">
|
||||
@@ -100,15 +115,16 @@ export function AgentActivityStatus({
|
||||
hasAnswer={hasAnswer}
|
||||
/>
|
||||
))}
|
||||
{showVarga ? <VargaTraceStep sentence={varga} /> : null}
|
||||
</ol>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (!live && completedSteps.length === 0 && !thinkingText?.trim()) return null;
|
||||
if (!live && completedSteps.length === 0 && !thinkingText?.trim() && !showVarga) return null;
|
||||
|
||||
return (
|
||||
<div className="agent-thinking-panel agent-activity-status">
|
||||
{(live || completedSteps.length > 0) && (
|
||||
{(live || completedSteps.length > 0 || showVarga) && (
|
||||
<ol className="agent-thinking-timeline">
|
||||
{completedSteps.map((step) => (
|
||||
<li className="agent-thinking-step is-done" key={step}>
|
||||
@@ -129,6 +145,7 @@ export function AgentActivityStatus({
|
||||
</span>
|
||||
</li>
|
||||
) : null}
|
||||
{showVarga ? <VargaTraceStep sentence={varga} /> : null}
|
||||
</ol>
|
||||
)}
|
||||
{thinkingText ? <MessageThinkingTrace text={thinkingText} hasAnswer={hasAnswer} /> : null}
|
||||
|
||||
@@ -104,6 +104,7 @@ export function ChatMessageRow({
|
||||
completedTrail={message.activity?.completedTrail}
|
||||
thinkingText={hasTrace ? undefined : message.thinkingText}
|
||||
activityTrace={message.activityTrace}
|
||||
vargaSentence={vargaSentence}
|
||||
hasAnswer={hasAnswer}
|
||||
showLive={showLiveActivity}
|
||||
/>
|
||||
@@ -114,7 +115,7 @@ export function ChatMessageRow({
|
||||
<ChatMessageContent
|
||||
text={message.text}
|
||||
auditRows={message.agentExecutionReceipt?.techniqueAuditTable}
|
||||
vargaSentence={vargaSentence}
|
||||
vargaSentence={showThinkingPanel ? null : vargaSentence}
|
||||
/>
|
||||
)
|
||||
: null;
|
||||
@@ -157,7 +158,7 @@ export function ChatMessageRow({
|
||||
/>
|
||||
)}
|
||||
{stackedThinkingAndAnswer ? (
|
||||
<div className="consultation-thinking-report">
|
||||
<div className="message-stage-and-answer">
|
||||
{thinkingPanel}
|
||||
<section className="consultation-report-analysis" aria-label="回复">
|
||||
{spokenAnswer}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
RECTIFICATION_ACTIVITY_PROGRESS_LABELS,
|
||||
RECTIFICATION_TOOL_DONE_LABELS,
|
||||
RECTIFICATION_TOOL_PROGRESS_LABELS,
|
||||
activityTraceFromReceipt,
|
||||
rectificationCompletedTrail,
|
||||
rectificationToolActivityPhase,
|
||||
} from "@/lib/rectification-activity-labels";
|
||||
@@ -45,7 +46,7 @@ import {
|
||||
isPublicRectificationTool,
|
||||
} from "@/lib/rectification-agentic/v9/public-receipt";
|
||||
import { userFacingRunFailure, isIncompleteRunBanner } from "@/lib/rectification-agentic/v9/run-diagnostic";
|
||||
import { isNearBottom } from "@/lib/rectification-sticky-scroll";
|
||||
import { isNearBottom, shouldShowJumpToLatest } from "@/lib/rectification-sticky-scroll";
|
||||
import {
|
||||
CHOICE_ACTION,
|
||||
STOP_ACTION,
|
||||
@@ -226,12 +227,14 @@ function messagesFromTurns(initialTurns: readonly PersistedTurn[]): RenderMessag
|
||||
if (failed && !raw) return [];
|
||||
if (isIncompleteRunBanner(raw)) return [];
|
||||
const split = raw ? finalizeRectificationSpokenAndThinking(raw) : { thinking: "", spoken: raw };
|
||||
const completedReceipt = completedReceiptFromPersisted(turn.receipt);
|
||||
return [{
|
||||
role: "assistant",
|
||||
text: split.spoken,
|
||||
renderKey: key,
|
||||
state: turn.status === "completed" || failed ? "settled" : "thinking",
|
||||
completedReceipt: completedReceiptFromPersisted(turn.receipt),
|
||||
completedReceipt,
|
||||
activityTrace: activityTraceFromReceipt(completedReceipt),
|
||||
failed,
|
||||
turnId: turn.id,
|
||||
}];
|
||||
@@ -291,6 +294,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
const followTailRef = useRef(true);
|
||||
const scrollFrameRef = useRef<number | null>(null);
|
||||
const choiceActionIds = useRef(new Map<string, string>());
|
||||
const choiceCardsOpen = useRef(false);
|
||||
const [showJumpToLatest, setShowJumpToLatest] = useState(false);
|
||||
const [compactBoard, setCompactBoard] = useState(false);
|
||||
const [boardOpen, setBoardOpen] = useState(false);
|
||||
@@ -332,7 +336,12 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
if (!viewport) return;
|
||||
const nearBottom = isNearBottom(viewport.scrollHeight, viewport.scrollTop, viewport.clientHeight);
|
||||
followTailRef.current = nearBottom;
|
||||
setShowJumpToLatest(!nearBottom);
|
||||
setShowJumpToLatest(shouldShowJumpToLatest(
|
||||
viewport.scrollHeight,
|
||||
viewport.scrollTop,
|
||||
viewport.clientHeight,
|
||||
choiceCardsOpen.current,
|
||||
));
|
||||
}, []);
|
||||
|
||||
const scrollToLatest = useCallback((behavior: ScrollBehavior = "smooth") => {
|
||||
@@ -974,6 +983,10 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
&& !readonly
|
||||
&& regeneratingMessageKey === null,
|
||||
);
|
||||
choiceCardsOpen.current = showChoiceCards;
|
||||
useLayoutEffect(() => {
|
||||
updateFollowState();
|
||||
}, [showChoiceCards, updateFollowState]);
|
||||
const showSelectionCards = Boolean(
|
||||
candidateResult?.selectionAllowed
|
||||
&& offeredSelectionOnce
|
||||
@@ -1051,7 +1064,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
}),
|
||||
}
|
||||
: message;
|
||||
const vargaSentence = message.state === "settled" && !message.failed
|
||||
const vargaSentence = !message.failed
|
||||
? vargaSentenceFromMethods(message.completedReceipt?.methods)
|
||||
: null;
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { AgentActivityTraceItem } from "./agent-activity-trace.ts";
|
||||
import { activityCompletedTrail } from "./chat-message-view.ts";
|
||||
import type { PublicActivityPhase } from "./consultation-agent-events.ts";
|
||||
import type { CompletedActivityReceiptView } from "./rectification-activity-receipt.ts";
|
||||
import type {
|
||||
PublicRectificationActivity,
|
||||
PublicRectificationTool,
|
||||
@@ -62,6 +64,19 @@ export function rectificationCompletedTrail(steps: readonly PublicRectificationT
|
||||
return activityCompletedTrail(steps.map((tool) => RECTIFICATION_TOOL_DONE_LABELS[tool]));
|
||||
}
|
||||
|
||||
export function activityTraceFromReceipt(
|
||||
receipt: CompletedActivityReceiptView | null | undefined,
|
||||
): readonly AgentActivityTraceItem[] {
|
||||
if (!receipt?.steps.length) return [];
|
||||
return receipt.steps.map((tool) => ({
|
||||
id: `persisted-${tool}`,
|
||||
kind: "activity" as const,
|
||||
status: "done" as const,
|
||||
label: RECTIFICATION_TOOL_DONE_LABELS[tool],
|
||||
tool,
|
||||
}));
|
||||
}
|
||||
|
||||
export function rectificationToolActivityPhase(tool: PublicRectificationTool): PublicActivityPhase {
|
||||
if (LOAD_TOOLS.has(tool)) return "loading-method";
|
||||
if (COMPARE_TOOLS.has(tool)) return "chart-calculation";
|
||||
|
||||
@@ -106,6 +106,23 @@ export function isPublicRectificationToolName(value: unknown): value is PublicRe
|
||||
return isPublicRectificationTool(value);
|
||||
}
|
||||
|
||||
function executedMethodsFromRecord(value: unknown): PublicRectificationMethod[] {
|
||||
if (!value || typeof value !== "object") return [];
|
||||
const record = value as Record<string, unknown>;
|
||||
const found: PublicRectificationMethod[] = [];
|
||||
const add = (methods: unknown) => {
|
||||
if (!Array.isArray(methods)) return;
|
||||
for (const method of methods) {
|
||||
if (isPublicRectificationMethod(method) && !found.includes(method)) found.push(method);
|
||||
}
|
||||
};
|
||||
add(record.executed_methods);
|
||||
if (record.rescore && typeof record.rescore === "object") {
|
||||
add((record.rescore as Record<string, unknown>).executed_methods);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
function resultMethods(chunk: AgentChunkType): PublicRectificationMethod[] {
|
||||
if (chunk.type !== "tool-result") return [];
|
||||
const payload = chunk.payload && typeof chunk.payload === "object"
|
||||
@@ -113,9 +130,8 @@ function resultMethods(chunk: AgentChunkType): PublicRectificationMethod[] {
|
||||
: {};
|
||||
const candidates = [payload.result, payload.output, (chunk as unknown as { object?: unknown }).object];
|
||||
for (const candidate of candidates) {
|
||||
if (!candidate || typeof candidate !== "object") continue;
|
||||
const methods = (candidate as Record<string, unknown>).executed_methods;
|
||||
if (Array.isArray(methods)) return [...new Set(methods.filter(isPublicRectificationMethod))];
|
||||
const methods = executedMethodsFromRecord(candidate);
|
||||
if (methods.length > 0) return methods;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
export const RECTIFICATION_NEAR_BOTTOM_PX = 96;
|
||||
/** Keep the jump chip hidden while a choice card still occupies the composer overlay band. */
|
||||
export const RECTIFICATION_CHOICE_NEAR_BOTTOM_PX = 360;
|
||||
|
||||
export function distanceFromBottom(
|
||||
scrollHeight: number,
|
||||
@@ -24,6 +26,18 @@ export function isNearBottom(
|
||||
return distanceFromBottom(scrollHeight, scrollTop, clientHeight) <= thresholdPx;
|
||||
}
|
||||
|
||||
export function shouldShowJumpToLatest(
|
||||
scrollHeight: number,
|
||||
scrollTop: number,
|
||||
clientHeight: number,
|
||||
choiceCardOpen = false,
|
||||
): boolean {
|
||||
const distance = distanceFromBottom(scrollHeight, scrollTop, clientHeight);
|
||||
if (distance <= RECTIFICATION_NEAR_BOTTOM_PX) return false;
|
||||
if (choiceCardOpen && distance <= RECTIFICATION_CHOICE_NEAR_BOTTOM_PX) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function shouldFollowLatest(followTail: boolean): boolean {
|
||||
return followTail === true;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from "../src/lib/consultation-activity-labels.ts";
|
||||
import {
|
||||
RECTIFICATION_TOOL_PROGRESS_LABELS,
|
||||
activityTraceFromReceipt,
|
||||
rectificationCompletedTrail,
|
||||
rectificationToolActivityPhase,
|
||||
} from "../src/lib/rectification-activity-labels.ts";
|
||||
@@ -96,6 +97,17 @@ test("live rectification labels name the actual public tool", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("persisted receipts rebuild the public activity steps without thinking text", () => {
|
||||
assert.deepEqual(
|
||||
activityTraceFromReceipt({
|
||||
steps: ["rectification-read-case", "rectification-record-evidence-batch"],
|
||||
methods: ["d1-rashi", "d10-dashamsa"],
|
||||
}).map((row) => `${row.kind}:${row.label}`),
|
||||
["activity:读取校正记录", "activity:整理多条事件证据"],
|
||||
);
|
||||
assert.deepEqual(activityTraceFromReceipt({ steps: [], methods: ["d1-rashi"] }), []);
|
||||
});
|
||||
|
||||
test("multi-domain chart calculation names the current item without domain ids", () => {
|
||||
assert.equal(chartCalculationProgressLabel(1, 1), CONSULTATION_CHART_CALCULATION_LABEL);
|
||||
assert.equal(chartCalculationProgressLabel(2, 3), "正在计算本命盘(第 2/3 项)…");
|
||||
|
||||
@@ -75,6 +75,10 @@ test("shows honest agent activity states before and during streamed text", () =>
|
||||
assert.match(messageRowSource, /<ChatMessageContent[\s\S]*text=\{message\.text\}[\s\S]*auditRows=\{message\.agentExecutionReceipt\?\.techniqueAuditTable\}/);
|
||||
assert.match(messageRowSource, /stackedThinkingAndAnswer/);
|
||||
assert.match(messageRowSource, /aria-label="回复"/);
|
||||
assert.match(messageRowSource, /className="message-stage-and-answer"/);
|
||||
assert.match(messageRowSource, /vargaSentence=\{showThinkingPanel \? null : vargaSentence\}/);
|
||||
assert.match(globalStyles, /\.message-stage-and-answer/);
|
||||
assert.match(globalStyles, /\.message-stage-and-answer \{[\s\S]*gap: var\(--space-2\)/);
|
||||
assert.match(globalStyles, /\.agent-activity-status \+ \.message-answer/);
|
||||
assert.match(activitySource, /<ThinkingOrb aria-hidden="true" state=\{state\} size=\{20\}/);
|
||||
assert.match(activitySource, /className="agent-activity-status__text"/);
|
||||
|
||||
@@ -276,7 +276,13 @@ test("Agentic rectification follows the conversation tail only while the reader
|
||||
assert.match(chat, /followLatestContent/);
|
||||
assert.match(chat, /const container = conversation\.current/);
|
||||
assert.match(chat, /top: viewport\.scrollHeight/);
|
||||
assert.match(chat, /shouldShowJumpToLatest/);
|
||||
assert.match(chat, /choiceCardsOpen/);
|
||||
assert.match(chat, /updateFollowState/);
|
||||
assert.match(chat, /\}, \[busy, candidateResult, choiceCard, error, messages, savedTime, followLatestContent\]\);/);
|
||||
assert.match(styles, /\.rectification-jump-latest \{[\s\S]*justify-content: flex-end/);
|
||||
assert.match(styles, /--rectification-jump-clearance/);
|
||||
assert.match(styles, /\.message-stage-and-answer \{[\s\S]*gap: var\(--space-2\)/);
|
||||
assert.doesNotMatch(chat, /conversationEnd|scrollIntoView/);
|
||||
assert.doesNotMatch(styles, /scrollIntoView/);
|
||||
});
|
||||
@@ -295,6 +301,9 @@ test("rectification keeps receipts for the varga sentence and shows live tool pr
|
||||
assert.match(chat, /showActivity=\{displayedMessage\.state !== "settled"\}/);
|
||||
assert.match(chat, /vargaSentenceFromMethods/);
|
||||
assert.match(chat, /vargaSentence=\{vargaSentence\}/);
|
||||
assert.match(chat, /activityTraceFromReceipt/);
|
||||
assert.match(activityStatus, /vargaSentence/);
|
||||
assert.match(activityStatus, /VargaTraceStep/);
|
||||
assert.match(board, /RectificationHouseTableView/);
|
||||
assert.doesNotMatch(chat, /activeActivity/);
|
||||
assert.match(chat, /RECTIFICATION_TOOL_PROGRESS_LABELS/);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
quoteIsFromAssistantQuestion,
|
||||
userVisibleChoiceLine,
|
||||
} from "../src/lib/rectification-agentic/v9/choice-action.ts";
|
||||
import { isNearBottom, shouldFollowLatest } from "../src/lib/rectification-sticky-scroll.ts";
|
||||
import { isNearBottom, shouldFollowLatest, shouldShowJumpToLatest } from "../src/lib/rectification-sticky-scroll.ts";
|
||||
import { projectTurnDecision, TURN_DECISION_MAX_BYTES, turnDecisionByteLength } from "../src/lib/rectification-agentic/v9/turn-decision.ts";
|
||||
import {
|
||||
mapModelFinishToErrorCode,
|
||||
@@ -131,6 +131,13 @@ test("does not force-follow when the reader has scrolled upward", () => {
|
||||
assert.equal(shouldFollowLatest(true), true);
|
||||
});
|
||||
|
||||
test("jump-to-latest stays hidden while a choice card still sits in the overlay band", () => {
|
||||
assert.equal(shouldShowJumpToLatest(1_000, 100, 400), true);
|
||||
assert.equal(shouldShowJumpToLatest(1_000, 520, 400), false);
|
||||
assert.equal(shouldShowJumpToLatest(1_000, 500, 400, true), false);
|
||||
assert.equal(shouldShowJumpToLatest(1_000, 200, 400, true), true);
|
||||
});
|
||||
|
||||
test("choice quotes come from the option label, not the assistant question year", () => {
|
||||
const question = "2016 年前后,有没有明显高考或重要考试发挥失常?";
|
||||
assert.equal(quoteIsFromAssistantQuestion("2016年", question), true);
|
||||
|
||||
@@ -135,6 +135,27 @@ test("every public rectification tool maps its real lifecycle to public activity
|
||||
assert.equal(mapStreamChunkToActivity(chunk("text-delta", { text: "x" }) as never), null);
|
||||
});
|
||||
|
||||
test("evidence-batch rescore methods surface on the public activity event", () => {
|
||||
assert.deepEqual(
|
||||
mapStreamChunkToActivity(chunk("tool-result", {
|
||||
toolName: "rectification-record-evidence-batch",
|
||||
result: {
|
||||
accepted_count: 1,
|
||||
rescore: {
|
||||
status: "completed",
|
||||
executed_methods: ["d1-rashi", "d10-dashamsa", "private-method"],
|
||||
},
|
||||
},
|
||||
}) as never),
|
||||
{
|
||||
type: "tool.activity",
|
||||
tool: "rectification-record-evidence-batch",
|
||||
status: "completed",
|
||||
methods: ["d1-rashi", "d10-dashamsa"],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("reasoning, raw payloads, provider metadata and step internals never map to the answer channel", () => {
|
||||
assert.equal(mapStreamChunkToPhase(chunk("reasoning-start", { id: "r1" }) as never), null);
|
||||
assert.equal(mapStreamChunkToPhase(chunk("reasoning-delta", { text: "内部推理" }) as never), null);
|
||||
|
||||
Reference in New Issue
Block a user