feat: upgrade investigation and presentation runtime
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
"Stack",
|
||||
"Section",
|
||||
"Text",
|
||||
"Result",
|
||||
"Metric",
|
||||
"Table",
|
||||
"Chart",
|
||||
@@ -66,6 +67,7 @@
|
||||
"Stack",
|
||||
"Section",
|
||||
"Text",
|
||||
"Result",
|
||||
"Metric",
|
||||
"Table",
|
||||
"Chart",
|
||||
@@ -90,6 +92,21 @@
|
||||
"compact",
|
||||
"normal"
|
||||
]
|
||||
},
|
||||
"intent": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"response",
|
||||
"comparison",
|
||||
"report"
|
||||
]
|
||||
},
|
||||
"density": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"compact",
|
||||
"comfortable"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -116,6 +133,15 @@
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"result",
|
||||
"analysis",
|
||||
"evidence",
|
||||
"follow_up"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -139,6 +165,10 @@
|
||||
"info",
|
||||
"warning"
|
||||
]
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -148,6 +178,47 @@
|
||||
"additionalProperties": false,
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"Result": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
},
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"maxLength": 4000
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"success",
|
||||
"partial",
|
||||
"unconfirmed",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"title",
|
||||
"summary",
|
||||
"status",
|
||||
"source"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"Metric": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import type { z } from "zod";
|
||||
import type { answerUiBlockSchema } from "./answer-ui.js";
|
||||
|
||||
type AnswerUiBlock = z.infer<typeof answerUiBlockSchema>;
|
||||
|
||||
/** Round-local publication state. Missing phase is reserved for legacy blocks. */
|
||||
export class AnswerUiPublication {
|
||||
private readonly blocks = new Map<string, AnswerUiBlock>();
|
||||
private closed = false;
|
||||
|
||||
get size(): number { return this.blocks.size; }
|
||||
|
||||
publish(block: AnswerUiBlock): string | undefined {
|
||||
if (this.closed) return "最终答复已提交,本轮不能继续更新界面";
|
||||
const previous = this.blocks.get(block.block_id);
|
||||
if (previous && previous.phase !== block.phase) return "相同 block_id 不能变更 phase;请使用独立 block_id 发布最终界面";
|
||||
this.blocks.set(block.block_id, block);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
finalize(refs: unknown): string[] {
|
||||
this.closed = true;
|
||||
return Array.isArray(refs) ? [...new Set(refs.filter((id): id is string => {
|
||||
if (typeof id !== "string") return false;
|
||||
const block = this.blocks.get(id);
|
||||
return !!block && block.phase !== "process";
|
||||
}))].slice(0, 48) : [];
|
||||
}
|
||||
}
|
||||
|
||||
/** Only standalone marker lines outside Markdown fenced code are references. */
|
||||
export function answerUiAnchors(answer: string): string[] {
|
||||
const ids: string[] = [];
|
||||
let fence: { char: string; length: number } | undefined;
|
||||
for (const line of answer.split(/\r?\n/)) {
|
||||
const marker = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line);
|
||||
if (marker) {
|
||||
if (!fence) fence = { char: marker[1][0], length: marker[1].length };
|
||||
else if (marker[1][0] === fence.char && marker[1].length >= fence.length && !marker[2].trim()) fence = undefined;
|
||||
continue;
|
||||
}
|
||||
if (fence) continue;
|
||||
const anchor = /^ {0,3}\[\[ui:([a-zA-Z0-9_-]{1,80})\]\][ \t]*$/.exec(line);
|
||||
if (anchor) ids.push(anchor[1]);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
@@ -36,10 +36,10 @@ export const answerUiSpecSchema = z.object({ root: key, elements: z.record(z.obj
|
||||
if (!visit(spec.root, 0) || visited.size !== names.length) fail();
|
||||
});
|
||||
|
||||
export const answerUiInputSchema = z.object({ block_id: key, fallback_text: z.string().min(1).max(4000), spec: answerUiSpecSchema }).strict();
|
||||
export const answerUiInputSchema = z.object({ block_id: key, phase: z.enum(["process", "final"]).optional(), fallback_text: z.string().min(1).max(4000), spec: answerUiSpecSchema }).strict();
|
||||
export const answerUiBlockSchema = answerUiInputSchema.extend({ version: z.literal("answer-ui@1") })
|
||||
.refine((block) => JSON.stringify(block).length <= 100000, "界面块过大");
|
||||
export const parseAnswerUiInput = (value: unknown) => {
|
||||
const parsed = answerUiInputSchema.safeParse(value);
|
||||
return parsed.success ? { version: "answer-ui@1" as const, ...parsed.data } : null;
|
||||
return parsed.success ? { version: "answer-ui@1" as const, ...parsed.data, phase: parsed.data.phase ?? "process" } : null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user