feat: source citations with footnotes in the transcript (#21)
This commit was merged in pull request #21.
This commit is contained in:
+50
-2
@@ -1,6 +1,8 @@
|
||||
import { linkCitations } from "./citations";
|
||||
import { CLOSING_FENCE, OPENING_FENCE, mapOutsideCodeSpans, readCodeSpan, repairCodeBlocks } from "./code";
|
||||
import { sanitizeInlineHtml } from "./html";
|
||||
import { looksLikeMath, renderLatex } from "./latex";
|
||||
import type { Source } from "./types";
|
||||
|
||||
/**
|
||||
* The rendering pass for model output.
|
||||
@@ -490,6 +492,30 @@ export function renderInlineContent(markdown: string): string {
|
||||
return flatten(htmlPass(mathPass(splitSegments(markdown))));
|
||||
}
|
||||
|
||||
/** What the pane will draw, and what the answer cited so the list can filter. */
|
||||
export interface RenderedMarkdown {
|
||||
text: string;
|
||||
/**
|
||||
* The numbers this answer cited that became links.
|
||||
*
|
||||
* Reported rather than worked out again by the caller: the pass already had to
|
||||
* decide which markers were real and which were inside code, and a second reader
|
||||
* of the same text could only disagree with it.
|
||||
*/
|
||||
cited: Set<number>;
|
||||
}
|
||||
|
||||
/** Optional input to the pass. */
|
||||
export interface RenderOptions {
|
||||
/**
|
||||
* The turn's sources, so a marker can become a link to the page it names.
|
||||
*
|
||||
* Absent for a question, and absent for an answer in a turn that consulted
|
||||
* nothing, in which case the citation pass is the identity function.
|
||||
*/
|
||||
sources?: Source[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The full rendering pass, from what the model wrote to what the pane draws.
|
||||
*
|
||||
@@ -497,6 +523,28 @@ export function renderInlineContent(markdown: string): string {
|
||||
* markdown around each message (a blockquote for a question, a rule between
|
||||
* exchanges) and none of that should be read as model output.
|
||||
*/
|
||||
export function renderModelOutput(markdown: string): string {
|
||||
return renderInlineContent(repairCodeBlocks(markdown));
|
||||
export function renderModelOutput(markdown: string, options: RenderOptions = {}): RenderedMarkdown {
|
||||
const sources = options.sources ?? [];
|
||||
const cited = new Set<number>();
|
||||
|
||||
/**
|
||||
* Maths first, then citations, then markup.
|
||||
*
|
||||
* The order is forced by one collision. A link label has to escape its brackets
|
||||
* or the parser ends the link text early, and `[\[1\]]` is exactly the shape of
|
||||
* LaTeX's own `\[...\]` display delimiter, which the maths pass would eat and
|
||||
* hand back as a bare `1`. So the model's maths is converted before any marker
|
||||
* becomes a link: the citation pass writes final form markdown, which is not
|
||||
* input for anything but the markup pass, and that one only reads angle brackets.
|
||||
*/
|
||||
const segments = mathPass(splitSegments(repairCodeBlocks(markdown))).map((segment) => {
|
||||
if (segment.fence || sources.length === 0) return segment;
|
||||
|
||||
const result = linkCitations(segment.lines.join("\n"), sources);
|
||||
for (const index of result.cited) cited.add(index);
|
||||
|
||||
return { fence: false, language: "", lines: result.text.split("\n") };
|
||||
});
|
||||
|
||||
return { text: flatten(htmlPass(segments)), cited };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user