Created
December 11, 2025 15:01
-
-
Save AndyCross/86a29ad97f8f24fb37b61f8bff72f4a0 to your computer and use it in GitHub Desktop.
Partial Json Parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Partial JSON parser for streaming LLM responses. | |
| * Extracts the "response" field from incomplete JSON as it streams in. | |
| * | |
| * Expected format (wrapped in markdown code fences): | |
| * ```json | |
| * {"response": "...", "language": "...", "suggestedCasetas": [...], "suggestedMenus": [...]} | |
| * ``` | |
| */ | |
| /** | |
| * Extract the response text from a partial JSON string. | |
| * Handles the markdown code fence wrapper and incomplete JSON. | |
| */ | |
| export function extractResponseFromPartialJson(partialText: string): string | null { | |
| // Remove markdown code fences if present | |
| let json = partialText; | |
| // Strip ```json prefix | |
| const jsonStart = json.indexOf('```json'); | |
| if (jsonStart !== -1) { | |
| json = json.substring(jsonStart + 7); | |
| } | |
| // Strip trailing ``` if present | |
| const jsonEnd = json.lastIndexOf('```'); | |
| if (jsonEnd !== -1) { | |
| json = json.substring(0, jsonEnd); | |
| } | |
| json = json.trim(); | |
| if (!json) return null; | |
| // Find the "response" field | |
| const responseKeyIndex = json.indexOf('"response"'); | |
| if (responseKeyIndex === -1) return null; | |
| // Find the colon after "response" | |
| const colonIndex = json.indexOf(':', responseKeyIndex + 10); | |
| if (colonIndex === -1) return null; | |
| // Find the opening quote of the value | |
| const valueStart = json.indexOf('"', colonIndex); | |
| if (valueStart === -1) return null; | |
| // Extract the string value, handling escape sequences | |
| let result = ''; | |
| let i = valueStart + 1; | |
| let escaped = false; | |
| while (i < json.length) { | |
| const char = json[i]; | |
| if (escaped) { | |
| // Handle escape sequences | |
| switch (char) { | |
| case 'n': result += '\n'; break; | |
| case 'r': result += '\r'; break; | |
| case 't': result += '\t'; break; | |
| case '"': result += '"'; break; | |
| case '\\': result += '\\'; break; | |
| case '/': result += '/'; break; | |
| case 'b': result += '\b'; break; | |
| case 'f': result += '\f'; break; | |
| case 'u': | |
| // Unicode escape \uXXXX | |
| if (i + 4 < json.length) { | |
| const hex = json.substring(i + 1, i + 5); | |
| const code = parseInt(hex, 16); | |
| if (!isNaN(code)) { | |
| result += String.fromCharCode(code); | |
| i += 4; | |
| } | |
| } | |
| break; | |
| default: | |
| // Unknown escape, just add the char | |
| result += char; | |
| } | |
| escaped = false; | |
| } else if (char === '\\') { | |
| escaped = true; | |
| } else if (char === '"') { | |
| // End of string value - this is the complete response | |
| break; | |
| } else { | |
| result += char; | |
| } | |
| i++; | |
| } | |
| return result; | |
| } | |
| /** | |
| * Try to parse complete JSON and extract structured data. | |
| * Returns null if JSON is not complete/valid. | |
| */ | |
| export interface ParsedResponse { | |
| response: string; | |
| language?: string; | |
| suggestedCasetas?: Array<{ id: string; name: string; number: string }>; | |
| suggestedMenus?: Array<{ id: string; name: string; price?: string; casetaId?: string }>; | |
| } | |
| export function tryParseCompleteJson(text: string): ParsedResponse | null { | |
| // Remove markdown code fences | |
| let json = text; | |
| const jsonStart = json.indexOf('```json'); | |
| if (jsonStart !== -1) { | |
| json = json.substring(jsonStart + 7); | |
| } | |
| const jsonEnd = json.lastIndexOf('```'); | |
| if (jsonEnd !== -1) { | |
| json = json.substring(0, jsonEnd); | |
| } | |
| json = json.trim(); | |
| if (!json) return null; | |
| try { | |
| const parsed = JSON.parse(json); | |
| if (typeof parsed.response === 'string') { | |
| return { | |
| response: parsed.response, | |
| language: parsed.language, | |
| suggestedCasetas: parsed.suggestedCasetas, | |
| suggestedMenus: parsed.suggestedMenus, | |
| }; | |
| } | |
| } catch { | |
| // JSON not complete yet | |
| } | |
| return null; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment