42 lines
1.5 KiB
TypeScript
42 lines
1.5 KiB
TypeScript
const protectedMarkdownPattern = /```[\s\S]*?(?:```|$)|~~~[\s\S]*?(?:~~~|$)|`+[^`\n]*`+/gu;
|
|
const inlineStrongListMarker = / - (?=\*\*[^*\n]{1,80}\*\*\s+(?:[—–:-]\s+)?)/gu;
|
|
const trailingQuestion = /\s+((?:Are|Can|Could|Do|Does|How|Is|What|When|Where|Which|Why|Would)\b[^?\n]*\?)$/u;
|
|
|
|
const normalizeProse = (text: string) => {
|
|
const withLineBreaks = text.replaceAll("\\r\\n", "\n").replaceAll("\\n", "\n");
|
|
|
|
return withLineBreaks
|
|
.split("\n")
|
|
.map((line) => {
|
|
const markers = [...line.matchAll(inlineStrongListMarker)];
|
|
if (markers.length < 2) return line;
|
|
|
|
let markerIndex = 0;
|
|
return line
|
|
.replaceAll(inlineStrongListMarker, () => (markerIndex++ === 0 ? "\n\n- " : "\n- "))
|
|
.replace(trailingQuestion, "\n\n$1");
|
|
})
|
|
.join("\n");
|
|
};
|
|
|
|
/**
|
|
* Makes common streamed model Markdown readable without changing code samples.
|
|
*
|
|
* Models occasionally emit escaped line breaks or an entire bold-label list on
|
|
* one line. The assistant-ui renderer receives this function through its
|
|
* streaming-safe `preprocess` hook, before Markdown is parsed.
|
|
*/
|
|
export const normalizeAssistantMarkdown = (text: string) => {
|
|
let offset = 0;
|
|
let normalized = "";
|
|
|
|
for (const match of text.matchAll(protectedMarkdownPattern)) {
|
|
const index = match.index;
|
|
normalized += normalizeProse(text.slice(offset, index));
|
|
normalized += match[0];
|
|
offset = index + match[0].length;
|
|
}
|
|
|
|
return normalized + normalizeProse(text.slice(offset));
|
|
};
|