Files
brk/website_next/ask/tools/render.js
T
2026-07-28 21:35:02 +02:00

93 lines
3.4 KiB
JavaScript

import { formatValue } from "./data.js";
/**
* @typedef {Object} MetricRead
* @property {string} label
* @property {string | undefined} unit
* @property {string} index
* @property {number | string} start
* @property {string | undefined} stamp
* @property {unknown[]} values
* @property {string} [requested]
*/
/**
* @typedef {Object} SourceEvidence
* @property {string} revision
* @property {string} path
* @property {number} startLine
* @property {number} [endLine]
*/
const SOURCE_URL = "https://github.com/bitcoinresearchkit/brk/blob";
/** @param {SourceEvidence} source */
function sourceKey(source) {
return `${source.revision}:${source.path}:${source.startLine}:${source.endLine ?? source.startLine}`;
}
/** @param {SourceEvidence} source */
function sourceLink(source) {
const end = source.endLine && source.endLine !== source.startLine
? `-${source.endLine}`
: "";
const path = source.path.split("/").map(encodeURIComponent).join("/");
const lines = `#L${source.startLine}${end ? `-L${source.endLine}` : ""}`;
const url = `${SOURCE_URL}/${encodeURIComponent(source.revision)}/${path}${lines}`;
return `[\`${source.path}:${source.startLine}${end}\`](${url})`;
}
/** @param {{ facts: string[], sources: SourceEvidence[], excerpts: (SourceEvidence & { content: string })[] }} evidence */
export function renderEvidence(evidence) {
const sections = [...new Set(evidence.facts)].filter(Boolean);
const cited = new Set();
for (const excerpt of evidence.excerpts) {
sections.push(`${sourceLink(excerpt)}\n\n\`\`\`\n${excerpt.content}\n\`\`\``);
cited.add(sourceKey(excerpt));
}
const sources = [...new Map(
evidence.sources.map((source) => [sourceKey(source), source]),
).values()].filter((source) => !cited.has(sourceKey(source)));
if (sources.length) {
sections.push(`Source${sources.length === 1 ? "" : "s"}: ${sources.map(sourceLink).join(", ")}`);
}
return sections.join("\n\n") || "I could not find enough verified evidence to answer that.";
}
/** @param {MetricRead[]} results */
export function renderData(results) {
return results.map((result) => {
const returnedPosition = result.index === "height"
? String(result.start)
: result.stamp;
if (
result.requested &&
returnedPosition &&
result.requested !== returnedPosition
) {
const returned = result.index === "height"
? `block ${returnedPosition}`
: returnedPosition;
return `**${result.label}**: no exact value was returned for ${result.requested}; the server returned ${returned} instead.`;
}
if (result.values.length === 1 && typeof result.values[0] === "number") {
const position = result.index === "height"
? ` at block ${result.start}`
: result.stamp
? ` at ${result.stamp}`
: "";
return `**${result.label}**: ${formatValue(result.values[0], result.unit)}${position}`;
}
const values = /** @type {number[]} */ (
result.values.filter((value) => typeof value === "number")
);
if (!values.length) return `**${result.label}**: no values returned.`;
return `**${result.label}**: ${values.length} values; latest ${formatValue(values[values.length - 1], result.unit)}.`;
}).join("\n");
}
/** @param {string} answer @param {{ method: string, path: string }} operation */
export function renderApiAnswer(answer, operation) {
return `${answer.trim()}\n\nData: \`${operation.method} ${operation.path}\``;
}