char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Aleksander Grygierui: Linting & Formatting scripts (#26819)92d1bb0c9

char/aliceai-t5-moe
3.6 KiB124 linesraw
1// JSON delimiters used while scanning partial streamed JSON. Single-char
2// tokens so they only need eq-comparison, but naming them keeps the
3// scanner readable and keeps the literal source-of-truth in one place.
4const JSON_QUOTE = '"';
5const JSON_BACKSLASH = '\\';
6const JSON_OBJECT_OPEN = '{';
7const JSON_OBJECT_CLOSE = '}';
8const JSON_ARRAY_OPEN = '[';
9const JSON_ARRAY_CLOSE = ']';
10// Trailing punctuation to strip before re-closing a partial object/array.
11// Matches an optional trailing comma plus any trailing whitespace; lets
12// us re-emit a syntactically-valid JSON document without an orphaned
13// comma when the model cut off mid-key.
14const TRAILING_JSON_PUNCTUATION_REGEX = /,?\s*$/;
15/** Bounded cache for parsePartialJsonArgs results. */
16const PARTIAL_JSON_CACHE_MAX_SIZE = 32;
17const partialJsonCache = new Map<string, Record<string, unknown> | null>();
18
19function cacheResult(input: string, result: Record<string, unknown> | null): void {
20	if (partialJsonCache.size >= PARTIAL_JSON_CACHE_MAX_SIZE) {
21		partialJsonCache.delete(partialJsonCache.keys().next().value!);
22	}
23
24	partialJsonCache.set(input, result);
25}
26
27// Parse partial tool-arg JSON streamed token-by-token. Closes any
28// unterminated string and dangling open containers (in reverse order),
29// so parsers can still surface keys already received while the call
30// is still in flight. Memoized: the char-by-char scanner runs on every
31// render during streaming even when toolArgs hasn't changed.
32export function parsePartialJsonArgs(toolArgsString: string): Record<string, unknown> | null {
33	const cached = partialJsonCache.get(toolArgsString);
34
35	if (cached !== undefined) return cached;
36
37	let result: Record<string, unknown> | null;
38
39	try {
40		const parsed: unknown = JSON.parse(toolArgsString);
41
42		result =
43			parsed && typeof parsed === 'object' && !Array.isArray(parsed)
44				? (parsed as Record<string, unknown>)
45				: null;
46	} catch {
47		result = scanPartialJson(toolArgsString);
48	}
49
50	cacheResult(toolArgsString, result);
51
52	return result;
53}
54
55/** Char-by-char scanner for unterminated partial JSON. */
56function scanPartialJson(toolArgsString: string): Record<string, unknown> | null {
57	let inString = false;
58	let escape = false;
59
60	const stack: ('{' | '[')[] = [];
61
62	for (let i = 0; i < toolArgsString.length; i++) {
63		const ch = toolArgsString[i];
64
65		if (escape) {
66			escape = false;
67
68			continue;
69		}
70
71		if (ch === JSON_BACKSLASH && inString) {
72			escape = true;
73
74			continue;
75		}
76
77		if (ch === JSON_QUOTE) {
78			inString = !inString;
79
80			continue;
81		}
82
83		if (inString) continue;
84
85		if (ch === JSON_OBJECT_OPEN) stack.push(JSON_OBJECT_OPEN);
86		else if (ch === JSON_OBJECT_CLOSE) {
87			if (stack.length === 0 || stack[stack.length - 1] !== JSON_OBJECT_OPEN) return null;
88
89			stack.pop();
90		} else if (ch === JSON_ARRAY_OPEN) stack.push(JSON_ARRAY_OPEN);
91		else if (ch === JSON_ARRAY_CLOSE) {
92			if (stack.length === 0 || stack[stack.length - 1] !== JSON_ARRAY_OPEN) return null;
93
94			stack.pop();
95		}
96	}
97
98	let completed = toolArgsString;
99
100	if (escape) {
101		// Dangling escape at end of partial JSON: escape the trailing
102		// backslash as a literal so we can close the string cleanly.
103		completed += JSON_BACKSLASH;
104	}
105
106	if (inString) completed += JSON_QUOTE;
107
108	if (!inString) completed = completed.replace(TRAILING_JSON_PUNCTUATION_REGEX, '');
109
110	// Close in reverse nesting order: innermost container first.
111	for (let i = stack.length - 1; i >= 0; i--) {
112		completed += stack[i] === JSON_OBJECT_OPEN ? JSON_OBJECT_CLOSE : JSON_ARRAY_CLOSE;
113	}
114
115	try {
116		const parsed: unknown = JSON.parse(completed);
117
118		return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
119			? (parsed as Record<string, unknown>)
120			: null;
121	} catch {
122		return null;
123	}
124}