@falai/agent 3.2.1 → 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
- package/dist/cjs/core/ResponseModal.js +16 -4
- package/dist/cjs/core/ResponseModal.js.map +1 -1
- package/dist/cjs/providers/ProviderAdapter.d.ts.map +1 -1
- package/dist/cjs/providers/ProviderAdapter.js +20 -8
- package/dist/cjs/providers/ProviderAdapter.js.map +1 -1
- package/dist/cjs/utils/json.d.ts +8 -0
- package/dist/cjs/utils/json.d.ts.map +1 -1
- package/dist/cjs/utils/json.js +95 -2
- package/dist/cjs/utils/json.js.map +1 -1
- package/dist/core/ResponseModal.d.ts.map +1 -1
- package/dist/core/ResponseModal.js +17 -5
- package/dist/core/ResponseModal.js.map +1 -1
- package/dist/providers/ProviderAdapter.d.ts.map +1 -1
- package/dist/providers/ProviderAdapter.js +20 -8
- package/dist/providers/ProviderAdapter.js.map +1 -1
- package/dist/utils/json.d.ts +8 -0
- package/dist/utils/json.d.ts.map +1 -1
- package/dist/utils/json.js +94 -2
- package/dist/utils/json.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ResponseModal.ts +16 -5
- package/src/providers/ProviderAdapter.ts +21 -7
- package/src/utils/json.ts +93 -2
package/src/utils/json.ts
CHANGED
|
@@ -2,12 +2,103 @@
|
|
|
2
2
|
* JSON parsing utilities
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
/** JSON's own short escapes for the control characters a model actually types. */
|
|
6
|
+
const CONTROL_ESCAPES: Record<string, string> = {
|
|
7
|
+
"\n": "\\n",
|
|
8
|
+
"\r": "\\r",
|
|
9
|
+
"\t": "\\t",
|
|
10
|
+
"\b": "\\b",
|
|
11
|
+
"\f": "\\f",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Escape the raw control characters a model left unescaped inside a string
|
|
16
|
+
* literal.
|
|
17
|
+
*
|
|
18
|
+
* JSON forbids a literal newline between quotes. A model whose decoder is
|
|
19
|
+
* pinned to the schema cannot break that rule, but one merely ASKED for the
|
|
20
|
+
* envelope in its prompt — which is how a schema rides on any call that also
|
|
21
|
+
* carries tools — breaks it constantly, because it pretty-prints the reply it
|
|
22
|
+
* would have sent:
|
|
23
|
+
*
|
|
24
|
+
* {
|
|
25
|
+
* "message": "Boa escolha!
|
|
26
|
+
* À vista: R$ 3.149"
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* `JSON.parse` calls that an unterminated string and gives up, and the whole
|
|
30
|
+
* envelope travels on as the user-visible reply. Re-escaping the control
|
|
31
|
+
* characters makes it parse into exactly what the model meant.
|
|
32
|
+
*
|
|
33
|
+
* A string ends at the next unescaped quote, so a stray quote inside the
|
|
34
|
+
* message shifts the boundary and the result fails to parse. That is the
|
|
35
|
+
* intended outcome: a wrong guess must never become a reply.
|
|
36
|
+
*/
|
|
37
|
+
function escapeControlCharsInStrings(text: string): string {
|
|
38
|
+
let out = "";
|
|
39
|
+
let inString = false;
|
|
40
|
+
let escaped = false;
|
|
41
|
+
|
|
42
|
+
for (const ch of text) {
|
|
43
|
+
if (!inString) {
|
|
44
|
+
if (ch === '"') inString = true;
|
|
45
|
+
out += ch;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (escaped) {
|
|
49
|
+
escaped = false;
|
|
50
|
+
out += ch;
|
|
51
|
+
} else if (ch === "\\") {
|
|
52
|
+
escaped = true;
|
|
53
|
+
out += ch;
|
|
54
|
+
} else if (ch === '"') {
|
|
55
|
+
inString = false;
|
|
56
|
+
out += ch;
|
|
57
|
+
} else if (ch < " ") {
|
|
58
|
+
out += CONTROL_ESCAPES[ch] ?? `\\u${ch.charCodeAt(0).toString(16).padStart(4, "0")}`;
|
|
59
|
+
} else {
|
|
60
|
+
out += ch;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Parse strictly, then once more with {@link escapeControlCharsInStrings}.
|
|
69
|
+
* Throws when neither reading is valid JSON.
|
|
70
|
+
*/
|
|
71
|
+
function parseLenient(text: string): unknown {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(text);
|
|
74
|
+
} catch (strictError) {
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(escapeControlCharsInStrings(text));
|
|
77
|
+
} catch {
|
|
78
|
+
// The repair is a second reading of the same text, not a different
|
|
79
|
+
// dialect — so the first violation is the one worth reporting.
|
|
80
|
+
throw strictError;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Whether text is shaped like a protocol envelope rather than a reply to a
|
|
87
|
+
* person — an opening brace or a code fence. Text that fails to parse AND
|
|
88
|
+
* looks like this is never user-worthy: it is a broken envelope, and showing
|
|
89
|
+
* it is the leak this module exists to prevent.
|
|
90
|
+
*/
|
|
91
|
+
export function isJSONShaped(text: string): boolean {
|
|
92
|
+
return /^\s*(```|\{)/.test(text);
|
|
93
|
+
}
|
|
94
|
+
|
|
5
95
|
/**
|
|
6
96
|
* Clean and parse JSON response that might be wrapped in markdown code blocks
|
|
7
97
|
* Handles cases like:
|
|
8
98
|
* - ```json\n{...}\n```
|
|
9
99
|
* - ```\n{...}\n```
|
|
10
100
|
* - Plain JSON: {...}
|
|
101
|
+
* - An object whose string values carry unescaped newlines
|
|
11
102
|
*/
|
|
12
103
|
export function parseJSONResponse(text: string): unknown {
|
|
13
104
|
if (!text || typeof text !== 'string') {
|
|
@@ -28,7 +119,7 @@ export function parseJSONResponse(text: string): unknown {
|
|
|
28
119
|
|
|
29
120
|
// Try to parse the cleaned JSON
|
|
30
121
|
try {
|
|
31
|
-
return
|
|
122
|
+
return parseLenient(cleaned);
|
|
32
123
|
} catch (error) {
|
|
33
124
|
throw new Error(`Failed to parse JSON response: ${error instanceof Error ? error.message : String(error)}\nContent: ${cleaned.substring(0, 200)}...`);
|
|
34
125
|
}
|
|
@@ -66,7 +157,7 @@ export function extractEmbeddedJSONObject(text: string): Record<string, unknown>
|
|
|
66
157
|
if (end === -1) continue;
|
|
67
158
|
|
|
68
159
|
try {
|
|
69
|
-
const parsed =
|
|
160
|
+
const parsed = parseLenient(text.slice(start, end + 1));
|
|
70
161
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
71
162
|
return parsed as Record<string, unknown>;
|
|
72
163
|
}
|