Download src/friend/text-utils.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 2.96 kB
-
https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/friend/text-utils.ts
- Command line
-
hf download hf://chenbhao/codev@afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/friend/text-utils.ts
-
curl -L -o text-utils.ts https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/friend/text-utils.ts
2.96 kB
| /** | |
| * Text cleaning utilities for Friend VRM avatar. | |
| */ | |
| /** | |
| * Strip agent's inline thinking/reasoning from text output. | |
| */ | |
| export function stripThinking(text: string): string { | |
| const lines = text.split('\n'); | |
| const TS_RE = /^\d{2}:\d{2}:\d{2}\s/; | |
| let lastTsIdx = -1; | |
| for (let i = lines.length - 1; i >= 0; i--) { | |
| if (TS_RE.test(lines[i])) { | |
| lastTsIdx = i; | |
| break; | |
| } | |
| } | |
| if (lastTsIdx >= 0) { | |
| lines[lastTsIdx] = lines[lastTsIdx].replace(TS_RE, ''); | |
| const result = lines.slice(lastTsIdx).join('\n').trim(); | |
| if (result) return result; | |
| } | |
| const EMOTION_RE = /^(think|happy|sad|angry|surprised|awkward|question|curious|neutral)\s*$/i; | |
| const REASONING_RE = /^(I'll |I need to |I should |I want to |The user |Time is |Let me |My response|Responding )/i; | |
| const THINKING_HEADER_RE = /^(\*{0,2}Thinking( Process)?[:\*]|\*{0,2}思考)/i; | |
| let startIdx = 0; | |
| for (let i = 0; i < lines.length; i++) { | |
| const line = lines[i].trim(); | |
| if (!line) { startIdx = i + 1; continue; } | |
| if (EMOTION_RE.test(line)) { startIdx = i + 1; continue; } | |
| if (THINKING_HEADER_RE.test(line)) { startIdx = i + 1; continue; } | |
| if (REASONING_RE.test(line)) { startIdx = i + 1; continue; } | |
| if (/^\d+[\.\)]\s/.test(line)) { startIdx = i + 1; continue; } | |
| break; | |
| } | |
| let result = lines.slice(startIdx).join('\n').trim(); | |
| if (!result) result = text.trim(); | |
| return result.replace(/\[\[\w+\]\]/g, '').trim(); | |
| } | |
| /** Strip action/narration text wrapped in *..* or **..***/ | |
| export function stripActions(text: string): string { | |
| return text.replace(/\*{1,2}[^*]+\*{1,2}/g, '').replace(/\n{2,}/g, '\n').trim(); | |
| } | |
| /** Strip markdown symbols */ | |
| export function stripMarkdown(text: string): string { | |
| return text.replace(/[*_~`#>]/g, '').trim(); | |
| } | |
| /** Strip emoji characters */ | |
| export function stripEmoji(text: string): string { | |
| return text.replace(/[\p{Emoji_Presentation}\p{Extended_Pictographic}]/gu, '').trim(); | |
| } | |
| /** | |
| * Strip text for TTS playback. | |
| */ | |
| export function stripForTts(text: string): string { | |
| return text | |
| .replace(/([^)]*)/g, '') | |
| .replace(/\([^)]*\)/g, '') | |
| .replace(/[_~`#>]/g, '') | |
| .replace(/\*([^*]*)\*/g, '$1') | |
| .replace(/[\p{Emoji_Presentation}\p{Extended_Pictographic}]/gu, '') | |
| .trim(); | |
| } | |
| /** | |
| * Split text into sentences for incremental TTS. | |
| */ | |
| export function splitSentences(text: string): string[] { | |
| const ellipsisMap: string[] = []; | |
| const safeText = text.replace(/\.{2,}|…+/g, (match) => { | |
| const idx = ellipsisMap.length; | |
| ellipsisMap.push(match); | |
| return `\x00E${idx}\x00`; | |
| }); | |
| const parts = safeText.split(/(?<=[。!?;\n.!?;~])\s*/); | |
| return parts | |
| .map((s) => { | |
| let restored = s; | |
| for (let i = 0; i < ellipsisMap.length; i++) { | |
| restored = restored.replace(`\x00E${i}\x00`, ellipsisMap[i]); | |
| } | |
| return restored.trim(); | |
| }) | |
| .filter((s) => s && !/^[。!?;.!?;~、,,\s]+$/.test(s)); | |
| } | |