mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
fix(hud): keep localized summary words whole and drop padding
- The proxy keeps combining marks, in-word apostrophes and name dots (ar vowel signs, fr d'Orléans, ja ・), normalizes to NFC, caps every summary in characters and cuts at a word boundary. - feedState is nullable instead of a `none` token, and the summary has at most five words: "exactly five" made the model pad short views with "no data" or "unavailable". - The HUD caches only the model's words and adds the current warning on every paint; an RTL locale gets a leading right-to-left mark. - The echo check also rejects an all-Latin reply in a non-Latin language, translated layer ids and, with every feed healthy, the UNAVAILABLE word; places are matched by word and one-word layer names are skipped in Latin-script locales. - The warning tag names the worst layers first; a partial analyst card shows one partial chip.
This commit is contained in:
1 parent
0d3c1ef1d9
commit
818d2c8295
12 files changed
+2521
-755
No files matched your search
@@ -1,10 +1,12 @@
|
||||
import {
|
||||
HUD_SUMMARY_FEED_STATES,
|
||||
HUD_SUMMARY_MAX_CHARS,
|
||||
HUD_SUMMARY_MAX_UNSPACED_CHARS,
|
||||
HUD_SUMMARY_RESPONSE_FORMAT,
|
||||
hudSummaryInput,
|
||||
hudSummaryInstructions,
|
||||
hudSummaryIsUnspaced,
|
||||
hudSummaryLocale,
|
||||
keylessHudSummaryResponse,
|
||||
} from '../../../src/hudSummaryResponse.js';
|
||||
import { enforceRateLimit, openAiRateLimiter } from './rate-limit.js';
|
||||
@@ -35,36 +37,50 @@ function parseHudSummaryReply(text, locale) {
|
||||
? reply.feedState
|
||||
: null;
|
||||
return {
|
||||
summary: toFiveWordHudSummary(reply?.summary, hudSummaryIsUnspaced(locale)),
|
||||
feedState: feedState === 'none' ? null : feedState,
|
||||
summary: toFiveWordHudSummary(reply?.summary, locale),
|
||||
feedState,
|
||||
};
|
||||
}
|
||||
|
||||
function toFiveWordHudSummary(value, unspaced) {
|
||||
function toFiveWordHudSummary(value, requested) {
|
||||
const locale = hudSummaryLocale(requested);
|
||||
const words = String(value || '')
|
||||
.replace(/[^\p{L}\p{N}\s-]/gu, ' ')
|
||||
.trim()
|
||||
.normalize('NFC')
|
||||
// Marks, and apostrophes and dots inside a word, belong to it: ar vowel
|
||||
// signs, fr d'Orléans, ja サンフランシスコ・ベイエリア.
|
||||
.replace(/[^\p{L}\p{M}\p{N}\s'’·・-]/gu, ' ')
|
||||
.split(/\s+/)
|
||||
.map((word) => word.replace(/^['’·・-]+|['’·・-]+$/gu, ''))
|
||||
.filter(Boolean);
|
||||
// Other languages keep five words, even around a Han place name.
|
||||
if (!unspaced) return words.slice(0, 5).join(' ');
|
||||
// Japanese and Chinese have no spaces to count words by: cap characters,
|
||||
// stepping back to the start of a Latin word rather than cutting it in two.
|
||||
const text = words.join(' ');
|
||||
const graphemes = [
|
||||
...new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(text),
|
||||
].map(({ segment }) => segment);
|
||||
if (graphemes.length <= HUD_SUMMARY_MAX_UNSPACED_CHARS) return text;
|
||||
const head = graphemes.slice(0, HUD_SUMMARY_MAX_UNSPACED_CHARS);
|
||||
const next = graphemes[HUD_SUMMARY_MAX_UNSPACED_CHARS];
|
||||
const inWord = (g) => /^[\p{Script=Latin}\p{N}]/u.test(g ?? '');
|
||||
if (inWord(head.at(-1)) && inWord(next)) {
|
||||
let start = head.length - 1;
|
||||
while (start > 0 && inWord(head[start - 1])) start -= 1;
|
||||
// A Latin word longer than the cap is cut where the cap falls.
|
||||
if (start > 0) return head.slice(0, start).join('').trim();
|
||||
// Japanese and Chinese have no spaces to count words by: cap characters.
|
||||
if (hudSummaryIsUnspaced(locale))
|
||||
return capAtWord(words.join(' '), HUD_SUMMARY_MAX_UNSPACED_CHARS, locale);
|
||||
return capAtWord(words.slice(0, 5).join(' '), HUD_SUMMARY_MAX_CHARS, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut `text` to `max` characters at a word boundary; a first word longer
|
||||
* than that is cut where the cap falls.
|
||||
*/
|
||||
function capAtWord(text, max, locale) {
|
||||
const graphemes = (value) => [
|
||||
...new Intl.Segmenter(locale, { granularity: 'grapheme' }).segment(value),
|
||||
];
|
||||
if (graphemes(text).length <= max) return text;
|
||||
let kept = '';
|
||||
for (const { segment } of new Intl.Segmenter(locale, {
|
||||
granularity: 'word',
|
||||
}).segment(text)) {
|
||||
if (graphemes(kept + segment).length > max) break;
|
||||
kept += segment;
|
||||
}
|
||||
return head.join('').trim();
|
||||
return (
|
||||
kept.trim() ||
|
||||
graphemes(text)
|
||||
.slice(0, max)
|
||||
.map(({ segment }) => segment)
|
||||
.join('')
|
||||
);
|
||||
}
|
||||
|
||||
async function handleHudSummary(req, res) {
|
||||
|
||||
Reference in new issue
Block a user