mirror of https://github.com/buster-so/buster.git
metric fixes
This commit is contained in:
parent
081cabc4c6
commit
db220b7fd5
|
@ -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()
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -27,7 +27,7 @@ pub struct MetricYml {
|
|||
pub dataset_ids: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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::<BarLineChartConfig>(config_value) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
return Err(detailed_deserialization_error::<D::Error>("Bar chart", &e));
|
||||
}
|
||||
};
|
||||
Ok(ChartConfig::Bar(config))
|
||||
}
|
||||
"line" => {
|
||||
let config = match serde_json::from_value::<BarLineChartConfig>(config_value) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
return Err(detailed_deserialization_error::<D::Error>("Line chart", &e));
|
||||
}
|
||||
};
|
||||
Ok(ChartConfig::Line(config))
|
||||
}
|
||||
"scatter" => {
|
||||
let config = match serde_json::from_value::<ScatterChartConfig>(config_value) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
return Err(detailed_deserialization_error::<D::Error>(
|
||||
"Scatter chart",
|
||||
&e,
|
||||
));
|
||||
}
|
||||
};
|
||||
Ok(ChartConfig::Scatter(config))
|
||||
}
|
||||
"pie" => {
|
||||
let config = match serde_json::from_value::<PieChartConfig>(config_value) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
return Err(detailed_deserialization_error::<D::Error>("Pie chart", &e));
|
||||
}
|
||||
};
|
||||
Ok(ChartConfig::Pie(config))
|
||||
}
|
||||
"combo" => {
|
||||
let config = match serde_json::from_value::<ComboChartConfig>(config_value) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
return Err(detailed_deserialization_error::<D::Error>(
|
||||
"Combo chart",
|
||||
&e,
|
||||
));
|
||||
}
|
||||
};
|
||||
Ok(ChartConfig::Combo(config))
|
||||
}
|
||||
"metric" => {
|
||||
let config = match serde_json::from_value::<MetricChartConfig>(config_value) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
return Err(detailed_deserialization_error::<D::Error>(
|
||||
"Metric chart",
|
||||
&e,
|
||||
));
|
||||
}
|
||||
};
|
||||
Ok(ChartConfig::Metric(config))
|
||||
}
|
||||
"table" => {
|
||||
let config = match serde_json::from_value::<TableChartConfig>(config_value) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
return Err(detailed_deserialization_error::<D::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<E: serde::de::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<String> {
|
||||
// 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<String> {
|
||||
// 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")]
|
||||
|
|
Loading…
Reference in New Issue