ok a few more small tweaks

This commit is contained in:
dal 2025-04-15 17:14:56 -06:00
parent 906b192d9a
commit db11a56c59
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
4 changed files with 116 additions and 107 deletions

View File

@ -105,47 +105,44 @@ pub async fn validate_metric_ids(ids: &[Uuid]) -> Result<Vec<Uuid>> {
pub const METRIC_YML_SCHEMA: &str = r##" pub const METRIC_YML_SCHEMA: &str = r##"
# METRIC CONFIGURATION - YML STRUCTURE # METRIC CONFIGURATION - YML STRUCTURE
# ------------------------------------- # -------------------------------------
# Required top-level fields: # REQUIRED Top-Level Fields: `name`, `description`, `datasetIds`, `timeFrame`, `sql`, `chartConfig`
# #
# name: Your Metric Title # Do NOT use quotes for string values # --- FIELD DETAILS & RULES ---
# description: A detailed description of what this metric measures and how it should be interpreted # Optional, NO quotes # `name`: Human-readable title (e.g., Total Sales).
# datasetIds: # - RULE: Should NOT contain underscores (`_`). Use spaces instead.
# - 123e4567-e89b-12d3-a456-426614174000 # Dataset UUIDs (not names) do not escape with quotes # - RULE: If using colons (`:`) or other special YAML chars, enclose the *entire* string in double quotes (`\"...\"`). Avoid if possible.
# timeFrame: Last 30 days # Human-readable time period covered by the query, NO quotes # `description`: Detailed explanation of the metric.
# sql: | # - RULE: If using colons (`:`) or other special YAML chars, enclose the *entire* string in double quotes (`\"...\"`).
# SELECT # `datasetIds`: Array of Dataset UUIDs this metric uses.
# date, # - RULE: Use standard YAML array syntax (`- uuid`).
# SUM(amount) AS total # - RULE: UUIDs should NEVER be quoted.
# FROM sales # - Example:
# GROUP BY date # datasetIds:
# # - 123e4567-e89b-12d3-a456-426614174000
# chartConfig: # `timeFrame`: Human-readable time period covered by the query (e.g., Last 30 days).
# selectedChartType: bar # One of: bar, line, scatter, pie, combo, metric, table # - RULE: If using colons (`:`) or other special YAML chars, enclose the *entire* string in double quotes (`\"...\"`).
# columnLabelFormats: # REQUIRED - Must define formatting for all columns # `sql`: The SQL query for the metric.
# date: # - RULE: MUST use the pipe `|` block scalar style to preserve formatting and newlines.
# columnType: date # - Example:
# style: date # sql: |
# dateFormat: MMM DD, YYYY # SELECT ...
# total: # `chartConfig`: Visualization settings.
# columnType: number # - RULE: Must contain `selectedChartType` (bar, line, scatter, pie, combo, metric, table).
# style: currency # - RULE: Must contain `columnLabelFormats` defining format for ALL columns in the SQL result.
# currency: USD # - RULE: Must contain ONE chart-specific config block based on `selectedChartType`:
# minimumFractionDigits: 2 # - `barAndLineAxis` (for type: bar, line)
# barAndLineAxis: {...} # Required for bar and line charts OR # - `scatterAxis` (for type: scatter)
# scatterAxis: {...} # Required for scatter charts OR # - `pieChartAxis` (for type: pie)
# pieChartAxis: {...} # Required for pie charts OR # - `comboChartAxis` (for type: combo)
# comboChartAxis: {...} # Required for combo charts OR # - `metricColumnId` (for type: metric)
# metricColumnId: column_id # Required for metric charts # - `tableConfig` (for type: table) - [Optional, if needed beyond basic columns]
# #
# RULES: # --- GENERAL YAML RULES ---
# 1. All arrays should follow the YML array syntax using `-` not `[` and `]` # 1. Use standard YAML syntax (indentation, colons for key-value, `-` for arrays).
# 2. String values generally should NOT use quotes unless they contain special characters (like :, {, }, [, ], ,, &, *, #, ?, |, -, <, >, =, !, %, @, `) or start/end with whitespace. # 2. Quoting: Generally avoid quotes for simple strings. Use double quotes (`"...") ONLY if a string contains special characters (like :, {, }, [, ], ,, &, *, #, ?, |, -, <, >, =, !, %, @, `) or needs to preserve leading/trailing whitespace.
# 3. If a string contains special characters or needs to preserve leading/trailing whitespace, enclose it in double quotes (`"`). Example: `name: "My: Special Metric"`
# 4. Avoid special characters in names and descriptions where possible, but if needed, use quotes as described in rule 3. UUIDs should NEVER be quoted.
# 5. All fields must use standard YAML syntax. Arrays use `-`, strings follow quoting rules above.
# 6. The SQL query MUST use the pipe `|` block scalar style to preserve formatting and newlines.
# ------------------------------------- # -------------------------------------
# --- FORMAL SCHEMA --- (Used for validation, reflects rules above)
type: object type: object
name: Metric Configuration Schema name: Metric Configuration Schema
description: Metric definition with SQL query and visualization settings description: Metric definition with SQL query and visualization settings
@ -154,26 +151,26 @@ properties:
# NAME # NAME
name: name:
type: string type: string
description: Human-readable title (e.g., Total Sales) - do NOT use quotes description: Human-readable title (e.g., Total Sales). NO underscores. Follow quoting rules.
# DESCRIPTION # DESCRIPTION
description: description:
type: string type: string
description: A detailed description of what this metric measures and how it should be interpreted description: Detailed description. Follow quoting rules.
# DATASET IDS # DATASET IDS
datasetIds: datasetIds:
type: array type: array
description: UUIDs of datasets this metric belongs to description: UUIDs of datasets this metric belongs to (NEVER quoted).
items: items:
type: string type: string
format: uuid format: uuid
description: UUID of the dataset (not the dataset name) do not escape with quotes description: Dataset UUID (unquoted)
# TIME FRAME # TIME FRAME
timeFrame: timeFrame:
type: string type: string
description: Human-readable time period covered by the query (e.g., Last 30 days, All time, August 1, 2024 - January 1, 2025, Comparison: August 2025 to August 2024) description: Human-readable time period covered by the query. Follow quoting rules.
# SQL QUERY # SQL QUERY
### SQL Best Practices and Constraints** (when creating new metrics) ### SQL Best Practices and Constraints** (when creating new metrics)
@ -196,22 +193,15 @@ properties:
type: string type: string
description: | description: |
SQL query using YAML pipe syntax (|) SQL query using YAML pipe syntax (|)
The SQL query should be formatted with proper indentation using the YAML pipe (|) syntax. The SQL query should be formatted with proper indentation using the YAML pipe (|) syntax.
This ensures the multi-line SQL is properly parsed while preserving whitespace and newlines. This ensures the multi-line SQL is properly parsed while preserving whitespace and newlines.
Example:
sql: |
SELECT
column1,
column2
FROM table
WHERE condition
# CHART CONFIGURATION # CHART CONFIGURATION
chartConfig: chartConfig:
description: Visualization settings (must match one chart type) description: Visualization settings (must include selectedChartType, columnLabelFormats, and ONE chart-specific block)
oneOf: # REQUIRED allOf: # Base requirements for ALL chart types
- $ref: '#/definitions/base_chart_config'
oneOf: # Specific block required based on type
- $ref: #/definitions/bar_line_chart_config - $ref: #/definitions/bar_line_chart_config
- $ref: #/definitions/scatter_chart_config - $ref: #/definitions/scatter_chart_config
- $ref: #/definitions/pie_chart_config - $ref: #/definitions/pie_chart_config
@ -227,18 +217,20 @@ required:
- chartConfig - chartConfig
definitions: definitions:
# BASE CHART CONFIG (common to all chart types) # BASE CHART CONFIG (common parts required by ALL chart types)
base_chart_config: base_chart_config:
type: object type: object
properties: properties:
selectedChartType: selectedChartType:
type: string type: string
description: Chart type (bar, line, scatter, pie, combo, metric, table) description: Chart type (bar, line, scatter, pie, combo, metric, table)
enum: [bar, line, scatter, pie, combo, metric, table]
columnLabelFormats: columnLabelFormats:
type: object type: object
description: The formatting for each column. description: REQUIRED formatting for ALL columns returned by the SQL query.
additionalProperties: additionalProperties:
$ref: #/definitions/column_label_format $ref: #/definitions/column_label_format
# Optional base properties below
columnSettings: columnSettings:
type: object type: object
description: Visual settings {columnId: settingsObject} description: Visual settings {columnId: settingsObject}
@ -248,7 +240,10 @@ definitions:
type: array type: array
items: items:
type: string type: string
description: Default color palette for the chart. Use this parameter when the user asks about customizing chart colors, unless specified otherwise. description: |
Default color palette.
RULE: Hex color codes (e.g., #FF0000) MUST be enclosed in quotes (e.g., "#FF0000" or '#FF0000') because '#' signifies a comment otherwise. Double quotes are preferred for consistency.
Use this parameter when the user asks about customizing chart colors, unless specified otherwise.
showLegend: showLegend:
type: boolean type: boolean
gridLines: gridLines:
@ -272,6 +267,7 @@ definitions:
columnType: columnType:
type: string type: string
description: number, string, date description: number, string, date
enum: [number, string, date]
style: style:
type: string type: string
enum: enum:
@ -297,10 +293,8 @@ definitions:
description: Value to multiply the number by before display description: Value to multiply the number by before display
prefix: prefix:
type: string type: string
description: Text to display before the value
suffix: suffix:
type: string type: string
description: Text to display after the value
replaceMissingDataWith: replaceMissingDataWith:
description: Value to display when data is missing, this should be set to null as default. description: Value to display when data is missing, this should be set to null as default.
compactNumbers: compactNumbers:
@ -1207,7 +1201,9 @@ mod tests {
#[test] #[test]
fn test_apply_modifications_multiple_matches() { fn test_apply_modifications_multiple_matches() {
// Content with repeated text // Content with repeated text
let content = "name: Test Dashboard\ndescription: Test description\nTest Dashboard is a dashboard for testing"; let content = "name: Test Dashboard
description: Test description
Test Dashboard is a dashboard for testing";
// Modification that would affect two places // Modification that would affect two places
let modifications = vec![Modification { let modifications = vec![Modification {
@ -1227,7 +1223,9 @@ mod tests {
#[test] #[test]
fn test_apply_modifications_empty_content_to_replace() { fn test_apply_modifications_empty_content_to_replace() {
let original_content = "name: test_metric\ntype: counter\ndescription: A test metric"; let original_content = "name: test_metric
type: counter
description: A test metric";
// Test appending content when content_to_replace is empty // Test appending content when content_to_replace is empty
let mods = vec![Modification { let mods = vec![Modification {
@ -1237,17 +1235,25 @@ mod tests {
let result = apply_modifications_to_content(original_content, &mods, "test.yml").unwrap(); let result = apply_modifications_to_content(original_content, &mods, "test.yml").unwrap();
assert_eq!( assert_eq!(
result, result,
"name: test_metric\ntype: counter\ndescription: A test metric\nadditional_field: true" "name: test_metric
type: counter
description: A test metric
additional_field: true"
); );
// Test appending content when original content doesn't end with newline // Test appending content when original content doesn't end with newline
let original_content_no_newline = let original_content_no_newline =
"name: test_metric\ntype: counter\ndescription: A test metric"; "name: test_metric
type: counter
description: A test metric";
let result = let result =
apply_modifications_to_content(original_content_no_newline, &mods, "test.yml").unwrap(); apply_modifications_to_content(original_content_no_newline, &mods, "test.yml").unwrap();
assert_eq!( assert_eq!(
result, result,
"name: test_metric\ntype: counter\ndescription: A test metric\nadditional_field: true" "name: test_metric
type: counter
description: A test metric
additional_field: true"
); );
} }

View File

@ -318,14 +318,16 @@ async fn get_metric_name_description() -> String {
async fn get_metric_yml_description() -> String { async fn get_metric_yml_description() -> String {
if env::var("USE_BRAINTRUST_PROMPTS").is_err() { if env::var("USE_BRAINTRUST_PROMPTS").is_err() {
// Revert to just returning the schema string
return METRIC_YML_SCHEMA.to_string(); return METRIC_YML_SCHEMA.to_string();
} }
let client = BraintrustClient::new(None, "96af8b2b-cf3c-494f-9092-44eb3d5b96ff").unwrap(); let client = BraintrustClient::new(None, "96af8b2b-cf3c-494f-9092-44eb3d5b96ff").unwrap();
match get_prompt_system_message(&client, "54d01b7c-07c9-4c80-8ec7-8026ab8242a9").await { match get_prompt_system_message(&client, "54d01b7c-07c9-4c80-8ec7-8026ab8242a9").await {
Ok(message) => message, Ok(message) => message,
Err(e) => { Err(e) => {
eprintln!("Failed to get prompt system message: {}", e); eprintln!("Failed to get prompt system message: {}", e);
// Revert to just returning the schema string on error
METRIC_YML_SCHEMA.to_string() METRIC_YML_SCHEMA.to_string()
} }
} }

View File

@ -491,6 +491,7 @@ async fn get_modify_metrics_yml_description() -> String {
async fn get_metric_yml_description() -> String { async fn get_metric_yml_description() -> String {
if env::var("USE_BRAINTRUST_PROMPTS").is_err() { if env::var("USE_BRAINTRUST_PROMPTS").is_err() {
// Revert to just returning the schema string plus basic instruction
return format!("The complete new YAML content for the metric, following the metric schema specification. This will replace the entire existing content of the file. Ensure all required fields are present and properly formatted according to the schema.\n\n{}", METRIC_YML_SCHEMA); return format!("The complete new YAML content for the metric, following the metric schema specification. This will replace the entire existing content of the file. Ensure all required fields are present and properly formatted according to the schema.\n\n{}", METRIC_YML_SCHEMA);
} }
@ -499,6 +500,7 @@ async fn get_metric_yml_description() -> String {
Ok(message) => message, Ok(message) => message,
Err(e) => { Err(e) => {
eprintln!("Failed to get prompt system message: {}", e); eprintln!("Failed to get prompt system message: {}", e);
// Revert to just returning the schema string plus basic instruction on error
format!("The complete new YAML content for the metric, following the metric schema specification. This will replace the entire existing content of the file. Ensure all required fields are present and properly formatted according to the schema.\n\n{}", METRIC_YML_SCHEMA) format!("The complete new YAML content for the metric, following the metric schema specification. This will replace the entire existing content of the file. Ensure all required fields are present and properly formatted according to the schema.\n\n{}", METRIC_YML_SCHEMA)
} }
} }

View File

@ -1588,16 +1588,16 @@ fn tool_create_metrics(id: String, content: String, delta_duration: Duration) ->
Ok(result) => result, Ok(result) => result,
Err(e) => { Err(e) => {
println!("Failed to parse CreateMetricFilesOutput: {:?}", e); println!("Failed to parse CreateMetricFilesOutput: {:?}", e);
// Return an error reasoning message // Return an error reasoning message as a File type
return Ok(vec![BusterReasoningMessage::Text(BusterReasoningText { return Ok(vec![BusterReasoningMessage::File(BusterReasoningFile {
id, id,
reasoning_type: "text".to_string(), message_type: "files".to_string(),
title: "Error Creating Metrics".to_string(), title: "Failed to Create Metrics".to_string(),
secondary_title: format!("{} seconds", delta_duration.as_secs()), secondary_title: format!("Error: {}", e),
message: Some(format!("Failed to parse tool output: {}", e)), status: "failed".to_string(), // Set status to failed
message_chunk: None, file_ids: vec![],
status: Some("error".to_string()), files: HashMap::new(),
})]); })]);
} }
}; };
@ -1652,16 +1652,16 @@ fn tool_modify_metrics(id: String, content: String, delta_duration: Duration) ->
Ok(result) => result, Ok(result) => result,
Err(e) => { Err(e) => {
tracing::error!("Failed to parse ModifyFilesOutput: {:?}", e); tracing::error!("Failed to parse ModifyFilesOutput: {:?}", e);
// Return an error reasoning message // Return an error reasoning message as a File type
return Ok(vec![BusterReasoningMessage::Text(BusterReasoningText { return Ok(vec![BusterReasoningMessage::File(BusterReasoningFile {
id, id,
reasoning_type: "text".to_string(), message_type: "files".to_string(),
title: "Error Modifying Metrics".to_string(), title: "Failed to Modify Metrics".to_string(),
secondary_title: format!("{} seconds", delta_duration.as_secs()), secondary_title: format!("Error: {}", e),
message: Some(format!("Failed to parse tool output: {}", e)), status: "failed".to_string(), // Set status to failed
message_chunk: None, file_ids: vec![],
status: Some("error".to_string()), files: HashMap::new(),
})]); })]);
} }
}; };
@ -1715,15 +1715,15 @@ fn tool_create_dashboards(id: String, content: String, delta_duration: Duration)
Ok(result) => result, Ok(result) => result,
Err(e) => { Err(e) => {
println!("Failed to parse CreateDashboardFilesOutput: {:?}", e); println!("Failed to parse CreateDashboardFilesOutput: {:?}", e);
// Return an error reasoning message // Return an error reasoning message as a File type
return Ok(vec![BusterReasoningMessage::Text(BusterReasoningText { return Ok(vec![BusterReasoningMessage::File(BusterReasoningFile {
id, id,
reasoning_type: "text".to_string(), message_type: "files".to_string(),
title: "Error Creating Dashboards".to_string(), title: "Failed to Create Dashboards".to_string(),
secondary_title: format!("{} seconds", delta_duration.as_secs()), secondary_title: format!("Error: {}", e),
message: Some(format!("Failed to parse tool output: {}", e)), status: "failed".to_string(), // Set status to failed
message_chunk: None, file_ids: vec![],
status: Some("error".to_string()), files: HashMap::new(),
})]); })]);
} }
}; };
@ -1779,15 +1779,15 @@ fn tool_modify_dashboards(id: String, content: String, delta_duration: Duration)
Ok(result) => result, Ok(result) => result,
Err(e) => { Err(e) => {
tracing::error!("Failed to parse ModifyFilesOutput: {:?}", e); tracing::error!("Failed to parse ModifyFilesOutput: {:?}", e);
// Return an error reasoning message // Return an error reasoning message as a File type
return Ok(vec![BusterReasoningMessage::Text(BusterReasoningText { return Ok(vec![BusterReasoningMessage::File(BusterReasoningFile {
id, id,
reasoning_type: "text".to_string(), message_type: "files".to_string(),
title: "Error Modifying Dashboards".to_string(), title: "Failed to Modify Dashboards".to_string(),
secondary_title: format!("{} seconds", delta_duration.as_secs()), secondary_title: format!("Error: {}", e),
message: Some(format!("Failed to parse tool output: {}", e)), status: "failed".to_string(), // Set status to failed
message_chunk: None, file_ids: vec![],
status: Some("error".to_string()), files: HashMap::new(),
})]); })]);
} }
}; };
@ -1840,15 +1840,14 @@ fn tool_data_catalog_search(id: String, content: String, delta_duration: Duratio
Ok(result) => result, Ok(result) => result,
Err(e) => { Err(e) => {
println!("Failed to parse SearchDataCatalogOutput: {}. Content: {}", e, content); println!("Failed to parse SearchDataCatalogOutput: {}. Content: {}", e, content);
// Return an error reasoning message // Return an error reasoning message - keep as Text for this tool? Or Pill? Let's use Pill for consistency
return Ok(vec![BusterReasoningMessage::Text(BusterReasoningText { return Ok(vec![BusterReasoningMessage::Pill(BusterReasoningPill {
id, id,
reasoning_type: "text".to_string(), thought_type: "pills".to_string(), // Changed from "text"
title: "Error Searching Data Catalog".to_string(), title: "Error Searching Data Catalog".to_string(),
secondary_title: format!("{} seconds", delta_duration.as_secs()), secondary_title: format!("Error: {}", e), // Changed message to secondary title
message: Some(format!("Failed to parse tool output: {}", e)), pill_containers: None, // No pills for error state
message_chunk: None, status: "failed".to_string(), // Set status to failed
status: Some("error".to_string()),
})]); })]);
} }
}; };