diff --git a/api/libs/agents/src/agent.rs b/api/libs/agents/src/agent.rs index 792897708..23b018c4f 100644 --- a/api/libs/agents/src/agent.rs +++ b/api/libs/agents/src/agent.rs @@ -618,7 +618,7 @@ impl Agent { session_id: thread.id.to_string(), trace_id: Uuid::new_v4().to_string(), }), - reasoning_effort: Some("low".to_string()), + reasoning_effort: Some("medium".to_string()), ..Default::default() }; diff --git a/api/libs/agents/src/tools/categories/file_tools/common.rs b/api/libs/agents/src/tools/categories/file_tools/common.rs index b35f4a5a6..51407d514 100644 --- a/api/libs/agents/src/tools/categories/file_tools/common.rs +++ b/api/libs/agents/src/tools/categories/file_tools/common.rs @@ -246,6 +246,7 @@ definitions: type: array items: type: string + description: Default color palette for the chart. Use this parameter when the user asks about customizing chart colors, unless specified otherwise. showLegend: type: boolean gridLines: diff --git a/api/libs/database/src/types/metric_yml.rs b/api/libs/database/src/types/metric_yml.rs index b2efd2004..d4e06c531 100644 --- a/api/libs/database/src/types/metric_yml.rs +++ b/api/libs/database/src/types/metric_yml.rs @@ -27,7 +27,7 @@ pub struct MetricYml { pub dataset_ids: Vec, } -#[derive(Debug, Serialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(tag = "selectedChartType")] #[serde(rename_all = "camelCase")] pub enum ChartConfig { @@ -47,242 +47,6 @@ pub enum ChartConfig { Table(TableChartConfig), } -impl<'de> Deserialize<'de> for ChartConfig { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - // Deserialize into a generic JSON value - let value: Value = Deserialize::deserialize(deserializer)?; - - // Ensure it's an object - let obj = value - .as_object() - .ok_or_else(|| serde::de::Error::custom("expected a JSON object"))?; - - // Look for the tag under either key - let tag = obj - .get("selectedChartType") - .or_else(|| obj.get("selected_chart_type")) - .and_then(|v| v.as_str()) - .ok_or_else(|| { - serde::de::Error::custom( - "missing or invalid 'selectedChartType' or 'selected_chart_type' field", - ) - })?; - - // Clone the object and remove both possible tag fields - let mut obj_clone = obj.clone(); - obj_clone.remove("selectedChartType"); - obj_clone.remove("selected_chart_type"); - let config_value = Value::Object(obj_clone); - - // Match the tag to the appropriate variant - match tag { - "bar" => { - let config = match serde_json::from_value::(config_value) { - Ok(config) => config, - Err(e) => { - return Err(detailed_deserialization_error::("Bar chart", &e)); - } - }; - Ok(ChartConfig::Bar(config)) - } - "line" => { - let config = match serde_json::from_value::(config_value) { - Ok(config) => config, - Err(e) => { - return Err(detailed_deserialization_error::("Line chart", &e)); - } - }; - Ok(ChartConfig::Line(config)) - } - "scatter" => { - let config = match serde_json::from_value::(config_value) { - Ok(config) => config, - Err(e) => { - return Err(detailed_deserialization_error::( - "Scatter chart", - &e, - )); - } - }; - Ok(ChartConfig::Scatter(config)) - } - "pie" => { - let config = match serde_json::from_value::(config_value) { - Ok(config) => config, - Err(e) => { - return Err(detailed_deserialization_error::("Pie chart", &e)); - } - }; - Ok(ChartConfig::Pie(config)) - } - "combo" => { - let config = match serde_json::from_value::(config_value) { - Ok(config) => config, - Err(e) => { - return Err(detailed_deserialization_error::( - "Combo chart", - &e, - )); - } - }; - Ok(ChartConfig::Combo(config)) - } - "metric" => { - let config = match serde_json::from_value::(config_value) { - Ok(config) => config, - Err(e) => { - return Err(detailed_deserialization_error::( - "Metric chart", - &e, - )); - } - }; - Ok(ChartConfig::Metric(config)) - } - "table" => { - let config = match serde_json::from_value::(config_value) { - Ok(config) => config, - Err(e) => { - return Err(detailed_deserialization_error::( - "Table chart", - &e, - )); - } - }; - Ok(ChartConfig::Table(config)) - } - unknown => Err(serde::de::Error::custom(format!( - "unknown chart type: {}", - unknown - ))), - } - } -} - -// Helper function to create detailed error messages for deserialization failures -fn detailed_deserialization_error( - chart_type: &str, - err: &serde_json::Error, -) -> E { - // Get the error message as a string - let err_msg = err.to_string(); - - // Match different error patterns to extract field information - let detailed_error = match extract_detailed_error_info(&err_msg) { - Some((field_name, expected_type, found_type)) => { - // Complete type mismatch info available - format!( - "{} config error: field '{}' has invalid type: {}, expected {}", - chart_type, field_name, found_type, expected_type - ) - } - None => { - // Try to at least extract the field name - if let Some(field_name) = extract_field_from_error(&err_msg) { - format!( - "{} config error at field '{}': {}", - chart_type, field_name, err - ) - } else { - format!("{} config error: {}", chart_type, err) - } - } - }; - - E::custom(detailed_error) -} - -// Helper function to extract detailed type mismatch information -fn extract_detailed_error_info(err_msg: &str) -> Option<(String, String, String)> { - // Pattern for serde "invalid type" errors: "invalid type: X, expected Y at ..." - if err_msg.starts_with("invalid type:") { - // Extract expected and found types - let parts: Vec<&str> = err_msg.split(", expected ").collect(); - if parts.len() >= 2 { - let found_type = parts[0].replace("invalid type: ", "").trim().to_string(); - - // Extract field path and expected type - let remaining = parts[1]; - let field_parts: Vec<&str> = remaining.split(" at ").collect(); - let expected_type = field_parts[0].trim().to_string(); - - // Try to extract field name - if field_parts.len() >= 2 { - if let Some(field_name) = extract_field_path(field_parts[1]) { - return Some((field_name, expected_type, found_type)); - } - } - - // If we couldn't extract field but have types, use a placeholder - return Some(("unknown_field".to_string(), expected_type, found_type)); - } - } - - None -} - -// Helper function to extract field path from location info -fn extract_field_path(location_info: &str) -> Option { - // Try to extract field name from different patterns - - // Pattern: key `field_name` at line X column Y - if let Some(start_idx) = location_info.find("key `") { - if let Some(end_idx) = location_info[start_idx + 5..].find('`') { - return Some(location_info[start_idx + 5..start_idx + 5 + end_idx].to_string()); - } - } - - // Pattern: at key "field_name" - if let Some(start_idx) = location_info.find("at key \"") { - if let Some(end_idx) = location_info[start_idx + 8..].find('"') { - return Some(location_info[start_idx + 8..start_idx + 8 + end_idx].to_string()); - } - } - - // Pattern: "field_name": - if let Some(start_idx) = location_info.find('"') { - if let Some(end_idx) = location_info[start_idx + 1..].find('"') { - return Some(location_info[start_idx + 1..start_idx + 1 + end_idx].to_string()); - } - } - - None -} - -// Helper function to extract field name from error message -fn extract_field_from_error(err_msg: &str) -> Option { - // Try multiple patterns to extract field names - - // Pattern: key `field_name` at line X column Y - if let Some(start_idx) = err_msg.find("key `") { - if let Some(end_idx) = err_msg[start_idx + 5..].find('`') { - return Some(err_msg[start_idx + 5..start_idx + 5 + end_idx].to_string()); - } - } - - // Pattern: at key "field_name" - if let Some(start_idx) = err_msg.find("at key \"") { - if let Some(end_idx) = err_msg[start_idx + 8..].find('"') { - return Some(err_msg[start_idx + 8..start_idx + 8 + end_idx].to_string()); - } - } - - // Pattern: "field_name": - if let Some(start_idx) = err_msg.find('"') { - if let Some(end_idx) = err_msg[start_idx + 1..].find('"') { - let field = &err_msg[start_idx + 1..start_idx + 1 + end_idx]; - // Avoid capturing things that aren't likely field names - if !field.contains(' ') && field.len() > 0 && field.len() < 50 { - return Some(field.to_string()); - } - } - } - - None -} #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")]