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:
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user