feat(ai): enabled literal null string preservation in JSON parsing

- Removed `coerceNullStrings` function and automatic null-string coercion from JSON parsing, changing behavior to preserve literal `"null"` strings.
- Updated `parseStreamingJson` to return parsed JSON without null-string transformation.
This commit is contained in:
can1357
2026-04-06 18:46:07 +02:00
parent 51467abc5a
commit 60e255f167
2 changed files with 6 additions and 27 deletions
+4 -1
View File
@@ -1,6 +1,9 @@
# Changelog
## [Unreleased]
### Breaking Changes
- Removed `coerceNullStrings` function and its automatic null-string coercion behavior from JSON parsing
## [13.19.0] - 2026-04-05
@@ -1975,4 +1978,4 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
## [0.9.4] - 2025-11-26
Initial release with multi-provider LLM support.
Initial release with multi-provider LLM support.
+2 -26
View File
@@ -14,39 +14,15 @@ export function parseStreamingJson<T = any>(partialJson: string | undefined): T
// Try standard parsing first (fastest for complete JSON)
try {
return coerceNullStrings(JSON.parse(partialJson) as T);
return JSON.parse(partialJson) as T;
} catch {
// Try partial-json for incomplete JSON
try {
const result = partialParse(partialJson);
return coerceNullStrings((result ?? {}) as T);
return (result ?? {}) as T;
} catch {
// If all parsing fails, return empty object
return {} as T;
}
}
}
/**
* Recursively replace the literal string `"null"` with `null` in parsed
* tool-call arguments. Many models serialize optional fields as the four
* character string `null` instead of the JSON keyword `null`, which
* downstream code sees as a truthy non-empty string.
*/
export function coerceNullStrings<T>(value: T): T {
if (value === null || value === undefined) return value;
if (typeof value === "string") {
return (value === "null" ? null : value) as T;
}
if (Array.isArray(value)) {
return value.map(coerceNullStrings) as T;
}
if (typeof value === "object") {
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
out[k] = coerceNullStrings(v);
}
return out as T;
}
return value;
}