json.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. export function parseJsonMarkdown(s, parser = parsePartialJson) {
  2. // eslint-disable-next-line no-param-reassign
  3. s = s.trim();
  4. const firstFenceIndex = s.indexOf("```");
  5. if (firstFenceIndex === -1) {
  6. return parser(s);
  7. }
  8. let contentAfterFence = s.substring(firstFenceIndex + 3);
  9. if (contentAfterFence.startsWith("json\n")) {
  10. contentAfterFence = contentAfterFence.substring(5);
  11. }
  12. else if (contentAfterFence.startsWith("json")) {
  13. contentAfterFence = contentAfterFence.substring(4);
  14. }
  15. else if (contentAfterFence.startsWith("\n")) {
  16. contentAfterFence = contentAfterFence.substring(1);
  17. }
  18. const closingFenceIndex = contentAfterFence.indexOf("```");
  19. let finalContent = contentAfterFence;
  20. if (closingFenceIndex !== -1) {
  21. finalContent = contentAfterFence.substring(0, closingFenceIndex);
  22. }
  23. return parser(finalContent.trim());
  24. }
  25. // Adapted from https://github.com/KillianLucas/open-interpreter/blob/main/interpreter/core/llm/utils/parse_partial_json.py
  26. // MIT License
  27. export function parsePartialJson(s) {
  28. // If the input is undefined, return null to indicate failure.
  29. if (typeof s === "undefined") {
  30. return null;
  31. }
  32. // Attempt to parse the string as-is.
  33. try {
  34. return JSON.parse(s);
  35. }
  36. catch (error) {
  37. // Pass
  38. }
  39. // Initialize variables.
  40. let new_s = "";
  41. const stack = [];
  42. let isInsideString = false;
  43. let escaped = false;
  44. // Process each character in the string one at a time.
  45. for (let char of s) {
  46. if (isInsideString) {
  47. if (char === '"' && !escaped) {
  48. isInsideString = false;
  49. }
  50. else if (char === "\n" && !escaped) {
  51. char = "\\n"; // Replace the newline character with the escape sequence.
  52. }
  53. else if (char === "\\") {
  54. escaped = !escaped;
  55. }
  56. else {
  57. escaped = false;
  58. }
  59. }
  60. else {
  61. if (char === '"') {
  62. isInsideString = true;
  63. escaped = false;
  64. }
  65. else if (char === "{") {
  66. stack.push("}");
  67. }
  68. else if (char === "[") {
  69. stack.push("]");
  70. }
  71. else if (char === "}" || char === "]") {
  72. if (stack && stack[stack.length - 1] === char) {
  73. stack.pop();
  74. }
  75. else {
  76. // Mismatched closing character; the input is malformed.
  77. return null;
  78. }
  79. }
  80. }
  81. // Append the processed character to the new string.
  82. new_s += char;
  83. }
  84. // If we're still inside a string at the end of processing,
  85. // we need to close the string.
  86. if (isInsideString) {
  87. new_s += '"';
  88. }
  89. // Close any remaining open structures in the reverse order that they were opened.
  90. for (let i = stack.length - 1; i >= 0; i -= 1) {
  91. new_s += stack[i];
  92. }
  93. // Attempt to parse the modified string as JSON.
  94. try {
  95. return JSON.parse(new_s);
  96. }
  97. catch (error) {
  98. // If we still can't parse the string as JSON, return null to indicate failure.
  99. return null;
  100. }
  101. }