fix(bridge): parse JSON tool output to prevent double-serialization

From trace analysis: web_search returned a JSON string, which was
wrapped as serde_json::json!(string) creating a Value::String containing
JSON. When Monty got this as MontyObject::String, the Python code
couldn't index it with result['title'] → TypeError.

Fix: try parsing the tool output string as JSON first. If valid, use the
parsed Value (becomes a Python dict/list). If not valid JSON, keep as
string. This means web_search results are directly indexable in Python:
  results = web_search(query="...")
  print(results["results"][0]["title"])  # works now

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
2026-03-22 23:08:52 -07:00
co-authored by Claude Opus 4.6
parent d8f01693f4
commit e8c0d3df52
+15 -7
View File
@@ -60,13 +60,21 @@ impl EffectExecutor for EffectBridgeAdapter {
.await;
match result {
Ok(output) => Ok(ActionResult {
call_id: String::new(), // Caller fills this in
action_name: action_name.to_string(),
output: serde_json::json!(output),
is_error: false,
duration: Duration::from_millis(1), // TODO: measure actual duration
}),
Ok(output) => {
// Tool output is a String. If it's valid JSON, parse it so the
// Python code gets a dict/list instead of a string that needs
// manual parsing. This prevents double-serialization.
let output_value = serde_json::from_str::<serde_json::Value>(&output)
.unwrap_or(serde_json::Value::String(output));
Ok(ActionResult {
call_id: String::new(), // Caller fills this in
action_name: action_name.to_string(),
output: output_value,
is_error: false,
duration: Duration::from_millis(1), // TODO: measure actual duration
})
}
Err(e) => Ok(ActionResult {
call_id: String::new(),
action_name: action_name.to_string(),