Skip to main content

agent_tools_interface/core/
response.rs

1use jsonpath_rust::JsonPath;
2use serde_json::Value;
3use thiserror::Error;
4
5use crate::core::manifest::{ResponseConfig, ResponseFormat};
6
7#[derive(Error, Debug)]
8pub enum ResponseError {
9    #[error("JSONPath extraction failed: {0}")]
10    ExtractionFailed(String),
11}
12
13/// Extract and format a response based on the tool's response config.
14pub fn process_response(
15    response: &Value,
16    config: Option<&ResponseConfig>,
17) -> Result<Value, ResponseError> {
18    let config = match config {
19        Some(c) => c,
20        None => return Ok(response.clone()), // No config = passthrough
21    };
22
23    // Apply JSONPath extraction if specified
24    let extracted = match &config.extract {
25        Some(path_str) => extract_jsonpath(response, path_str)?,
26        None => response.clone(),
27    };
28
29    Ok(extracted)
30}
31
32/// Apply a JSONPath expression to extract data from a JSON value.
33fn extract_jsonpath(value: &Value, path: &str) -> Result<Value, ResponseError> {
34    let path = JsonPath::try_from(path)
35        .map_err(|e| ResponseError::ExtractionFailed(format!("Invalid JSONPath '{path}': {e}")))?;
36
37    let results = path.find_slice(value);
38
39    if results.is_empty() {
40        return Ok(Value::Null);
41    }
42
43    // Convert JsonPathValue items to owned Values
44    let values: Vec<Value> = results.into_iter().map(|v| v.to_data()).collect();
45
46    if values.len() == 1 {
47        Ok(values.into_iter().next().unwrap())
48    } else {
49        Ok(Value::Array(values))
50    }
51}
52
53/// Determine the response format from config, with auto-detection fallback.
54pub fn get_format(config: Option<&ResponseConfig>, value: &Value) -> ResponseFormat {
55    if let Some(config) = config {
56        return config.format.clone();
57    }
58
59    // Auto-detect: arrays of objects → table, otherwise text
60    if let Value::Array(arr) = value {
61        if arr.iter().all(|v| v.is_object()) {
62            return ResponseFormat::MarkdownTable;
63        }
64    }
65
66    ResponseFormat::Text
67}