validmind 2.13.4__tar.gz → 2.13.5__tar.gz
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.
- {validmind-2.13.4 → validmind-2.13.5}/PKG-INFO +3 -1
- {validmind-2.13.4 → validmind-2.13.5}/pyproject.toml +4 -2
- validmind-2.13.5/validmind/__version__.py +1 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/ai/utils.py +92 -21
- {validmind-2.13.4 → validmind-2.13.5}/validmind/api_client.py +183 -13
- validmind-2.13.5/validmind/credentials_store.py +179 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/llm/agent_dataset.py +8 -6
- {validmind-2.13.4 → validmind-2.13.5}/validmind/errors.py +4 -0
- validmind-2.13.5/validmind/oidc_device.py +421 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/AnswerRelevancy.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/ArgumentCorrectness.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/Bias.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/ContextualPrecision.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/ContextualRecall.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/ContextualRelevancy.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/Faithfulness.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/Hallucination.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/Summarization.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/ToolCorrectness.py +2 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/__init__.py +1 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/MissingValues.py +3 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/AnswerCorrectness.py +1 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/AspectCritic.py +1 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/ContextEntityRecall.py +1 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/ContextPrecision.py +1 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/ContextPrecisionWithoutReference.py +1 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/ContextRecall.py +1 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/Faithfulness.py +1 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/SemanticSimilarity.py +1 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/FeatureImportance.py +3 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/HyperParametersTuning.py +1 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/RobustnessDiagnosis.py +3 -3
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/prompt_validation/ai_powered_test.py +2 -1
- {validmind-2.13.4 → validmind-2.13.5}/validmind/utils.py +16 -15
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/dataset/dataset.py +1 -1
- validmind-2.13.4/validmind/__version__.py +0 -1
- {validmind-2.13.4 → validmind-2.13.5}/.gitignore +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/LICENSE +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/README.pypi.md +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/ai/test_descriptions.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/client.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/client_config.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/classification/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/classification/config.json +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/classification/customer_churn.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/classification/datasets/bank_customer_churn.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/classification/datasets/taiwan_credit.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/classification/taiwan_credit.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/cluster/digits.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/credit_risk/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/credit_risk/datasets/lending_club_biased.csv.gz +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/credit_risk/datasets/lending_club_loan_data_2007_2014_clean.csv.gz +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/credit_risk/lending_club.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/credit_risk/lending_club_bias.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/llm/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/llm/rag/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_1.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_2.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_3.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_4.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_5.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/llm/rag/rfp.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/nlp/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/nlp/cnn_dailymail.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/nlp/datasets/Covid_19.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/nlp/datasets/cnn_dailymail_100_with_predictions.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/nlp/datasets/cnn_dailymail_500_with_predictions.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/nlp/datasets/sentiments_with_predictions.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/nlp/twitter_covid_19.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/california_housing.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/california_housing.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/CPIAUCSL.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/CSUSHPISA.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/DRSFRMACBS.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/FEDFUNDS.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/GDP.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/GDPC1.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/GS10.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/GS3.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/GS5.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/MORTGAGE30US.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred/UNRATE.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred_loan_rates.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred_loan_rates_test_1.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred_loan_rates_test_2.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred_loan_rates_test_3.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred_loan_rates_test_4.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/fred_loan_rates_test_5.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/datasets/leanding_club_loan_rates.csv +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/fred.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/fred_timeseries.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/generate_california_housing_csv.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/datasets/regression/lending_club.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/experimental/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/experimental/agents.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/html_templates/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/html_templates/content_blocks.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/input_registry.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/logging.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/models/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/models/foundation.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/models/function.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/models/huggingface.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/models/metadata.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/models/pipeline.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/models/pytorch.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/models/r_model.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/models/sklearn.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/AbsoluteError.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/BrierScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/CalibrationError.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/ClassBalance.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/Confidence.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/Correctness.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/LogLoss.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/OutlierScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/ProbabilityError.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/Uncertainty.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/classification/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/GEval.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/PlanAdherence.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/PlanQuality.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/scorers/llm/deepeval/TaskCompletion.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/template.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/classifier.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/cluster.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/embeddings.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/llm.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/nlp.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/parameters_optimization.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/regression.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/statsmodels_timeseries.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/summarization.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/tabular_datasets.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/text_data.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/test_suites/time_series.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/__types__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/_store.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/comparison.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ACFandPACFPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ADF.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/AutoAR.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/AutoMA.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/AutoStationarity.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/BivariateScatterPlots.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/BoxPierce.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ChiSquaredFeaturesTable.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ClassImbalance.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/DatasetDescription.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/DatasetSplit.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/DescriptiveStatistics.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/DickeyFullerGLS.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/Duplicates.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/EngleGrangerCoint.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/FeatureTargetCorrelationPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/HighCardinality.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/HighPearsonCorrelation.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/IQROutliersBarPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/IQROutliersTable.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/IsolationForestOutliers.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/JarqueBera.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/KPSS.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/LJungBox.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/LaggedCorrelationHeatmap.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/MissingValuesBarPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/MutualInformation.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/PearsonCorrelationMatrix.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/PhillipsPerronArch.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ProtectedClassesCombination.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ProtectedClassesDescription.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ProtectedClassesDisparity.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ProtectedClassesThresholdOptimizer.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/RollingStatsPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/RunsTest.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ScatterPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ScoreBandDefaultRates.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/SeasonalDecompose.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ShapiroWilk.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/Skewness.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/SpreadPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TabularCategoricalBarPlots.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TabularDateTimeHistograms.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TabularDescriptionTables.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TabularNumericalHistograms.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TargetRateBarPlots.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TimeSeriesDescription.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TimeSeriesDescriptiveStatistics.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TimeSeriesFrequency.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TimeSeriesHistogram.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TimeSeriesLinePlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TimeSeriesMissingValues.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TimeSeriesOutliers.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/TooManyZeroValues.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/UniqueRows.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/WOEBinPlots.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/WOEBinTable.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/ZivotAndrewsArch.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/CommonWords.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/Hashtags.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/LanguageDetection.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/Mentions.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/PolarityAndSubjectivity.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/Punctuations.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/Sentiment.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/StopWords.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/TextDescription.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/Toxicity.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/data_validation/nlp/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/decorator.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/load.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/BertScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/BleuScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ClusterSizeDistribution.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ContextualRecall.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/FeaturesAUC.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/MeteorScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ModelMetadata.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ModelPredictionResiduals.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/RegardScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/RegressionResidualsPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/RougeScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/TimeSeriesPredictionWithCI.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/TimeSeriesPredictionsPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/TimeSeriesR2SquareBySegments.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/TokenDisparity.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ToxicityScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/ClusterDistribution.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/CosineSimilarityComparison.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/CosineSimilarityDistribution.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/CosineSimilarityHeatmap.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/DescriptiveAnalytics.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/EmbeddingsVisualization2D.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/EuclideanDistanceComparison.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/EuclideanDistanceHeatmap.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/PCAComponentsPairwisePlots.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/StabilityAnalysisKeyword.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/StabilityAnalysisRandomNoise.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/StabilityAnalysisSynonyms.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/StabilityAnalysisTranslation.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/TSNEComponentsPairwisePlots.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/embeddings/utils.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/NoiseSensitivity.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/ResponseRelevancy.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/ragas/utils.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/AdjustedMutualInformation.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/AdjustedRandIndex.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/CalibrationCurve.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/ClassifierPerformance.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/ClassifierThresholdOptimization.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/ClusterCosineSimilarity.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/ClusterPerformanceMetrics.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/CompletenessScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/ConfusionMatrix.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/FowlkesMallowsScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/HomogeneityScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/KMeansClustersOptimization.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/MinimumAccuracy.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/MinimumF1Score.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/MinimumROCAUCScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/ModelParameters.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/ModelsPerformanceComparison.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/OverfitDiagnosis.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/PermutationFeatureImportance.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/PopulationStabilityIndex.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/PrecisionRecallCurve.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/ROCCurve.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/RegressionErrors.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/RegressionErrorsComparison.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/RegressionPerformance.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/RegressionR2Square.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/RegressionR2SquareComparison.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/SHAPGlobalImportance.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/ScoreProbabilityAlignment.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/SilhouettePlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/TrainingTestDegradation.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/VMeasure.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/WeakspotsDiagnosis.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/sklearn/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/AutoARIMA.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/CumulativePredictionProbabilities.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/DurbinWatsonTest.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/GINITable.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/KolmogorovSmirnov.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/Lilliefors.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/PredictionProbabilitiesHistogram.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/RegressionCoeffs.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/RegressionFeatureSignificance.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/RegressionModelForecastPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/RegressionModelForecastPlotLevels.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/RegressionModelSensitivityPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/RegressionModelSummary.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/RegressionPermutationFeatureImportance.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/ScorecardHistogram.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/model_validation/statsmodels/statsutils.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/CalibrationCurveDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/ClassDiscriminationDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/ClassImbalanceDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/ClassificationAccuracyDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/ConfusionMatrixDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/CumulativePredictionProbabilitiesDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/FeatureDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/PredictionAcrossEachFeature.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/PredictionCorrelation.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/PredictionProbabilitiesHistogramDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/PredictionQuantilesAcrossFeatures.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/ROCCurveDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/ScoreBandsDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/ScorecardHistogramDrift.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/ongoing_monitoring/TargetPredictionDistributionPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/output.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/plots/BoxPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/plots/CorrelationHeatmap.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/plots/HistogramPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/plots/ViolinPlot.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/plots/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/prompt_validation/Bias.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/prompt_validation/Clarity.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/prompt_validation/Conciseness.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/prompt_validation/Delimitation.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/prompt_validation/NegativeInstruction.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/prompt_validation/Robustness.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/prompt_validation/Specificity.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/prompt_validation/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/run.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/stats/CorrelationAnalysis.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/stats/DescriptiveStats.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/stats/NormalityTests.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/stats/OutlierDetection.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/stats/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/test_providers.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/tests/utils.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/classification/Accuracy.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/classification/F1.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/classification/Precision.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/classification/ROC_AUC.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/classification/Recall.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/AdjustedRSquaredScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/GiniCoefficient.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/HuberLoss.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/KolmogorovSmirnovStatistic.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/MeanAbsoluteError.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/MeanAbsolutePercentageError.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/MeanBiasDeviation.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/MeanSquaredError.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/QuantileLoss.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/RSquaredScore.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/unit_metrics/regression/RootMeanSquaredError.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/dataset/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/dataset/utils.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/figure.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/html_progress.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/html_renderer.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/input.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/model.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/result/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/result/pii_filter.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/result/result.jinja +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/result/result.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/result/utils.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/test_suite/__init__.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/test_suite/runner.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/test_suite/summary.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/test_suite/test.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/test_suite/test_suite.py +0 -0
- {validmind-2.13.4 → validmind-2.13.5}/validmind/vm_models/text_generation_summary.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: validmind
|
|
3
|
-
Version: 2.13.
|
|
3
|
+
Version: 2.13.5
|
|
4
4
|
Summary: ValidMind Library
|
|
5
5
|
Author-email: Andres Rodriguez <andres@validmind.ai>, Juan Martinez <juan@validmind.ai>, Anil Sorathiya <anil@validmind.ai>, Luis Pallares <luis@validmind.ai>, John Walz <john@validmind.ai>
|
|
6
6
|
License: DUAL LICENSE NOTICE
|
|
@@ -707,8 +707,10 @@ Requires-Dist: pandas<3.0.0,>=2.0.3
|
|
|
707
707
|
Requires-Dist: plotly>=6.0.0
|
|
708
708
|
Requires-Dist: polars
|
|
709
709
|
Requires-Dist: python-dotenv
|
|
710
|
+
Requires-Dist: requests<3.0.0,>=2.28.0
|
|
710
711
|
Requires-Dist: scikit-learn
|
|
711
712
|
Requires-Dist: seaborn
|
|
713
|
+
Requires-Dist: segno<2.0.0,>=1.6.6
|
|
712
714
|
Requires-Dist: tabulate<0.10.0,>=0.9.0
|
|
713
715
|
Requires-Dist: tiktoken
|
|
714
716
|
Requires-Dist: tqdm
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "validmind"
|
|
3
|
-
version = "2.13.
|
|
3
|
+
version = "2.13.5"
|
|
4
4
|
description = "ValidMind Library"
|
|
5
5
|
readme = "README.pypi.md"
|
|
6
6
|
requires-python = ">=3.9,<3.15"
|
|
@@ -14,6 +14,7 @@ authors = [
|
|
|
14
14
|
]
|
|
15
15
|
dependencies = [
|
|
16
16
|
"aiohttp[speedups] (<3.13.1)",
|
|
17
|
+
"requests (>=2.28.0,<3.0.0)",
|
|
17
18
|
"ipywidgets",
|
|
18
19
|
"kaleido (>=1.2.0,<2.0.0)",
|
|
19
20
|
"matplotlib",
|
|
@@ -33,6 +34,7 @@ dependencies = [
|
|
|
33
34
|
"tqdm",
|
|
34
35
|
"anywidget",
|
|
35
36
|
"beautifulsoup4",
|
|
37
|
+
"segno (>=1.6.6,<2.0.0)",
|
|
36
38
|
]
|
|
37
39
|
|
|
38
40
|
[project.optional-dependencies]
|
|
@@ -122,7 +124,7 @@ pii-detection = [
|
|
|
122
124
|
|
|
123
125
|
[dependency-groups]
|
|
124
126
|
dev = [
|
|
125
|
-
"black>=22.1.0,<
|
|
127
|
+
"black>=22.1.0,<26",
|
|
126
128
|
"click",
|
|
127
129
|
"cython>=0.29.34,<0.30",
|
|
128
130
|
"docstring-parser",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.13.5"
|
|
@@ -20,7 +20,7 @@ __judge_embeddings = None
|
|
|
20
20
|
OPENAI_MODEL = "gpt-4.1"
|
|
21
21
|
OPENAI_EMBEDDINGS_MODEL = "text-embedding-3-small"
|
|
22
22
|
GEMINI_MODEL = "gemini-2.5-pro"
|
|
23
|
-
GEMINI_EMBEDDINGS_MODEL = "
|
|
23
|
+
GEMINI_EMBEDDINGS_MODEL = "gemini-embedding-001"
|
|
24
24
|
|
|
25
25
|
# can be None, True or False (ternary to represent initial state, ack and failed ack)
|
|
26
26
|
__ack = None
|
|
@@ -59,10 +59,7 @@ def _get_configured_provider():
|
|
|
59
59
|
if os.getenv("AZURE_OPENAI_KEY"):
|
|
60
60
|
return "azure"
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
return "gemini"
|
|
64
|
-
|
|
65
|
-
return None
|
|
62
|
+
return "gemini"
|
|
66
63
|
|
|
67
64
|
|
|
68
65
|
def get_client_and_model():
|
|
@@ -104,18 +101,12 @@ def get_client_and_model():
|
|
|
104
101
|
|
|
105
102
|
logger.debug(f"Using Azure OpenAI {__model} for generating descriptions")
|
|
106
103
|
|
|
107
|
-
|
|
104
|
+
else:
|
|
108
105
|
__client = None
|
|
109
106
|
__model = os.getenv("GEMINI_MODEL", GEMINI_MODEL)
|
|
110
107
|
|
|
111
108
|
logger.debug(f"Using Gemini {__model} for generating descriptions")
|
|
112
109
|
|
|
113
|
-
else:
|
|
114
|
-
raise ValueError(
|
|
115
|
-
"OPENAI_API_KEY, AZURE_OPENAI_KEY, GOOGLE_API_KEY, or GEMINI_API_KEY "
|
|
116
|
-
"must be setup to use LLM features"
|
|
117
|
-
)
|
|
118
|
-
|
|
119
110
|
return __client, __model
|
|
120
111
|
|
|
121
112
|
|
|
@@ -202,16 +193,18 @@ def _build_gemini_judge_config(model):
|
|
|
202
193
|
langchain_google_genai, "GoogleGenerativeAIEmbeddings"
|
|
203
194
|
)
|
|
204
195
|
google_api_key = _get_google_api_key()
|
|
196
|
+
chat_kwargs = {"model": model}
|
|
197
|
+
embeddings_kwargs = {
|
|
198
|
+
"model": os.getenv("GEMINI_EMBEDDINGS_MODEL", GEMINI_EMBEDDINGS_MODEL),
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if google_api_key:
|
|
202
|
+
chat_kwargs["api_key"] = google_api_key
|
|
203
|
+
embeddings_kwargs["google_api_key"] = google_api_key
|
|
205
204
|
|
|
206
205
|
return (
|
|
207
|
-
ChatGoogleGenerativeAI(
|
|
208
|
-
|
|
209
|
-
api_key=google_api_key,
|
|
210
|
-
),
|
|
211
|
-
GoogleGenerativeAIEmbeddings(
|
|
212
|
-
model=os.getenv("GEMINI_EMBEDDINGS_MODEL", GEMINI_EMBEDDINGS_MODEL),
|
|
213
|
-
google_api_key=google_api_key,
|
|
214
|
-
),
|
|
206
|
+
ChatGoogleGenerativeAI(**chat_kwargs),
|
|
207
|
+
GoogleGenerativeAIEmbeddings(**embeddings_kwargs),
|
|
215
208
|
)
|
|
216
209
|
|
|
217
210
|
|
|
@@ -230,6 +223,80 @@ def _build_openai_judge_config(client, model):
|
|
|
230
223
|
)
|
|
231
224
|
|
|
232
225
|
|
|
226
|
+
def _import_deepeval_base_llm():
|
|
227
|
+
try:
|
|
228
|
+
deepeval_base_model = importlib.import_module("deepeval.models.base_model")
|
|
229
|
+
except ImportError:
|
|
230
|
+
raise ImportError(
|
|
231
|
+
"Please run `pip install validmind[llm]` to use Gemini DeepEval scorers"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
return getattr(deepeval_base_model, "DeepEvalBaseLLM")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _unwrap_deepeval_response(response):
|
|
238
|
+
return getattr(response, "content", response)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _build_gemini_deepeval_model(model):
|
|
242
|
+
DeepEvalBaseLLM = _import_deepeval_base_llm()
|
|
243
|
+
judge_llm, _ = _build_gemini_judge_config(model)
|
|
244
|
+
|
|
245
|
+
class GeminiDeepEvalModel(DeepEvalBaseLLM):
|
|
246
|
+
def __init__(self, chat_model, model_name):
|
|
247
|
+
self._chat_model = chat_model
|
|
248
|
+
self._model_name = model_name
|
|
249
|
+
self.model = self.load_model()
|
|
250
|
+
|
|
251
|
+
def load_model(self, *args, **kwargs):
|
|
252
|
+
return self._chat_model
|
|
253
|
+
|
|
254
|
+
def generate(self, prompt: str, schema=None):
|
|
255
|
+
chat_model = self.load_model()
|
|
256
|
+
if schema is not None and hasattr(chat_model, "with_structured_output"):
|
|
257
|
+
response = chat_model.with_structured_output(schema).invoke(prompt)
|
|
258
|
+
else:
|
|
259
|
+
response = chat_model.invoke(prompt)
|
|
260
|
+
|
|
261
|
+
return _unwrap_deepeval_response(response)
|
|
262
|
+
|
|
263
|
+
async def a_generate(self, prompt: str, schema=None):
|
|
264
|
+
chat_model = self.load_model()
|
|
265
|
+
if schema is not None and hasattr(chat_model, "with_structured_output"):
|
|
266
|
+
response = await chat_model.with_structured_output(schema).ainvoke(
|
|
267
|
+
prompt
|
|
268
|
+
)
|
|
269
|
+
else:
|
|
270
|
+
response = await chat_model.ainvoke(prompt)
|
|
271
|
+
|
|
272
|
+
return _unwrap_deepeval_response(response)
|
|
273
|
+
|
|
274
|
+
def get_model_name(self, *args, **kwargs):
|
|
275
|
+
return self._model_name
|
|
276
|
+
|
|
277
|
+
return GeminiDeepEvalModel(judge_llm, model)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def run_deepeval_evaluation(*, test_cases, metrics):
|
|
281
|
+
try:
|
|
282
|
+
from deepeval import evaluate
|
|
283
|
+
|
|
284
|
+
deepeval_test_run = importlib.import_module("deepeval.test_run.test_run")
|
|
285
|
+
except ImportError:
|
|
286
|
+
raise ImportError(
|
|
287
|
+
"Please run `pip install validmind[llm]` to use Gemini DeepEval scorers"
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
original_is_confident = deepeval_test_run.is_confident
|
|
291
|
+
|
|
292
|
+
try:
|
|
293
|
+
# ValidMind scorers should run locally without depending on Confident AI login state.
|
|
294
|
+
deepeval_test_run.is_confident = lambda: False
|
|
295
|
+
return evaluate(test_cases=test_cases, metrics=metrics)
|
|
296
|
+
finally:
|
|
297
|
+
deepeval_test_run.is_confident = original_is_confident
|
|
298
|
+
|
|
299
|
+
|
|
233
300
|
def get_judge_config(judge_llm=None, judge_embeddings=None):
|
|
234
301
|
Embeddings, BaseChatModel, FunctionModel = _import_judge_dependencies()
|
|
235
302
|
|
|
@@ -270,6 +337,10 @@ def get_deepeval_model():
|
|
|
270
337
|
_, model = get_client_and_model()
|
|
271
338
|
|
|
272
339
|
if provider == "gemini":
|
|
340
|
+
google_api_key = _get_google_api_key()
|
|
341
|
+
if google_api_key is None:
|
|
342
|
+
return _build_gemini_deepeval_model(model)
|
|
343
|
+
|
|
273
344
|
try:
|
|
274
345
|
deepeval_models = importlib.import_module("deepeval.models")
|
|
275
346
|
except ImportError:
|
|
@@ -280,7 +351,7 @@ def get_deepeval_model():
|
|
|
280
351
|
GeminiModel = getattr(deepeval_models, "GeminiModel")
|
|
281
352
|
return GeminiModel(
|
|
282
353
|
model=model,
|
|
283
|
-
api_key=
|
|
354
|
+
api_key=google_api_key,
|
|
284
355
|
temperature=0,
|
|
285
356
|
)
|
|
286
357
|
|
|
@@ -21,7 +21,12 @@ from aiohttp import FormData
|
|
|
21
21
|
|
|
22
22
|
from .__version__ import __version__
|
|
23
23
|
from .client_config import client_config
|
|
24
|
-
from .errors import
|
|
24
|
+
from .errors import (
|
|
25
|
+
MissingAPICredentialsError,
|
|
26
|
+
MissingModelIdError,
|
|
27
|
+
ValidMindAuthError,
|
|
28
|
+
raise_api_error,
|
|
29
|
+
)
|
|
25
30
|
from .logging import get_logger, log_api_operation
|
|
26
31
|
from .utils import NumpyEncoder, is_html, md_to_html, run_async
|
|
27
32
|
from .vm_models.figure import Figure
|
|
@@ -34,10 +39,30 @@ _api_host = os.getenv("VM_API_HOST")
|
|
|
34
39
|
_model_cuid = os.getenv("VM_API_MODEL")
|
|
35
40
|
_document = None
|
|
36
41
|
_monitoring = False
|
|
42
|
+
_auth_mode = "api_key"
|
|
43
|
+
_access_token: Optional[str] = None
|
|
44
|
+
_oidc_login_context: Optional[Dict[str, str]] = None
|
|
37
45
|
|
|
38
46
|
__api_session: Optional[aiohttp.ClientSession] = None
|
|
39
47
|
|
|
40
48
|
|
|
49
|
+
def _invalidate_async_session() -> None:
|
|
50
|
+
"""Drop the aiohttp session so the next request picks up new headers."""
|
|
51
|
+
global __api_session
|
|
52
|
+
sess = __api_session
|
|
53
|
+
__api_session = None
|
|
54
|
+
if sess is None or sess.closed:
|
|
55
|
+
return
|
|
56
|
+
try:
|
|
57
|
+
loop = asyncio.get_running_loop()
|
|
58
|
+
loop.create_task(sess.close())
|
|
59
|
+
except RuntimeError:
|
|
60
|
+
try:
|
|
61
|
+
asyncio.run(sess.close())
|
|
62
|
+
except RuntimeError:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
41
66
|
@atexit.register
|
|
42
67
|
def _close_session():
|
|
43
68
|
"""Closes the async client session at exit."""
|
|
@@ -70,14 +95,21 @@ def get_api_model() -> Optional[str]:
|
|
|
70
95
|
|
|
71
96
|
def _get_api_headers() -> Dict[str, str]:
|
|
72
97
|
headers = {
|
|
73
|
-
"X-API-KEY": _api_key,
|
|
74
|
-
"X-API-SECRET": _api_secret,
|
|
75
98
|
"X-MODEL-CUID": _model_cuid,
|
|
76
99
|
"X-MONITORING": str(_monitoring),
|
|
77
100
|
"X-LIBRARY-VERSION": __version__,
|
|
78
101
|
}
|
|
79
102
|
if _document:
|
|
80
103
|
headers["X-DOCUMENT-TYPE"] = _document
|
|
104
|
+
if _auth_mode == "oidc":
|
|
105
|
+
if not _access_token:
|
|
106
|
+
raise ValidMindAuthError(
|
|
107
|
+
"OAuth access token is missing. Run vm.init() again with issuer and client_id."
|
|
108
|
+
)
|
|
109
|
+
headers["Authorization"] = f"Bearer {_access_token}"
|
|
110
|
+
else:
|
|
111
|
+
headers["X-API-KEY"] = _api_key
|
|
112
|
+
headers["X-API-SECRET"] = _api_secret
|
|
81
113
|
return headers
|
|
82
114
|
|
|
83
115
|
|
|
@@ -192,14 +224,71 @@ def _ping() -> Dict[str, Any]:
|
|
|
192
224
|
)
|
|
193
225
|
|
|
194
226
|
|
|
227
|
+
def _obtain_oidc_tokens(
|
|
228
|
+
issuer: str,
|
|
229
|
+
client_id: str,
|
|
230
|
+
scope: str,
|
|
231
|
+
audience: Optional[str] = None,
|
|
232
|
+
) -> Dict[str, Any]:
|
|
233
|
+
"""Return a credentials entry dict with access_token, expires_at, refresh_token, etc."""
|
|
234
|
+
from .credentials_store import (
|
|
235
|
+
delete_cached_entry,
|
|
236
|
+
get_cached_entry,
|
|
237
|
+
is_expired,
|
|
238
|
+
normalize_client_id,
|
|
239
|
+
normalize_issuer,
|
|
240
|
+
upsert_cached_entry,
|
|
241
|
+
)
|
|
242
|
+
from .oidc_device import run_device_flow, try_refresh_cached_tokens
|
|
243
|
+
|
|
244
|
+
norm_issuer = normalize_issuer(issuer)
|
|
245
|
+
norm_client_id = normalize_client_id(client_id)
|
|
246
|
+
cached = get_cached_entry(norm_issuer, norm_client_id, audience=audience)
|
|
247
|
+
if cached and not is_expired(cached):
|
|
248
|
+
return cached
|
|
249
|
+
if cached and cached.get("refresh_token"):
|
|
250
|
+
try:
|
|
251
|
+
new_tokens = try_refresh_cached_tokens(
|
|
252
|
+
norm_issuer,
|
|
253
|
+
norm_client_id,
|
|
254
|
+
cached["refresh_token"],
|
|
255
|
+
scope,
|
|
256
|
+
audience=audience,
|
|
257
|
+
)
|
|
258
|
+
upsert_cached_entry(
|
|
259
|
+
norm_issuer, norm_client_id, new_tokens, audience=audience
|
|
260
|
+
)
|
|
261
|
+
return new_tokens
|
|
262
|
+
except ValidMindAuthError:
|
|
263
|
+
delete_cached_entry(norm_issuer, norm_client_id, audience=audience)
|
|
264
|
+
tokens = run_device_flow(norm_issuer, norm_client_id, scope, audience=audience)
|
|
265
|
+
upsert_cached_entry(norm_issuer, norm_client_id, tokens, audience=audience)
|
|
266
|
+
return tokens
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _is_entra_issuer(issuer: str) -> bool:
|
|
270
|
+
return "login.microsoftonline.com" in issuer.lower()
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _select_oidc_bearer_token(entry: Dict[str, Any]) -> str:
|
|
274
|
+
if _is_entra_issuer(entry.get("issuer", "")) and entry.get("id_token"):
|
|
275
|
+
return entry["id_token"]
|
|
276
|
+
return entry["access_token"]
|
|
277
|
+
|
|
278
|
+
|
|
195
279
|
def init(
|
|
196
280
|
api_key: Optional[str] = None,
|
|
197
281
|
api_secret: Optional[str] = None,
|
|
198
282
|
api_host: Optional[str] = None,
|
|
283
|
+
api_url: Optional[str] = None,
|
|
199
284
|
model: Optional[str] = None,
|
|
200
285
|
monitoring: bool = False,
|
|
201
286
|
generate_descriptions: Optional[bool] = None,
|
|
202
287
|
document: Optional[str] = None,
|
|
288
|
+
issuer: Optional[str] = None,
|
|
289
|
+
client_id: Optional[str] = None,
|
|
290
|
+
scope: Optional[str] = None,
|
|
291
|
+
audience: Optional[str] = None,
|
|
203
292
|
):
|
|
204
293
|
"""
|
|
205
294
|
Initializes the API client instances and calls the /ping endpoint to ensure
|
|
@@ -208,36 +297,82 @@ def init(
|
|
|
208
297
|
If the API key and secret are not provided, the client will attempt to
|
|
209
298
|
retrieve them from the environment variables `VM_API_KEY` and `VM_API_SECRET`.
|
|
210
299
|
|
|
300
|
+
Alternatively, pass ``issuer`` and ``client_id`` or set their ``VM_OIDC_*``
|
|
301
|
+
environment variables to authenticate via the OIDC device authorization flow
|
|
302
|
+
(RFC 8628). Tokens are cached under ``~/.validmind/credentials.json``. Do not
|
|
303
|
+
combine API keys with OIDC parameters.
|
|
304
|
+
|
|
211
305
|
Args:
|
|
212
306
|
model (str, optional): The model CUID. Defaults to None.
|
|
213
307
|
api_key (str, optional): The API key. Defaults to None.
|
|
214
308
|
api_secret (str, optional): The API secret. Defaults to None.
|
|
215
|
-
api_host (str, optional): The API host. Defaults to
|
|
309
|
+
api_host (str, optional): The API host (tracking base URL). Defaults to
|
|
310
|
+
env ``VM_API_HOST`` or ``VM_API_URL``.
|
|
311
|
+
api_url (str, optional): Alias for ``api_host``. Defaults to env
|
|
312
|
+
``VM_API_URL`` or ``VM_API_HOST``.
|
|
216
313
|
monitoring (bool): The ongoing monitoring flag. Defaults to False.
|
|
217
314
|
generate_descriptions (bool, optional): Whether to use GenAI to generate test result descriptions. Defaults to True.
|
|
218
315
|
document (str, optional): The name of the document. Omitting this argument is deprecated.
|
|
316
|
+
issuer (str, optional): OIDC issuer URL (e.g. Entra tenant ``.../v2.0``).
|
|
317
|
+
Can be set via env ``VM_OIDC_ISSUER``.
|
|
318
|
+
client_id (str, optional): OAuth public client id for device flow. Can be
|
|
319
|
+
set via env ``VM_OIDC_CLIENT_ID``.
|
|
320
|
+
scope (str, optional): OAuth scopes (default ``openid profile email``).
|
|
321
|
+
Can be set via env ``VM_OIDC_SCOPE``.
|
|
322
|
+
audience (str, optional): Resource / API identifier for the access token
|
|
323
|
+
(e.g. Auth0 API Identifier). Use the same value the ValidMind backend
|
|
324
|
+
expects as ``api_audience`` so the provider can issue RS256 API tokens.
|
|
325
|
+
Can be set via env ``VM_OIDC_AUDIENCE``.
|
|
326
|
+
|
|
219
327
|
Raises:
|
|
220
|
-
|
|
328
|
+
MissingAPICredentialsError: If neither API keys nor OIDC parameters can be resolved.
|
|
329
|
+
MissingModelIdError: If model id is missing.
|
|
330
|
+
ValidMindAuthError: If OIDC configuration conflicts or login fails.
|
|
221
331
|
"""
|
|
222
332
|
global _api_key, _api_secret, _api_host, _model_cuid, _monitoring, _document
|
|
333
|
+
global _auth_mode, _access_token, _oidc_login_context
|
|
223
334
|
|
|
224
335
|
if api_key == "...":
|
|
225
336
|
# special case to detect when running a notebook placeholder (...)
|
|
226
337
|
# will override with environment variables for easier local development
|
|
227
|
-
api_host = api_key = api_secret = model =
|
|
338
|
+
api_host = api_url = api_key = api_secret = model = issuer = client_id = (
|
|
339
|
+
audience
|
|
340
|
+
) = None
|
|
228
341
|
|
|
229
342
|
_model_cuid = model or os.getenv("VM_API_MODEL")
|
|
230
343
|
if _model_cuid is None:
|
|
231
344
|
raise MissingModelIdError()
|
|
232
345
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
346
|
+
resolved_host = (
|
|
347
|
+
api_url
|
|
348
|
+
or api_host
|
|
349
|
+
or os.getenv("VM_API_URL")
|
|
350
|
+
or os.getenv("VM_API_HOST", "http://localhost:5000/api/v1/tracking/")
|
|
351
|
+
)
|
|
352
|
+
oidc_issuer = issuer if issuer is not None else os.getenv("VM_OIDC_ISSUER")
|
|
353
|
+
oidc_client_id = (
|
|
354
|
+
client_id if client_id is not None else os.getenv("VM_OIDC_CLIENT_ID")
|
|
355
|
+
)
|
|
356
|
+
oidc_scope = scope if scope is not None else os.getenv("VM_OIDC_SCOPE")
|
|
357
|
+
|
|
358
|
+
env_key = api_key if api_key is not None else os.getenv("VM_API_KEY")
|
|
359
|
+
env_secret = api_secret if api_secret is not None else os.getenv("VM_API_SECRET")
|
|
360
|
+
has_api_creds = (
|
|
361
|
+
env_key is not None
|
|
362
|
+
and env_secret is not None
|
|
363
|
+
and env_key != ""
|
|
364
|
+
and env_secret != ""
|
|
240
365
|
)
|
|
366
|
+
has_oidc = bool(oidc_issuer and oidc_client_id)
|
|
367
|
+
if has_oidc and has_api_creds:
|
|
368
|
+
raise ValidMindAuthError(
|
|
369
|
+
"Provide either API credentials (api_key and api_secret) or OIDC "
|
|
370
|
+
"(issuer and client_id), not both."
|
|
371
|
+
)
|
|
372
|
+
if oidc_issuer and not oidc_client_id:
|
|
373
|
+
raise ValidMindAuthError("client_id is required when issuer is set.")
|
|
374
|
+
if oidc_client_id and not oidc_issuer:
|
|
375
|
+
raise ValidMindAuthError("issuer is required when client_id is set.")
|
|
241
376
|
|
|
242
377
|
_monitoring = monitoring
|
|
243
378
|
|
|
@@ -251,6 +386,41 @@ def init(
|
|
|
251
386
|
)
|
|
252
387
|
|
|
253
388
|
_document = document
|
|
389
|
+
|
|
390
|
+
if has_oidc:
|
|
391
|
+
_auth_mode = "oidc"
|
|
392
|
+
_api_key = None
|
|
393
|
+
_api_secret = None
|
|
394
|
+
_api_host = resolved_host
|
|
395
|
+
scope_val = oidc_scope or "openid profile email"
|
|
396
|
+
from .credentials_store import normalize_audience
|
|
397
|
+
|
|
398
|
+
oidc_audience_val = normalize_audience(
|
|
399
|
+
audience if audience is not None else os.getenv("VM_OIDC_AUDIENCE")
|
|
400
|
+
)
|
|
401
|
+
oidc_audience_opt = oidc_audience_val or None
|
|
402
|
+
entry = _obtain_oidc_tokens(
|
|
403
|
+
oidc_issuer, oidc_client_id, scope_val, audience=oidc_audience_opt
|
|
404
|
+
)
|
|
405
|
+
_access_token = _select_oidc_bearer_token(entry)
|
|
406
|
+
_oidc_login_context = {
|
|
407
|
+
"issuer": entry["issuer"],
|
|
408
|
+
"client_id": entry["client_id"],
|
|
409
|
+
"scope": scope_val,
|
|
410
|
+
"audience": entry.get("audience") or oidc_audience_val,
|
|
411
|
+
}
|
|
412
|
+
_invalidate_async_session()
|
|
413
|
+
else:
|
|
414
|
+
if env_key is None or env_secret is None:
|
|
415
|
+
raise MissingAPICredentialsError()
|
|
416
|
+
_auth_mode = "api_key"
|
|
417
|
+
_access_token = None
|
|
418
|
+
_oidc_login_context = None
|
|
419
|
+
_api_key = env_key
|
|
420
|
+
_api_secret = env_secret
|
|
421
|
+
_api_host = resolved_host
|
|
422
|
+
_invalidate_async_session()
|
|
423
|
+
|
|
254
424
|
reload()
|
|
255
425
|
|
|
256
426
|
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Copyright © 2023-2026 ValidMind Inc. All rights reserved.
|
|
2
|
+
# Refer to the LICENSE file in the root of this repository for details.
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
|
|
4
|
+
|
|
5
|
+
"""Persist OIDC tokens for library authentication under ``~/.validmind/``."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import tempfile
|
|
12
|
+
from datetime import datetime, timedelta, timezone
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Dict, Optional
|
|
15
|
+
|
|
16
|
+
from .errors import ValidMindAuthError
|
|
17
|
+
|
|
18
|
+
_CREDENTIALS_VERSION = 1
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def normalize_issuer(issuer: str) -> str:
|
|
22
|
+
"""Strip whitespace, trailing slash, and matching outer quotes.
|
|
23
|
+
|
|
24
|
+
Surrounding quotes often appear when copying from ``.env`` or notebooks
|
|
25
|
+
(e.g. ``'https://idp.example.com'``), which would otherwise break HTTP clients.
|
|
26
|
+
"""
|
|
27
|
+
base = issuer.strip().rstrip("/")
|
|
28
|
+
while len(base) >= 2 and base[0] == base[-1] and base[0] in ('"', "'"):
|
|
29
|
+
base = base[1:-1].strip().rstrip("/")
|
|
30
|
+
return base
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def normalize_client_id(client_id: str) -> str:
|
|
34
|
+
"""Strip whitespace and matching outer quotes from OAuth ``client_id``."""
|
|
35
|
+
base = client_id.strip()
|
|
36
|
+
while len(base) >= 2 and base[0] == base[-1] and base[0] in ('"', "'"):
|
|
37
|
+
base = base[1:-1].strip()
|
|
38
|
+
return base
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def normalize_audience(audience: Optional[str]) -> str:
|
|
42
|
+
"""Normalize OAuth resource/API ``audience`` (Identifier). Empty if unset."""
|
|
43
|
+
if not audience:
|
|
44
|
+
return ""
|
|
45
|
+
base = audience.strip()
|
|
46
|
+
while len(base) >= 2 and base[0] == base[-1] and base[0] in ('"', "'"):
|
|
47
|
+
base = base[1:-1].strip()
|
|
48
|
+
return base
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def credential_key(issuer: str, client_id: str, audience: Optional[str] = None) -> str:
|
|
52
|
+
base = f"{normalize_issuer(issuer)}|{normalize_client_id(client_id)}"
|
|
53
|
+
aud = normalize_audience(audience)
|
|
54
|
+
if aud:
|
|
55
|
+
return f"{base}|{aud}"
|
|
56
|
+
return base
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def credentials_path() -> Path:
|
|
60
|
+
return Path.home() / ".validmind" / "credentials.json"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _empty_store() -> Dict[str, Any]:
|
|
64
|
+
return {"version": _CREDENTIALS_VERSION, "credentials": {}}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def load_credentials_file(path: Optional[Path] = None) -> Dict[str, Any]:
|
|
68
|
+
path = path or credentials_path()
|
|
69
|
+
if not path.is_file():
|
|
70
|
+
return _empty_store()
|
|
71
|
+
try:
|
|
72
|
+
with open(path, encoding="utf-8") as f:
|
|
73
|
+
data = json.load(f)
|
|
74
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
75
|
+
raise ValidMindAuthError(f"Could not read credentials file {path}: {e}") from e
|
|
76
|
+
if not isinstance(data, dict):
|
|
77
|
+
raise ValidMindAuthError(f"Invalid credentials file format at {path}")
|
|
78
|
+
data.setdefault("version", _CREDENTIALS_VERSION)
|
|
79
|
+
data.setdefault("credentials", {})
|
|
80
|
+
return data
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _atomic_write(path: Path, payload: Dict[str, Any]) -> None:
|
|
84
|
+
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
85
|
+
fd, tmp = tempfile.mkstemp(
|
|
86
|
+
dir=str(path.parent), prefix=".credentials-", suffix=".tmp", text=True
|
|
87
|
+
)
|
|
88
|
+
tmp_path = Path(tmp)
|
|
89
|
+
try:
|
|
90
|
+
with os.fdopen(fd, "w", encoding="utf-8") as handle:
|
|
91
|
+
json.dump(payload, handle, indent=2)
|
|
92
|
+
os.chmod(tmp_path, 0o600)
|
|
93
|
+
os.replace(tmp_path, path)
|
|
94
|
+
except Exception:
|
|
95
|
+
try:
|
|
96
|
+
tmp_path.unlink()
|
|
97
|
+
except OSError:
|
|
98
|
+
pass
|
|
99
|
+
raise
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def save_credentials_file(data: Dict[str, Any], path: Optional[Path] = None) -> None:
|
|
103
|
+
path = path or credentials_path()
|
|
104
|
+
data = dict(data)
|
|
105
|
+
data["version"] = _CREDENTIALS_VERSION
|
|
106
|
+
if "credentials" not in data or not isinstance(data["credentials"], dict):
|
|
107
|
+
data["credentials"] = {}
|
|
108
|
+
_atomic_write(path, data)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_cached_entry(
|
|
112
|
+
issuer: str,
|
|
113
|
+
client_id: str,
|
|
114
|
+
path: Optional[Path] = None,
|
|
115
|
+
audience: Optional[str] = None,
|
|
116
|
+
) -> Optional[Dict[str, Any]]:
|
|
117
|
+
key = credential_key(issuer, client_id, audience)
|
|
118
|
+
data = load_credentials_file(path)
|
|
119
|
+
entry = data.get("credentials", {}).get(key)
|
|
120
|
+
if not entry:
|
|
121
|
+
return None
|
|
122
|
+
return dict(entry)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def upsert_cached_entry(
|
|
126
|
+
issuer: str,
|
|
127
|
+
client_id: str,
|
|
128
|
+
entry: Dict[str, Any],
|
|
129
|
+
path: Optional[Path] = None,
|
|
130
|
+
audience: Optional[str] = None,
|
|
131
|
+
) -> None:
|
|
132
|
+
key = credential_key(issuer, client_id, audience)
|
|
133
|
+
norm_issuer = normalize_issuer(issuer)
|
|
134
|
+
aud = normalize_audience(audience)
|
|
135
|
+
data = load_credentials_file(path)
|
|
136
|
+
credentials = dict(data.get("credentials", {}))
|
|
137
|
+
row = {
|
|
138
|
+
"issuer": norm_issuer,
|
|
139
|
+
"client_id": client_id,
|
|
140
|
+
**entry,
|
|
141
|
+
}
|
|
142
|
+
if aud:
|
|
143
|
+
row["audience"] = aud
|
|
144
|
+
credentials[key] = row
|
|
145
|
+
data["credentials"] = credentials
|
|
146
|
+
save_credentials_file(data, path)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def delete_cached_entry(
|
|
150
|
+
issuer: str,
|
|
151
|
+
client_id: str,
|
|
152
|
+
path: Optional[Path] = None,
|
|
153
|
+
audience: Optional[str] = None,
|
|
154
|
+
) -> None:
|
|
155
|
+
key = credential_key(issuer, client_id, audience)
|
|
156
|
+
data = load_credentials_file(path)
|
|
157
|
+
credentials = dict(data.get("credentials", {}))
|
|
158
|
+
credentials.pop(key, None)
|
|
159
|
+
data["credentials"] = credentials
|
|
160
|
+
save_credentials_file(data, path)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def is_expired(entry: Dict[str, Any], skew_seconds: int = 120) -> bool:
|
|
164
|
+
raw = entry.get("expires_at")
|
|
165
|
+
if not raw:
|
|
166
|
+
return True
|
|
167
|
+
try:
|
|
168
|
+
expires = datetime.fromisoformat(raw.replace("Z", "+00:00"))
|
|
169
|
+
except ValueError:
|
|
170
|
+
return True
|
|
171
|
+
if expires.tzinfo is None:
|
|
172
|
+
expires = expires.replace(tzinfo=timezone.utc)
|
|
173
|
+
return datetime.now(timezone.utc) >= expires - timedelta(seconds=skew_seconds)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def expires_at_from_secs(expires_in: Optional[int]) -> str:
|
|
177
|
+
seconds = int(expires_in) if expires_in is not None else 3600
|
|
178
|
+
when = datetime.now(timezone.utc) + timedelta(seconds=seconds)
|
|
179
|
+
return when.isoformat()
|