Skip to content

Commit a211ead

Browse files
j-mendezclaude
andcommitted
fix: extract JSON from LLM responses with reasoning text
The best_effort_parse_json_object function now handles JSON blocks anywhere in the response text, not just at the start. This fixes extraction when models output reasoning/thinking before the JSON. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 4e0a9e8 commit a211ead

File tree

1 file changed

+44
-11
lines changed

1 file changed

+44
-11
lines changed

spider/src/features/automation.rs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1990,30 +1990,63 @@ fn extract_usage(root: &Value) -> AutomationUsage {
19901990
/// Best effort parse the json object.
19911991
#[cfg(feature = "chrome")]
19921992
fn best_effort_parse_json_object(s: &str) -> EngineResult<Value> {
1993+
// Try direct parse first
19931994
if let Ok(v) = serde_json::from_str::<Value>(s) {
19941995
return Ok(v);
19951996
}
19961997

19971998
let trimmed = s.trim();
1998-
let unfenced = trimmed
1999-
.strip_prefix("```json")
2000-
.or_else(|| trimmed.strip_prefix("```"))
2001-
.map(|x| x.trim())
2002-
.unwrap_or(trimmed);
2003-
let unfenced = unfenced
2004-
.strip_suffix("```")
2005-
.map(|x| x.trim())
2006-
.unwrap_or(unfenced);
1999+
2000+
// Try to find ```json block anywhere in the text (handles reasoning before JSON)
2001+
let unfenced = if let Some(start) = trimmed.find("```json") {
2002+
let after_marker = &trimmed[start + 7..];
2003+
if let Some(end) = after_marker.find("```") {
2004+
after_marker[..end].trim()
2005+
} else {
2006+
after_marker.trim()
2007+
}
2008+
} else if let Some(start) = trimmed.find("```") {
2009+
// Try generic code block
2010+
let after_marker = &trimmed[start + 3..];
2011+
if let Some(end) = after_marker.find("```") {
2012+
after_marker[..end].trim()
2013+
} else {
2014+
after_marker.trim()
2015+
}
2016+
} else {
2017+
// Fallback: strip prefix/suffix if at boundaries
2018+
let unfenced = trimmed
2019+
.strip_prefix("```json")
2020+
.or_else(|| trimmed.strip_prefix("```"))
2021+
.map(|x| x.trim())
2022+
.unwrap_or(trimmed);
2023+
unfenced
2024+
.strip_suffix("```")
2025+
.map(|x| x.trim())
2026+
.unwrap_or(unfenced)
2027+
};
20072028

20082029
if let Ok(v) = serde_json::from_str::<Value>(unfenced) {
20092030
return Ok(v);
20102031
}
20112032

2033+
// Try to find JSON object/array directly in the text
20122034
if let (Some(a), Some(b)) = (unfenced.find('{'), unfenced.rfind('}')) {
20132035
if b > a {
20142036
let slice = &unfenced[a..=b];
2015-
let v = serde_json::from_str::<Value>(slice)?;
2016-
return Ok(v);
2037+
if let Ok(v) = serde_json::from_str::<Value>(slice) {
2038+
return Ok(v);
2039+
}
2040+
}
2041+
}
2042+
2043+
// Try array as well
2044+
if let (Some(a), Some(b)) = (unfenced.find('['), unfenced.rfind(']')) {
2045+
if b > a {
2046+
let slice = &unfenced[a..=b];
2047+
if let Ok(v) = serde_json::from_str::<Value>(slice) {
2048+
return Ok(v);
2049+
}
20172050
}
20182051
}
20192052

0 commit comments

Comments
 (0)