hotfix: relationship tweak for LLM

This commit is contained in:
dal 2025-05-30 16:54:49 -06:00
parent f98e23f15f
commit 86c275e70d
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
4 changed files with 34 additions and 52 deletions

View File

@ -21,25 +21,25 @@ models:
description: Minimum number of logins description: Minimum number of logins
description: Customers with logins above threshold and active subscription description: Customers with logins above threshold and active subscription
metrics: metrics:
# Metric using entity columns, requires deduplication for many-to-many # Metric using relationship columns, requires deduplication for many-to-many
- name: popular_product_revenue - name: popular_product_revenue
expr: SUM(revenue) WHERE culture_products.product_count > 5 expr: SUM(revenue) WHERE culture_products.product_count > 5
description: Revenue from cultures with popular products description: Revenue from cultures with popular products
entities: relationships:
- name: logins - name: logins
primary_key: cultureid source_col: cultureid
foreign_key: cultureid ref_col: cultureid
type: LEFT # Explicitly set, but LLM could override type: LEFT # Explicitly set, but LLM could override
cardinality: one-to-many cardinality: one-to-many
description: Links to login activity description: Links to login activity
- name: subscriptions - name: subscriptions
primary_key: cultureid source_col: cultureid
foreign_key: cultureid ref_col: cultureid
cardinality: one-to-one cardinality: one-to-one
description: Links to subscription data (no type, LLM decides) description: Links to subscription data (no type, LLM decides)
- name: culture_products - name: culture_products
primary_key: cultureid source_col: cultureid
foreign_key: cultureid ref_col: cultureid
cardinality: many-to-many cardinality: many-to-many
description: Links to product associations (many-to-many via junction) description: Links to product associations (many-to-many via junction)
@ -52,10 +52,10 @@ models:
measures: measures:
- name: login_count - name: login_count
description: Number of logins description: Number of logins
entities: relationships:
- name: culture - name: culture
primary_key: cultureid source_col: cultureid
foreign_key: cultureid ref_col: cultureid
cardinality: many-to-one cardinality: many-to-one
# Model for subscriptions # Model for subscriptions
@ -67,10 +67,10 @@ models:
- name: subscription_status - name: subscription_status
description: Current subscription status description: Current subscription status
options: ["active", "inactive"] options: ["active", "inactive"]
entities: relationships:
- name: culture - name: culture
primary_key: cultureid source_col: cultureid
foreign_key: cultureid ref_col: cultureid
cardinality: one-to-one cardinality: one-to-one
# Junction model for many-to-many between culture and products # Junction model for many-to-many between culture and products
@ -84,12 +84,12 @@ models:
measures: measures:
- name: product_count - name: product_count
description: Number of products in this association description: Number of products in this association
entities: relationships:
- name: culture - name: culture
primary_key: cultureid source_col: cultureid
foreign_key: cultureid ref_col: cultureid
cardinality: many-to-many cardinality: many-to-many
- name: products - name: products
primary_key: productid source_col: productid
foreign_key: productid ref_col: productid
cardinality: many-to-many cardinality: many-to-many

View File

@ -13,7 +13,7 @@
type: string # Optional, inferred if omitted type: string # Optional, inferred if omitted
metrics: metrics:
- name: string # Required - name: string # Required
expr: string # Required, can use model.column from entities expr: string # Required, can use model.column from relationships
description: string # Optional description: string # Optional
args: # Optional, required only if expr contains arguments, default: null args: # Optional, required only if expr contains arguments, default: null
- name: string # Required - name: string # Required
@ -21,16 +21,16 @@
description: string # Optional description: string # Optional
filters: filters:
- name: string # Required - name: string # Required
expr: string # Required, can use model.column from entities expr: string # Required, can use model.column from relationships
description: string # Optional description: string # Optional
args: # Optional, required only if expr contains arguments, default: null args: # Optional, required only if expr contains arguments, default: null
- name: string # Required - name: string # Required
type: string # Required type: string # Required
description: string # Optional description: string # Optional
entities: relationships:
- name: string # Required - name: string # Required
primary_key: string # Required source_col: string # Required
foreign_key: string # Required ref_col: string # Required
type: string # Optional, e.g., "LEFT", "INNER"; LLM decides if omitted type: string # Optional, e.g., "LEFT", "INNER"; LLM decides if omitted
cardinality: string # Optional, e.g., "one-to-many", "many-to-many" cardinality: string # Optional, e.g., "one-to-many", "many-to-many"
description: string # Optional description: string # Optional

View File

@ -18,7 +18,7 @@ pub struct Model {
pub metrics: Vec<Metric>, pub metrics: Vec<Metric>,
#[serde(default)] #[serde(default)]
pub filters: Vec<Filter>, pub filters: Vec<Filter>,
#[serde(rename = "entities", default)] // Added default #[serde(default)] // Added default
pub relationships: Vec<Relationship>, pub relationships: Vec<Relationship>,
} }

View File

@ -439,23 +439,9 @@ fn to_deploy_request(model: &Model, sql_content: String) -> DeployDatasetsReques
}); });
} }
// Convert entity relationships // Note: Relationships are now preserved in the yml_file field rather than being converted to entity_relationships.
let entity_relationships: Option<Vec<DeployDatasetsEntityRelationshipsRequest>> = // This allows the full semantic model structure (including relationships with all their metadata like
if !model.relationships.is_empty() { // cardinality, descriptions, etc.) to be preserved and processed by the backend.
Some(
model
.relationships
.iter()
.map(|rel| DeployDatasetsEntityRelationshipsRequest {
name: rel.name.clone(),
expr: rel.source_col.clone(), // Assuming foreign_key is the expression for the relationship for now
type_: rel.type_.clone().unwrap_or_else(|| "LEFT".to_string()), // Default to LEFT if not specified
})
.collect(),
)
} else {
None
};
let data_source_name = model.data_source_name.clone() let data_source_name = model.data_source_name.clone()
.expect("data_source_name missing after validation, should be resolved by resolve_model_configurations"); .expect("data_source_name missing after validation, should be resolved by resolve_model_configurations");
@ -464,7 +450,7 @@ fn to_deploy_request(model: &Model, sql_content: String) -> DeployDatasetsReques
); );
// Serialize the input Model to YAML to be stored in the yml_file field of the request. // Serialize the input Model to YAML to be stored in the yml_file field of the request.
// This captures the full semantic definition as sent. // This captures the full semantic definition as sent, including relationships with all their metadata.
let yml_content_for_request = serde_yaml::to_string(&model).unwrap_or_else(|e| { let yml_content_for_request = serde_yaml::to_string(&model).unwrap_or_else(|e| {
eprintln!( eprintln!(
"Error serializing model {} to YAML for deploy request: {}. Using empty string.", "Error serializing model {} to YAML for deploy request: {}. Using empty string.",
@ -484,7 +470,7 @@ fn to_deploy_request(model: &Model, sql_content: String) -> DeployDatasetsReques
database: model.database.clone(), database: model.database.clone(),
description: model.description.clone().unwrap_or_default(), description: model.description.clone().unwrap_or_default(),
sql_definition: Some(sql_content), sql_definition: Some(sql_content),
entity_relationships, entity_relationships: None, // Relationships are now preserved in yml_file instead
columns, columns,
yml_file: Some(yml_content_for_request), // Store the YAML of the model being deployed yml_file: Some(yml_content_for_request), // Store the YAML of the model being deployed
} }
@ -1191,7 +1177,7 @@ mod tests {
name: test_model name: test_model
description: "Test model" description: "Test model"
dimension: dimensions:
- name: dim1 - name: dim1
description: "First dimension" description: "First dimension"
type: "string" type: "string"
@ -1362,19 +1348,15 @@ models:
}; };
let sql_content = "SELECT * FROM test_schema.test_model"; let sql_content = "SELECT * FROM test_schema.test_model";
let request = to_deploy_request(&model, sql_content.to_string()); // Call the restored function let request = to_deploy_request(&model, sql_content.to_string());
assert_eq!(request.name, "test_model"); assert_eq!(request.name, "test_model");
assert_eq!(request.columns.len(), 2); // 1 dim, 1 measure assert_eq!(request.columns.len(), 2); // 1 dim, 1 measure
assert_eq!(request.columns[0].name, "dim1"); assert_eq!(request.columns[0].name, "dim1");
assert_eq!(request.columns[0].searchable, true); assert_eq!(request.columns[0].searchable, true);
assert_eq!(request.columns[1].name, "measure1"); assert_eq!(request.columns[1].name, "measure1");
assert!(request.entity_relationships.is_some()); // The model has empty relationships, so entity_relationships should be None
assert_eq!(request.entity_relationships.as_ref().unwrap().len(), 1); assert!(request.entity_relationships.is_none());
assert_eq!(
request.entity_relationships.as_ref().unwrap()[0].name,
"related_model"
);
let expected_yml_content = serde_yaml::to_string(&model)?; let expected_yml_content = serde_yaml::to_string(&model)?;
assert_eq!(request.yml_file, Some(expected_yml_content)); assert_eq!(request.yml_file, Some(expected_yml_content));