@@ -88,7 +88,7 @@ def validate_model(model: "Model") -> list[str]:
8888 # Derived, ratio, cumulative, time_comparison, and conversion metrics don't need agg
8989 for measure in model .metrics :
9090 # Skip validation for complex metric types that don't use agg
91- if measure .type in ["derived" , "ratio" , "cumulative" , "time_comparison" , "conversion" ]:
91+ if measure .type in ["derived" , "ratio" , "cumulative" , "time_comparison" , "conversion" , "retention" ]:
9292 continue
9393
9494 valid_aggs = _valid_measure_aggs ()
@@ -126,10 +126,11 @@ def validate_metric(measure: "Metric", graph: "SemanticGraph") -> list[str]:
126126 "cumulative" ,
127127 "time_comparison" ,
128128 "conversion" ,
129+ "retention" ,
129130 ]:
130131 errors .append (
131132 f"Metric '{ measure .name } ' has invalid type '{ measure .type } '. "
132- f"Must be one of: ratio, derived, cumulative, time_comparison, conversion"
133+ f"Must be one of: ratio, derived, cumulative, time_comparison, conversion, retention "
133134 )
134135 return errors # Can't continue validation with invalid type
135136
@@ -191,6 +192,12 @@ def validate_metric(measure: "Metric", graph: "SemanticGraph") -> list[str]:
191192 if not measure .sql and not measure .window_expression :
192193 errors .append (f"Cumulative measure '{ measure .name } ' must have 'sql' or 'window_expression' defined" )
193194
195+ elif measure .type == "retention" :
196+ if not measure .entity :
197+ errors .append (f"Retention measure '{ measure .name } ' must have 'entity' defined" )
198+ if not measure .cohort_event :
199+ errors .append (f"Retention measure '{ measure .name } ' must have 'cohort_event' defined" )
200+
194201 return errors
195202
196203
0 commit comments