char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
92d1bb0c9
char/aliceai-t5-moe
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 { 20if ( partialJsonCache . size >= PARTIAL_JSON_CACHE_MAX_SIZE ) { 21partialJsonCache . delete ( partialJsonCache . keys (). next (). value ! ); 22} 23 24partialJsonCache . 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 { 33const cached = partialJsonCache . get ( toolArgsString ); 34 35if ( cached !== undefined ) return cached ; 36 37let result :Record < string , unknown > | null ; 38 39try { 40const parsed :unknown = JSON . parse ( toolArgsString ); 41 42result = 43parsed && typeof parsed === 'object' && ! Array . isArray ( parsed ) 44 ?( parsed as Record < string , unknown >) 45 :null ; 46} catch { 47result = scanPartialJson ( toolArgsString ); 48} 49 50cacheResult ( toolArgsString , result ); 51 52return result ; 53} 54 55/** Char-by-char scanner for unterminated partial JSON. */ 56function scanPartialJson ( toolArgsString :string ) :Record < string , unknown > | null { 57let inString = false ; 58let escape = false ; 59 60const stack :( '{' | '[' )[] = []; 61 62for ( let i = 0 ; i < toolArgsString . length ; i ++ ) { 63const ch = toolArgsString [ i ]; 64 65if ( escape ) { 66escape = false ; 67 68continue ; 69} 70 71if ( ch === JSON_BACKSLASH && inString ) { 72escape = true ; 73 74continue ; 75} 76 77if ( ch === JSON_QUOTE ) { 78inString = ! inString ; 79 80continue ; 81} 82 83if ( inString ) continue ; 84 85if ( ch === JSON_OBJECT_OPEN ) stack . push ( JSON_OBJECT_OPEN ); 86else if ( ch === JSON_OBJECT_CLOSE ) { 87if ( stack . length === 0 || stack [ stack . length - 1 ] !== JSON_OBJECT_OPEN ) return null ; 88 89stack . pop (); 90} else if ( ch === JSON_ARRAY_OPEN ) stack . push ( JSON_ARRAY_OPEN ); 91else if ( ch === JSON_ARRAY_CLOSE ) { 92if ( stack . length === 0 || stack [ stack . length - 1 ] !== JSON_ARRAY_OPEN ) return null ; 93 94stack . pop (); 95} 96} 97 98let completed = toolArgsString ; 99 100if ( escape ) { 101// Dangling escape at end of partial JSON: escape the trailing 102// backslash as a literal so we can close the string cleanly. 103completed += JSON_BACKSLASH ; 104} 105 106if ( inString ) completed += JSON_QUOTE ; 107 108if ( ! inString ) completed = completed . replace ( TRAILING_JSON_PUNCTUATION_REGEX , '' ); 109 110// Close in reverse nesting order: innermost container first. 111for ( let i = stack . length - 1 ; i >= 0 ; i -- ) { 112completed += stack [ i ] === JSON_OBJECT_OPEN ?JSON_OBJECT_CLOSE :JSON_ARRAY_CLOSE ; 113} 114 115try { 116const parsed :unknown = JSON . parse ( completed ); 117 118return parsed && typeof parsed === 'object' && ! Array . isArray ( parsed ) 119 ?( parsed as Record < string , unknown >) 120 :null ; 121} catch { 122return null ; 123} 124}