validmind 2.5.8__py3-none-any.whl → 2.5.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. validmind/__version__.py +1 -1
  2. validmind/ai/test_descriptions.py +80 -119
  3. validmind/ai/test_result_description/config.yaml +29 -0
  4. validmind/ai/test_result_description/context.py +73 -0
  5. validmind/ai/test_result_description/image_processing.py +124 -0
  6. validmind/ai/test_result_description/system.jinja +39 -0
  7. validmind/ai/test_result_description/user.jinja +25 -0
  8. validmind/api_client.py +89 -43
  9. validmind/client.py +2 -2
  10. validmind/client_config.py +11 -14
  11. validmind/datasets/credit_risk/__init__.py +1 -0
  12. validmind/datasets/credit_risk/datasets/lending_club_biased.csv.gz +0 -0
  13. validmind/datasets/credit_risk/lending_club_bias.py +142 -0
  14. validmind/datasets/regression/fred_timeseries.py +67 -138
  15. validmind/template.py +1 -0
  16. validmind/test_suites/__init__.py +0 -2
  17. validmind/test_suites/statsmodels_timeseries.py +1 -1
  18. validmind/test_suites/summarization.py +0 -1
  19. validmind/test_suites/time_series.py +0 -43
  20. validmind/tests/__types__.py +14 -15
  21. validmind/tests/data_validation/ACFandPACFPlot.py +15 -13
  22. validmind/tests/data_validation/ADF.py +31 -24
  23. validmind/tests/data_validation/AutoAR.py +9 -9
  24. validmind/tests/data_validation/AutoMA.py +23 -16
  25. validmind/tests/data_validation/AutoSeasonality.py +18 -16
  26. validmind/tests/data_validation/AutoStationarity.py +21 -16
  27. validmind/tests/data_validation/BivariateScatterPlots.py +67 -96
  28. validmind/tests/{model_validation/statsmodels → data_validation}/BoxPierce.py +34 -34
  29. validmind/tests/data_validation/ChiSquaredFeaturesTable.py +85 -124
  30. validmind/tests/data_validation/ClassImbalance.py +15 -12
  31. validmind/tests/data_validation/DFGLSArch.py +19 -13
  32. validmind/tests/data_validation/DatasetDescription.py +17 -11
  33. validmind/tests/data_validation/DatasetSplit.py +7 -5
  34. validmind/tests/data_validation/DescriptiveStatistics.py +28 -21
  35. validmind/tests/data_validation/Duplicates.py +33 -25
  36. validmind/tests/data_validation/EngleGrangerCoint.py +35 -33
  37. validmind/tests/data_validation/FeatureTargetCorrelationPlot.py +59 -71
  38. validmind/tests/data_validation/HighCardinality.py +19 -12
  39. validmind/tests/data_validation/HighPearsonCorrelation.py +27 -22
  40. validmind/tests/data_validation/IQROutliersBarPlot.py +13 -10
  41. validmind/tests/data_validation/IQROutliersTable.py +40 -36
  42. validmind/tests/data_validation/IsolationForestOutliers.py +21 -14
  43. validmind/tests/data_validation/JarqueBera.py +70 -0
  44. validmind/tests/data_validation/KPSS.py +34 -29
  45. validmind/tests/data_validation/LJungBox.py +66 -0
  46. validmind/tests/data_validation/LaggedCorrelationHeatmap.py +22 -15
  47. validmind/tests/data_validation/MissingValues.py +32 -27
  48. validmind/tests/data_validation/MissingValuesBarPlot.py +25 -21
  49. validmind/tests/data_validation/PearsonCorrelationMatrix.py +71 -84
  50. validmind/tests/data_validation/PhillipsPerronArch.py +37 -30
  51. validmind/tests/data_validation/ProtectedClassesCombination.py +197 -0
  52. validmind/tests/data_validation/ProtectedClassesDescription.py +130 -0
  53. validmind/tests/data_validation/ProtectedClassesDisparity.py +133 -0
  54. validmind/tests/data_validation/ProtectedClassesThresholdOptimizer.py +172 -0
  55. validmind/tests/data_validation/RollingStatsPlot.py +31 -23
  56. validmind/tests/data_validation/RunsTest.py +72 -0
  57. validmind/tests/data_validation/ScatterPlot.py +63 -78
  58. validmind/tests/data_validation/SeasonalDecompose.py +38 -34
  59. validmind/tests/{model_validation/statsmodels → data_validation}/ShapiroWilk.py +35 -30
  60. validmind/tests/data_validation/Skewness.py +35 -37
  61. validmind/tests/data_validation/SpreadPlot.py +35 -35
  62. validmind/tests/data_validation/TabularCategoricalBarPlots.py +23 -17
  63. validmind/tests/data_validation/TabularDateTimeHistograms.py +21 -13
  64. validmind/tests/data_validation/TabularDescriptionTables.py +51 -16
  65. validmind/tests/data_validation/TabularNumericalHistograms.py +25 -22
  66. validmind/tests/data_validation/TargetRateBarPlots.py +21 -14
  67. validmind/tests/data_validation/TimeSeriesDescription.py +25 -18
  68. validmind/tests/data_validation/TimeSeriesDescriptiveStatistics.py +23 -17
  69. validmind/tests/data_validation/TimeSeriesFrequency.py +24 -17
  70. validmind/tests/data_validation/TimeSeriesHistogram.py +33 -32
  71. validmind/tests/data_validation/TimeSeriesLinePlot.py +17 -10
  72. validmind/tests/data_validation/TimeSeriesMissingValues.py +15 -10
  73. validmind/tests/data_validation/TimeSeriesOutliers.py +37 -33
  74. validmind/tests/data_validation/TooManyZeroValues.py +16 -11
  75. validmind/tests/data_validation/UniqueRows.py +11 -6
  76. validmind/tests/data_validation/WOEBinPlots.py +23 -16
  77. validmind/tests/data_validation/WOEBinTable.py +35 -30
  78. validmind/tests/data_validation/ZivotAndrewsArch.py +34 -28
  79. validmind/tests/data_validation/nlp/CommonWords.py +21 -14
  80. validmind/tests/data_validation/nlp/Hashtags.py +42 -40
  81. validmind/tests/data_validation/nlp/LanguageDetection.py +33 -14
  82. validmind/tests/data_validation/nlp/Mentions.py +21 -15
  83. validmind/tests/data_validation/nlp/PolarityAndSubjectivity.py +32 -9
  84. validmind/tests/data_validation/nlp/Punctuations.py +24 -20
  85. validmind/tests/data_validation/nlp/Sentiment.py +27 -8
  86. validmind/tests/data_validation/nlp/StopWords.py +26 -19
  87. validmind/tests/data_validation/nlp/TextDescription.py +39 -36
  88. validmind/tests/data_validation/nlp/Toxicity.py +32 -9
  89. validmind/tests/decorator.py +81 -42
  90. validmind/tests/model_validation/BertScore.py +36 -27
  91. validmind/tests/model_validation/BleuScore.py +25 -19
  92. validmind/tests/model_validation/ClusterSizeDistribution.py +38 -34
  93. validmind/tests/model_validation/ContextualRecall.py +38 -13
  94. validmind/tests/model_validation/FeaturesAUC.py +32 -13
  95. validmind/tests/model_validation/MeteorScore.py +46 -33
  96. validmind/tests/model_validation/ModelMetadata.py +32 -64
  97. validmind/tests/model_validation/ModelPredictionResiduals.py +75 -73
  98. validmind/tests/model_validation/RegardScore.py +30 -14
  99. validmind/tests/model_validation/RegressionResidualsPlot.py +10 -5
  100. validmind/tests/model_validation/RougeScore.py +36 -30
  101. validmind/tests/model_validation/TimeSeriesPredictionWithCI.py +30 -14
  102. validmind/tests/model_validation/TimeSeriesPredictionsPlot.py +27 -30
  103. validmind/tests/model_validation/TimeSeriesR2SquareBySegments.py +68 -63
  104. validmind/tests/model_validation/TokenDisparity.py +31 -23
  105. validmind/tests/model_validation/ToxicityScore.py +26 -17
  106. validmind/tests/model_validation/embeddings/ClusterDistribution.py +24 -20
  107. validmind/tests/model_validation/embeddings/CosineSimilarityComparison.py +30 -27
  108. validmind/tests/model_validation/embeddings/CosineSimilarityDistribution.py +7 -5
  109. validmind/tests/model_validation/embeddings/CosineSimilarityHeatmap.py +32 -23
  110. validmind/tests/model_validation/embeddings/DescriptiveAnalytics.py +7 -5
  111. validmind/tests/model_validation/embeddings/EmbeddingsVisualization2D.py +15 -11
  112. validmind/tests/model_validation/embeddings/EuclideanDistanceComparison.py +29 -29
  113. validmind/tests/model_validation/embeddings/EuclideanDistanceHeatmap.py +34 -25
  114. validmind/tests/model_validation/embeddings/PCAComponentsPairwisePlots.py +38 -26
  115. validmind/tests/model_validation/embeddings/StabilityAnalysis.py +40 -1
  116. validmind/tests/model_validation/embeddings/StabilityAnalysisKeyword.py +18 -17
  117. validmind/tests/model_validation/embeddings/StabilityAnalysisRandomNoise.py +40 -45
  118. validmind/tests/model_validation/embeddings/StabilityAnalysisSynonyms.py +17 -19
  119. validmind/tests/model_validation/embeddings/StabilityAnalysisTranslation.py +29 -25
  120. validmind/tests/model_validation/embeddings/TSNEComponentsPairwisePlots.py +38 -28
  121. validmind/tests/model_validation/ragas/AnswerCorrectness.py +5 -4
  122. validmind/tests/model_validation/ragas/AnswerRelevance.py +5 -4
  123. validmind/tests/model_validation/ragas/AnswerSimilarity.py +5 -4
  124. validmind/tests/model_validation/ragas/AspectCritique.py +12 -6
  125. validmind/tests/model_validation/ragas/ContextEntityRecall.py +9 -8
  126. validmind/tests/model_validation/ragas/ContextPrecision.py +5 -4
  127. validmind/tests/model_validation/ragas/ContextRecall.py +5 -4
  128. validmind/tests/model_validation/ragas/ContextUtilization.py +155 -0
  129. validmind/tests/model_validation/ragas/Faithfulness.py +5 -4
  130. validmind/tests/model_validation/ragas/NoiseSensitivity.py +152 -0
  131. validmind/tests/model_validation/ragas/utils.py +6 -0
  132. validmind/tests/model_validation/sklearn/AdjustedMutualInformation.py +19 -12
  133. validmind/tests/model_validation/sklearn/AdjustedRandIndex.py +22 -17
  134. validmind/tests/model_validation/sklearn/ClassifierPerformance.py +27 -25
  135. validmind/tests/model_validation/sklearn/ClusterCosineSimilarity.py +7 -5
  136. validmind/tests/model_validation/sklearn/ClusterPerformance.py +40 -78
  137. validmind/tests/model_validation/sklearn/ClusterPerformanceMetrics.py +15 -17
  138. validmind/tests/model_validation/sklearn/CompletenessScore.py +17 -11
  139. validmind/tests/model_validation/sklearn/ConfusionMatrix.py +22 -15
  140. validmind/tests/model_validation/sklearn/FeatureImportance.py +95 -0
  141. validmind/tests/model_validation/sklearn/FowlkesMallowsScore.py +7 -7
  142. validmind/tests/model_validation/sklearn/HomogeneityScore.py +19 -12
  143. validmind/tests/model_validation/sklearn/HyperParametersTuning.py +35 -30
  144. validmind/tests/model_validation/sklearn/KMeansClustersOptimization.py +10 -5
  145. validmind/tests/model_validation/sklearn/MinimumAccuracy.py +32 -32
  146. validmind/tests/model_validation/sklearn/MinimumF1Score.py +23 -23
  147. validmind/tests/model_validation/sklearn/MinimumROCAUCScore.py +15 -10
  148. validmind/tests/model_validation/sklearn/ModelsPerformanceComparison.py +26 -19
  149. validmind/tests/model_validation/sklearn/OverfitDiagnosis.py +38 -18
  150. validmind/tests/model_validation/sklearn/PermutationFeatureImportance.py +32 -26
  151. validmind/tests/model_validation/sklearn/PopulationStabilityIndex.py +8 -6
  152. validmind/tests/model_validation/sklearn/PrecisionRecallCurve.py +24 -17
  153. validmind/tests/model_validation/sklearn/ROCCurve.py +12 -7
  154. validmind/tests/model_validation/sklearn/RegressionErrors.py +74 -130
  155. validmind/tests/model_validation/sklearn/RegressionErrorsComparison.py +27 -12
  156. validmind/tests/model_validation/sklearn/{RegressionModelsPerformanceComparison.py → RegressionPerformance.py} +18 -20
  157. validmind/tests/model_validation/sklearn/RegressionR2Square.py +55 -94
  158. validmind/tests/model_validation/sklearn/RegressionR2SquareComparison.py +32 -13
  159. validmind/tests/model_validation/sklearn/RobustnessDiagnosis.py +36 -32
  160. validmind/tests/model_validation/sklearn/SHAPGlobalImportance.py +66 -5
  161. validmind/tests/model_validation/sklearn/SilhouettePlot.py +27 -19
  162. validmind/tests/model_validation/sklearn/TrainingTestDegradation.py +25 -18
  163. validmind/tests/model_validation/sklearn/VMeasure.py +14 -13
  164. validmind/tests/model_validation/sklearn/WeakspotsDiagnosis.py +7 -5
  165. validmind/tests/model_validation/statsmodels/AutoARIMA.py +24 -18
  166. validmind/tests/model_validation/statsmodels/CumulativePredictionProbabilities.py +73 -104
  167. validmind/tests/model_validation/statsmodels/DurbinWatsonTest.py +59 -32
  168. validmind/tests/model_validation/statsmodels/GINITable.py +44 -77
  169. validmind/tests/model_validation/statsmodels/KolmogorovSmirnov.py +33 -34
  170. validmind/tests/model_validation/statsmodels/Lilliefors.py +27 -24
  171. validmind/tests/model_validation/statsmodels/PredictionProbabilitiesHistogram.py +86 -119
  172. validmind/tests/model_validation/statsmodels/RegressionCoeffs.py +100 -0
  173. validmind/tests/model_validation/statsmodels/RegressionFeatureSignificance.py +14 -9
  174. validmind/tests/model_validation/statsmodels/RegressionModelForecastPlot.py +17 -13
  175. validmind/tests/model_validation/statsmodels/RegressionModelForecastPlotLevels.py +46 -43
  176. validmind/tests/model_validation/statsmodels/RegressionModelSensitivityPlot.py +38 -36
  177. validmind/tests/model_validation/statsmodels/RegressionModelSummary.py +30 -28
  178. validmind/tests/model_validation/statsmodels/RegressionPermutationFeatureImportance.py +18 -11
  179. validmind/tests/model_validation/statsmodels/ScorecardHistogram.py +75 -107
  180. validmind/tests/ongoing_monitoring/FeatureDrift.py +10 -6
  181. validmind/tests/ongoing_monitoring/PredictionAcrossEachFeature.py +31 -25
  182. validmind/tests/ongoing_monitoring/PredictionCorrelation.py +29 -21
  183. validmind/tests/ongoing_monitoring/TargetPredictionDistributionPlot.py +31 -23
  184. validmind/tests/prompt_validation/Bias.py +14 -11
  185. validmind/tests/prompt_validation/Clarity.py +16 -14
  186. validmind/tests/prompt_validation/Conciseness.py +7 -5
  187. validmind/tests/prompt_validation/Delimitation.py +23 -22
  188. validmind/tests/prompt_validation/NegativeInstruction.py +7 -5
  189. validmind/tests/prompt_validation/Robustness.py +12 -10
  190. validmind/tests/prompt_validation/Specificity.py +13 -11
  191. validmind/tests/prompt_validation/ai_powered_test.py +6 -0
  192. validmind/tests/run.py +68 -23
  193. validmind/unit_metrics/__init__.py +81 -144
  194. validmind/unit_metrics/classification/{sklearn/Accuracy.py → Accuracy.py} +1 -1
  195. validmind/unit_metrics/classification/{sklearn/F1.py → F1.py} +1 -1
  196. validmind/unit_metrics/classification/{sklearn/Precision.py → Precision.py} +1 -1
  197. validmind/unit_metrics/classification/{sklearn/ROC_AUC.py → ROC_AUC.py} +1 -2
  198. validmind/unit_metrics/classification/{sklearn/Recall.py → Recall.py} +1 -1
  199. validmind/unit_metrics/regression/{sklearn/AdjustedRSquaredScore.py → AdjustedRSquaredScore.py} +1 -1
  200. validmind/unit_metrics/regression/GiniCoefficient.py +1 -1
  201. validmind/unit_metrics/regression/HuberLoss.py +1 -1
  202. validmind/unit_metrics/regression/KolmogorovSmirnovStatistic.py +1 -1
  203. validmind/unit_metrics/regression/{sklearn/MeanAbsoluteError.py → MeanAbsoluteError.py} +1 -1
  204. validmind/unit_metrics/regression/MeanAbsolutePercentageError.py +1 -1
  205. validmind/unit_metrics/regression/MeanBiasDeviation.py +1 -1
  206. validmind/unit_metrics/regression/{sklearn/MeanSquaredError.py → MeanSquaredError.py} +1 -1
  207. validmind/unit_metrics/regression/QuantileLoss.py +1 -1
  208. validmind/unit_metrics/regression/{sklearn/RSquaredScore.py → RSquaredScore.py} +1 -1
  209. validmind/unit_metrics/regression/{sklearn/RootMeanSquaredError.py → RootMeanSquaredError.py} +1 -1
  210. validmind/utils.py +4 -0
  211. validmind/vm_models/dataset/dataset.py +2 -0
  212. validmind/vm_models/figure.py +5 -0
  213. validmind/vm_models/test/metric.py +1 -0
  214. validmind/vm_models/test/result_wrapper.py +143 -158
  215. validmind/vm_models/test/threshold_test.py +1 -0
  216. {validmind-2.5.8.dist-info → validmind-2.5.18.dist-info}/METADATA +4 -3
  217. validmind-2.5.18.dist-info/RECORD +324 -0
  218. validmind/tests/data_validation/ANOVAOneWayTable.py +0 -138
  219. validmind/tests/data_validation/BivariateFeaturesBarPlots.py +0 -142
  220. validmind/tests/data_validation/BivariateHistograms.py +0 -117
  221. validmind/tests/data_validation/HeatmapFeatureCorrelations.py +0 -124
  222. validmind/tests/data_validation/MissingValuesRisk.py +0 -88
  223. validmind/tests/model_validation/ModelMetadataComparison.py +0 -59
  224. validmind/tests/model_validation/sklearn/FeatureImportanceComparison.py +0 -83
  225. validmind/tests/model_validation/statsmodels/JarqueBera.py +0 -73
  226. validmind/tests/model_validation/statsmodels/LJungBox.py +0 -66
  227. validmind/tests/model_validation/statsmodels/RegressionCoeffsPlot.py +0 -135
  228. validmind/tests/model_validation/statsmodels/RegressionModelsCoeffs.py +0 -103
  229. validmind/tests/model_validation/statsmodels/RunsTest.py +0 -71
  230. validmind-2.5.8.dist-info/RECORD +0 -318
  231. {validmind-2.5.8.dist-info → validmind-2.5.18.dist-info}/LICENSE +0 -0
  232. {validmind-2.5.8.dist-info → validmind-2.5.18.dist-info}/WHEEL +0 -0
  233. {validmind-2.5.8.dist-info → validmind-2.5.18.dist-info}/entry_points.txt +0 -0
validmind/api_client.py CHANGED
@@ -186,12 +186,24 @@ def __ping() -> Dict[str, Any]:
186
186
  client_config.project = client_info["project"]
187
187
  client_config.documentation_template = client_info.get("documentation_template", {})
188
188
  client_config.feature_flags = client_info.get("feature_flags", {})
189
+ client_config.model = client_info.get("model", {})
190
+ client_config.document_type = client_info.get(
191
+ "document_type", "model_documentation"
192
+ )
189
193
 
190
194
  if ack_connected:
191
- logger.info(
192
- f"Connected to ValidMind... Current Model: {client_config.project['name']}"
193
- f" ({client_config.project['cuid']})"
194
- )
195
+ if client_config.model:
196
+ logger.info(
197
+ f"🎉 Connected to ValidMind!\n"
198
+ f"📊 Model: {client_config.model.get('name', 'N/A')} "
199
+ f"(ID: {client_config.model.get('cuid', 'N/A')})\n"
200
+ f"📁 Document Type: {client_config.document_type}"
201
+ )
202
+ else:
203
+ logger.info(
204
+ f"Connected to ValidMind... Current Model: {client_config.project['name']}"
205
+ f" ({client_config.project['cuid']})"
206
+ )
195
207
 
196
208
 
197
209
  def reload():
@@ -331,32 +343,6 @@ async def log_figures(figures: List[Figure]) -> Dict[str, Any]:
331
343
  Returns:
332
344
  dict: The response from the API
333
345
  """
334
- # this actually slows things down - better to log them in parallel
335
- # if client_config.can_log_figures(): # check if the backend supports batch logging
336
- # try:
337
- # data = {}
338
- # files = {}
339
- # for figure in figures:
340
- # data.update(
341
- # {f"{k}-{figure.key}": v for k, v in figure.serialize().items()}
342
- # )
343
- # files.update(
344
- # {
345
- # f"{k}-{figure.key}": v
346
- # for k, v in figure.serialize_files().items()
347
- # }
348
- # )
349
-
350
- # return await _post(
351
- # "log_figures",
352
- # data=data,
353
- # files=files,
354
- # )
355
- # except Exception as e:
356
- # logger.error("Error logging figures to ValidMind API")
357
- # raise e
358
-
359
- # else:
360
346
  return await asyncio.gather(*[log_figure(figure) for figure in figures])
361
347
 
362
348
 
@@ -416,11 +402,11 @@ async def log_metrics(
416
402
  Returns:
417
403
  dict: The response from the API
418
404
  """
419
- params = {}
405
+ request_params = {}
420
406
  if section_id:
421
- params["section_id"] = section_id
407
+ request_params["section_id"] = section_id
422
408
  if position is not None:
423
- params["position"] = position
409
+ request_params["position"] = position
424
410
 
425
411
  data = []
426
412
 
@@ -430,7 +416,7 @@ async def log_metrics(
430
416
  "inputs": inputs,
431
417
  }
432
418
 
433
- if output_template and client_config.can_log_output_template():
419
+ if output_template:
434
420
  metric_data["output_template"] = output_template
435
421
 
436
422
  data.append(metric_data)
@@ -438,7 +424,7 @@ async def log_metrics(
438
424
  try:
439
425
  return await _post(
440
426
  "log_metrics",
441
- params=params,
427
+ params=request_params,
442
428
  data=json.dumps(data, cls=NumpyEncoder, allow_nan=False),
443
429
  )
444
430
  except Exception as e:
@@ -469,16 +455,16 @@ async def log_test_result(
469
455
  Returns:
470
456
  dict: The response from the API
471
457
  """
472
- params = {}
458
+ request_params = {}
473
459
  if section_id:
474
- params["section_id"] = section_id
460
+ request_params["section_id"] = section_id
475
461
  if position is not None:
476
- params["position"] = position
462
+ request_params["position"] = position
477
463
 
478
464
  try:
479
465
  return await _post(
480
466
  "log_test_results",
481
- params=params,
467
+ params=request_params,
482
468
  data=json.dumps(
483
469
  {
484
470
  **result.serialize(),
@@ -503,7 +489,7 @@ def log_test_results(
503
489
 
504
490
  Args:
505
491
  results (list): A list of ThresholdTestResults objects
506
- inputs (list): A list of input keys (names) that were used to run the test
492
+ inputs (list): A list of input IDs that were used to run the test
507
493
 
508
494
  Raises:
509
495
  Exception: If the API call fails
@@ -522,11 +508,11 @@ def log_test_results(
522
508
  return responses
523
509
 
524
510
 
525
- def log_input(name: str, type: str, metadata: Dict[str, Any]) -> Dict[str, Any]:
511
+ def log_input(input_id: str, type: str, metadata: Dict[str, Any]) -> Dict[str, Any]:
526
512
  """Logs input information - internal use for now (don't expose via public API)
527
513
 
528
514
  Args:
529
- name (str): The name of the input
515
+ input_id (str): The input_id of the input
530
516
  type (str): The type of the input
531
517
  metadata (dict): The metadata of the input
532
518
 
@@ -542,7 +528,7 @@ def log_input(name: str, type: str, metadata: Dict[str, Any]) -> Dict[str, Any]:
542
528
  "log_input",
543
529
  data=json.dumps(
544
530
  {
545
- "name": name,
531
+ "name": input_id,
546
532
  "type": type,
547
533
  "metadata": metadata,
548
534
  },
@@ -555,6 +541,66 @@ def log_input(name: str, type: str, metadata: Dict[str, Any]) -> Dict[str, Any]:
555
541
  raise e
556
542
 
557
543
 
544
+ async def alog_metric(
545
+ key: str,
546
+ value: float,
547
+ inputs: Optional[List[str]] = None,
548
+ params: Optional[Dict[str, Any]] = None,
549
+ recorded_at: Optional[str] = None,
550
+ ) -> None:
551
+ """See log_metric for details"""
552
+ if not key or not isinstance(key, str):
553
+ raise ValueError("`key` must be a non-empty string")
554
+
555
+ if not value or not isinstance(value, (int, float)):
556
+ raise ValueError("`value` must be a scalar (int or float)")
557
+
558
+ try:
559
+ return await _post(
560
+ "log_unit_metric",
561
+ data=json.dumps(
562
+ {
563
+ "key": key,
564
+ "value": value,
565
+ "inputs": inputs or [],
566
+ "params": params or {},
567
+ "recorded_at": recorded_at,
568
+ },
569
+ cls=NumpyEncoder,
570
+ allow_nan=False,
571
+ ),
572
+ )
573
+ except Exception as e:
574
+ logger.error("Error logging metric to ValidMind API")
575
+ raise e
576
+
577
+
578
+ def log_metric(
579
+ key: str,
580
+ value: float,
581
+ inputs: Optional[List[str]] = None,
582
+ params: Optional[Dict[str, Any]] = None,
583
+ recorded_at: Optional[str] = None,
584
+ ) -> None:
585
+ """Logs a unit metric
586
+
587
+ Unit metrics are key-value pairs where the key is the metric name and the value is
588
+ a scalar (int or float). These key-value pairs are associated with the currently
589
+ selected model (inventory model in the ValidMind Platform) and keys can be logged
590
+ to over time to create a history of the metric. On the platform, these metrics
591
+ will be used to create plots/visualizations for documentation and dashboards etc.
592
+
593
+ Args:
594
+ key (str): The metric key
595
+ value (float): The metric value
596
+ inputs (list, optional): A list of input IDs that were used to compute the metric.
597
+ params (dict, optional): Dictionary of parameters used to compute the metric.
598
+ recorded_at (str, optional): The timestamp of the metric. Server will use
599
+ current time if not provided.
600
+ """
601
+ run_async(alog_metric, key, value, inputs, params, recorded_at)
602
+
603
+
558
604
  def start_run() -> str:
559
605
  """Starts a new test run
560
606
 
validmind/client.py CHANGED
@@ -164,7 +164,7 @@ def init_dataset(
164
164
 
165
165
  if __log:
166
166
  log_input(
167
- name=input_id,
167
+ input_id=input_id,
168
168
  type="dataset",
169
169
  metadata=get_dataset_info(vm_dataset),
170
170
  )
@@ -265,7 +265,7 @@ def init_model(
265
265
 
266
266
  if __log:
267
267
  log_input(
268
- name=input_id,
268
+ input_id=input_id,
269
269
  type="model",
270
270
  metadata=metadata,
271
271
  )
@@ -18,7 +18,9 @@ class ClientConfig:
18
18
  """
19
19
 
20
20
  project: object
21
+ model: object
21
22
  feature_flags: dict
23
+ document_type: str
22
24
  documentation_template: object
23
25
  running_on_colab: bool = False
24
26
 
@@ -34,21 +36,16 @@ class ClientConfig:
34
36
  except ImportError:
35
37
  self.running_on_colab = False
36
38
 
37
- def is_json_plots_enabled(self):
38
- """
39
- Returns True if the JSON plots feature flag is enabled on the backend
40
- """
41
- return self.feature_flags.get("generate_json_plots", False)
42
-
43
- def can_log_figures(self):
44
- """Returns True if the client can log figures to the API"""
45
- return self.feature_flags.get("log_figures", False)
46
-
47
- def can_log_output_template(self):
48
- """Returns True if the client can log output templates to the API"""
49
- return self.feature_flags.get("output_templates", False)
39
+ def can_generate_llm_test_descriptions(self):
40
+ """Returns True if the client can generate LLM based test descriptions"""
41
+ return self.feature_flags.get("llm_test_descriptions", True)
50
42
 
51
43
 
52
44
  client_config = ClientConfig(
53
- project=None, feature_flags={}, documentation_template=None
45
+ project=None,
46
+ model=None,
47
+ feature_flags={},
48
+ document_type="model_documentation",
49
+ documentation_template=None,
50
+ running_on_colab=False,
54
51
  )
@@ -8,4 +8,5 @@ Entrypoint for credit risk datasets.
8
8
 
9
9
  __all__ = [
10
10
  "lending_club",
11
+ "lending_club_bias",
11
12
  ]
@@ -0,0 +1,142 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ import os
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+ from sklearn.model_selection import train_test_split
10
+ from sklearn.preprocessing import LabelEncoder
11
+
12
+ current_path = os.path.dirname(os.path.abspath(__file__))
13
+ dataset_path = os.path.join(current_path, "datasets")
14
+
15
+ # URLs or file paths for online and offline data
16
+ data_file = os.path.join(dataset_path, "lending_club_biased.csv.gz")
17
+
18
+ target_column = "loan_status"
19
+ protected_classes = ["Gender", "Race", "Marital_Status"]
20
+
21
+ drop_columns = ["total_pymnt", "id", "verification_status", "purpose"]
22
+
23
+ score_params = {
24
+ "target_score": 600,
25
+ "target_odds": 50,
26
+ "pdo": 20,
27
+ }
28
+
29
+
30
+ def load_data():
31
+ """
32
+ Load data from the specified CSV file.
33
+
34
+ :return: DataFrame containing the loaded data.
35
+ """
36
+
37
+ print(f"Loading data from: {data_file}")
38
+ # Since we know the offline_data_file path ends with '.zip', we replace it with '.csv.gz'
39
+ gzip_file_path = data_file.replace(".zip", ".csv.gz")
40
+ # Read the CSV file directly from the .gz archive
41
+ df = pd.read_csv(gzip_file_path, compression="gzip")
42
+ print("Data loaded successfully.")
43
+ df = _clean_data(df)
44
+
45
+ return df
46
+
47
+
48
+ def _clean_data(df):
49
+ df = df.copy()
50
+ print("Loading the raw dataset:")
51
+ print(
52
+ f"Rows: {df.shape[0]}\nColumns: {df.shape[1]}\nMissing values: {df.isnull().sum().sum()}\n"
53
+ )
54
+
55
+ # Drop columns not relevant for this model
56
+ print(f"Dropping columns not relevant for this model: {drop_columns}")
57
+ df = df.drop(columns=drop_columns)
58
+ print(
59
+ f"Rows: {df.shape[0]}\nColumns: {df.shape[1]}\nMissing values: {df.isnull().sum().sum()}\n"
60
+ )
61
+
62
+ # Drop rows with missing target values
63
+ df.dropna(subset=[target_column], inplace=True)
64
+ print("Dropping rows with missing target values:")
65
+ print(
66
+ f"Rows: {df.shape[0]}\nColumns: {df.shape[1]}\nMissing values: {df.isnull().sum().sum()}\n"
67
+ )
68
+
69
+ # Drop columns with more than N percent missing values
70
+ missing_values = df.isnull().mean()
71
+ df = df.loc[:, missing_values < 0.7]
72
+ print("Dropping columns with more than 70% missing values:")
73
+ print(
74
+ f"Rows: {df.shape[0]}\nColumns: {df.shape[1]}\nMissing values: {df.isnull().sum().sum()}\n"
75
+ )
76
+
77
+ # Drop columns with only one unique value
78
+ unique_values = df.nunique()
79
+ df = df.loc[:, unique_values > 1]
80
+ print("Dropping columns with only one unique value:")
81
+ print(
82
+ f"Rows: {df.shape[0]}\nColumns: {df.shape[1]}\nMissing values: {df.isnull().sum().sum()}\n"
83
+ )
84
+
85
+ return df
86
+
87
+
88
+ def preprocess(df):
89
+ df = df.copy()
90
+
91
+ # Convert the target variable to integer type for modeling.
92
+ df[target_column] = df[target_column].astype(int)
93
+
94
+ # Identify and encode categorical variables for modeling purposes
95
+ label_encoders = {}
96
+ categorical_columns = df.select_dtypes(include=["object"]).columns
97
+
98
+ for column in categorical_columns:
99
+ le = LabelEncoder()
100
+ df[f"{column}_encoded"] = le.fit_transform(df[column])
101
+ label_encoders[column] = le
102
+ df = df.drop(columns=[column]) # Remove the original column
103
+
104
+ print(f"Encoding categorical variables: {list(categorical_columns)}")
105
+ print(
106
+ f"Rows: {df.shape[0]}\nColumns: {df.shape[1]}\nMissing values: {df.isnull().sum().sum()}\n"
107
+ )
108
+
109
+ return df
110
+
111
+
112
+ def split(df, test_size=0.3):
113
+ df = df.copy()
114
+
115
+ # Splitting the dataset into training and test sets
116
+ train_df, test_df = train_test_split(df, test_size=test_size, random_state=42)
117
+
118
+ # Calculate and print details for the training dataset
119
+ print(
120
+ f"Training Dataset:\nRows: {train_df.shape[0]}\nColumns: {train_df.shape[1]}\nMissing values: {train_df.isnull().sum().sum()}\n"
121
+ )
122
+
123
+ # Calculate and print details for the test dataset
124
+ print(
125
+ f"Test Dataset:\nRows: {test_df.shape[0]}\nColumns: {test_df.shape[1]}\nMissing values: {test_df.isnull().sum().sum()}\n"
126
+ )
127
+
128
+ return train_df, test_df
129
+
130
+
131
+ def compute_scores(probabilities):
132
+
133
+ target_score = score_params["target_score"]
134
+ target_odds = score_params["target_odds"]
135
+ pdo = score_params["pdo"]
136
+
137
+ factor = pdo / np.log(2)
138
+ offset = target_score - (factor * np.log(target_odds))
139
+
140
+ scores = offset + factor * np.log(probabilities / (1 - probabilities))
141
+
142
+ return scores