procurelytics-ai 0.1.0__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.
Files changed (27) hide show
  1. procurelytics_ai-0.1.0/.gitignore +12 -0
  2. procurelytics_ai-0.1.0/LICENSE +0 -0
  3. procurelytics_ai-0.1.0/PKG-INFO +217 -0
  4. procurelytics_ai-0.1.0/README.md +188 -0
  5. procurelytics_ai-0.1.0/pyproject.toml +48 -0
  6. procurelytics_ai-0.1.0/src/procurelytics_ai/__init__.py +3 -0
  7. procurelytics_ai-0.1.0/src/procurelytics_ai/cli.py +61 -0
  8. procurelytics_ai-0.1.0/src/procurelytics_ai/config.py +7 -0
  9. procurelytics_ai-0.1.0/src/procurelytics_ai/datasets/__init__.py +0 -0
  10. procurelytics_ai-0.1.0/src/procurelytics_ai/datasets/loaders.py +13 -0
  11. procurelytics_ai-0.1.0/src/procurelytics_ai/datasets/schema.py +13 -0
  12. procurelytics_ai-0.1.0/src/procurelytics_ai/esg/__init__.py +0 -0
  13. procurelytics_ai-0.1.0/src/procurelytics_ai/esg/scoring.py +39 -0
  14. procurelytics_ai-0.1.0/src/procurelytics_ai/exceptions.py +11 -0
  15. procurelytics_ai-0.1.0/src/procurelytics_ai/governance/__init__.py +0 -0
  16. procurelytics_ai-0.1.0/src/procurelytics_ai/governance/responsible_ai.py +47 -0
  17. procurelytics_ai-0.1.0/src/procurelytics_ai/insights/__init__.py +0 -0
  18. procurelytics_ai-0.1.0/src/procurelytics_ai/insights/cards.py +18 -0
  19. procurelytics_ai-0.1.0/src/procurelytics_ai/insights/prompts.py +16 -0
  20. procurelytics_ai-0.1.0/src/procurelytics_ai/ml/__init__.py +0 -0
  21. procurelytics_ai-0.1.0/src/procurelytics_ai/ml/features.py +35 -0
  22. procurelytics_ai-0.1.0/src/procurelytics_ai/ml/models.py +24 -0
  23. procurelytics_ai-0.1.0/src/procurelytics_ai/ml/predict.py +32 -0
  24. procurelytics_ai-0.1.0/src/procurelytics_ai/process_mining/__init__.py +0 -0
  25. procurelytics_ai-0.1.0/src/procurelytics_ai/process_mining/mining.py +52 -0
  26. procurelytics_ai-0.1.0/tests/test_process_mining.py +0 -0
  27. procurelytics_ai-0.1.0/tests/test_responsible_ai.py +14 -0
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .env
8
+ .DS_Store
9
+ .vscode/
10
+ .idea/
11
+ .pytest_cache/
12
+
File without changes
@@ -0,0 +1,217 @@
1
+ Metadata-Version: 2.4
2
+ Name: procurelytics-ai
3
+ Version: 0.1.0
4
+ Summary: AI-powered procurement analytics: predictive sourcing, spend optimization, process mining, ESG scoring, and responsible AI governance.
5
+ Project-URL: Homepage, https://github.com/jvasanthada/procurelytics-ai
6
+ Project-URL: Issues, https://github.com/jvasanthada/procurelytics-ai/issues
7
+ Author: Jagadeesh Vasanthada
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai,esg,governance,ml,process-mining,procurement,sourcing,spend-analytics
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Financial and Insurance Industry
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Office/Business
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Python: >=3.9
22
+ Requires-Dist: numpy>=1.26.0
23
+ Requires-Dist: pandas>=2.2.0
24
+ Requires-Dist: pydantic>=2.7.0
25
+ Requires-Dist: rich>=13.7.1
26
+ Requires-Dist: scikit-learn>=1.4.0
27
+ Requires-Dist: typer>=0.12.3
28
+ Description-Content-Type: text/markdown
29
+
30
+ # procurelytics-ai
31
+
32
+ **procurelytics-ai** is an open-source Python library that delivers **AI-powered procurement analytics** for modern procurement and ERP environments.
33
+
34
+ It is designed to help teams transform raw procurement data into:
35
+ - **Predictive sourcing signals** (ML-based supplier health / risk scoring)
36
+ - **Spend optimization intelligence** (category/supplier consolidation hooks)
37
+ - **Process mining foundations** (variants, lead-time patterns; extensible)
38
+ - **ESG & sustainability scoring** (supplier responsibility insights)
39
+ - **Responsible AI governance checks** (data quality and representation proxy checks)
40
+ - **GenAI-ready prompts** (provider-agnostic prompts for copilots and LLMs)
41
+
42
+ The library is vendor-neutral and works with exports from **SAP/ERP systems**, procurement platforms, and analytics warehouses.
43
+
44
+ ---
45
+
46
+ ## Why procurelytics-ai?
47
+
48
+ Procurement teams often face these challenges:
49
+ - Lack of visibility into supplier performance risks
50
+ - Difficulty detecting where procurement cycles slow down
51
+ - ESG and compliance pressures with inconsistent data
52
+ - A need for executive-friendly insights, not just raw reports
53
+
54
+ **procurelytics-ai** provides explainable analytics primitives plus AI-ready outputs so you can:
55
+ - prioritize supplier strategies,
56
+ - reduce risk,
57
+ - improve resilience, and
58
+ - accelerate decision-making.
59
+
60
+ ---
61
+
62
+ ## Installation
63
+
64
+ Install from PyPI:
65
+
66
+ ```bash
67
+ pip install procurelytics-ai
68
+ ```
69
+ ## Requirements
70
+ - Python 3.9+
71
+
72
+ ## Quick Start (CLI)
73
+ ### 1) Prepare a supplier dataset
74
+
75
+ #### Supported formats:
76
+
77
+ - .csv
78
+
79
+ - .json
80
+
81
+ - .jsonl
82
+
83
+ #### Recommended columns:
84
+
85
+ - supplier_id (required)
86
+
87
+ - esg_score (0..100)
88
+
89
+ - on_time_rate (0..1)
90
+
91
+ - defect_rate (0..1)
92
+
93
+ - country (optional)
94
+
95
+ - category (optional)
96
+
97
+ ## Example CSV:
98
+
99
+ ```
100
+ supplier_id,country,category,esg_score,on_time_rate,defect_rate
101
+ SUP-1,US,IT,88,0.93,0.01
102
+ SUP-2,IN,Hardware,72,0.84,0.03
103
+ SUP-3,US,IT,55,0.78,0.05
104
+ ```
105
+
106
+ ## 2) Run supplier scoring
107
+ ```
108
+ procurelytics score-suppliers suppliers.csv
109
+ ```
110
+
111
+ ### You will get:
112
+
113
+ - top supplier ranking (health score)
114
+
115
+ - governance findings (data quality + representation proxy checks)
116
+
117
+ - a provider-agnostic GenAI prompt you can paste into any LLM/copilot
118
+
119
+ ## Programmatic Usage
120
+ ### Supplier feature engineering + ML scoring
121
+
122
+ ```
123
+ import pandas as pd
124
+ from procurelytics_ai.ml.features import build_supplier_features
125
+ from procurelytics_ai.ml.predict import train_and_score_suppliers
126
+
127
+ df = pd.read_csv("suppliers.csv")
128
+ X = build_supplier_features(df)
129
+ ranked = train_and_score_suppliers(X)
130
+
131
+ print(ranked.head(10))
132
+ ```
133
+
134
+ ### ESG scoring (optional)
135
+
136
+ ```
137
+ from procurelytics_ai.esg.scoring import weighted_esg_score
138
+
139
+ score = weighted_esg_score(
140
+ supplier_id="SUP-1",
141
+ factors={"co2_intensity": 0.7, "compliance": 0.9, "labor_risk": 0.8}
142
+ )
143
+
144
+ print(score.score, score.rationale)
145
+ ```
146
+
147
+ ### Responsible AI / Governance
148
+
149
+ - procurelytics-ai includes lightweight governance checks to support safer analytics:
150
+
151
+ #### Data quality report
152
+
153
+ - warns on high missingness
154
+
155
+ - flags invalid ESG score ranges
156
+
157
+ #### Representation proxy check
158
+
159
+ - warns when one group dominates the dataset (e.g., one country is 80%+)
160
+
161
+ - These checks are intentionally lightweight and transparent—useful for production workflows without heavy dependencies.
162
+
163
+ ### GenAI Support
164
+
165
+ - procurelytics-ai generates a provider-agnostic GenAI prompt, so you can feed results into:
166
+
167
+ - internal copilots
168
+
169
+ - chatbots
170
+
171
+ - LLM tools (OpenAI / Vertex / Azure / local models)
172
+
173
+ - This package does not force any vendor lock-in.
174
+
175
+ ### Testing
176
+ ```
177
+ pytest
178
+ ```
179
+
180
+ ## Roadmap
181
+
182
+ #### Planned enhancements:
183
+
184
+ - CSV/BigQuery ingestion utilities
185
+
186
+ - Spend optimization modules (leakage detection, consolidation suggestions)
187
+
188
+ - Process mining expansion (rework loops, conformance scoring)
189
+
190
+ - Optional LLM adapters (kept modular)
191
+
192
+ #### Contributing
193
+
194
+ ##### Contributions are welcome:
195
+
196
+ - new analytics modules
197
+
198
+ - data adapters for ERP platforms
199
+
200
+ - examples and notebooks
201
+
202
+ - documentation improvements
203
+
204
+
205
+ ## MIT License
206
+
207
+ ```
208
+ MIT License
209
+
210
+ Copyright (c) 2026 Jagadeesh Vasanthada
211
+
212
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
213
+
214
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
215
+
216
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
217
+ ```
@@ -0,0 +1,188 @@
1
+ # procurelytics-ai
2
+
3
+ **procurelytics-ai** is an open-source Python library that delivers **AI-powered procurement analytics** for modern procurement and ERP environments.
4
+
5
+ It is designed to help teams transform raw procurement data into:
6
+ - **Predictive sourcing signals** (ML-based supplier health / risk scoring)
7
+ - **Spend optimization intelligence** (category/supplier consolidation hooks)
8
+ - **Process mining foundations** (variants, lead-time patterns; extensible)
9
+ - **ESG & sustainability scoring** (supplier responsibility insights)
10
+ - **Responsible AI governance checks** (data quality and representation proxy checks)
11
+ - **GenAI-ready prompts** (provider-agnostic prompts for copilots and LLMs)
12
+
13
+ The library is vendor-neutral and works with exports from **SAP/ERP systems**, procurement platforms, and analytics warehouses.
14
+
15
+ ---
16
+
17
+ ## Why procurelytics-ai?
18
+
19
+ Procurement teams often face these challenges:
20
+ - Lack of visibility into supplier performance risks
21
+ - Difficulty detecting where procurement cycles slow down
22
+ - ESG and compliance pressures with inconsistent data
23
+ - A need for executive-friendly insights, not just raw reports
24
+
25
+ **procurelytics-ai** provides explainable analytics primitives plus AI-ready outputs so you can:
26
+ - prioritize supplier strategies,
27
+ - reduce risk,
28
+ - improve resilience, and
29
+ - accelerate decision-making.
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ Install from PyPI:
36
+
37
+ ```bash
38
+ pip install procurelytics-ai
39
+ ```
40
+ ## Requirements
41
+ - Python 3.9+
42
+
43
+ ## Quick Start (CLI)
44
+ ### 1) Prepare a supplier dataset
45
+
46
+ #### Supported formats:
47
+
48
+ - .csv
49
+
50
+ - .json
51
+
52
+ - .jsonl
53
+
54
+ #### Recommended columns:
55
+
56
+ - supplier_id (required)
57
+
58
+ - esg_score (0..100)
59
+
60
+ - on_time_rate (0..1)
61
+
62
+ - defect_rate (0..1)
63
+
64
+ - country (optional)
65
+
66
+ - category (optional)
67
+
68
+ ## Example CSV:
69
+
70
+ ```
71
+ supplier_id,country,category,esg_score,on_time_rate,defect_rate
72
+ SUP-1,US,IT,88,0.93,0.01
73
+ SUP-2,IN,Hardware,72,0.84,0.03
74
+ SUP-3,US,IT,55,0.78,0.05
75
+ ```
76
+
77
+ ## 2) Run supplier scoring
78
+ ```
79
+ procurelytics score-suppliers suppliers.csv
80
+ ```
81
+
82
+ ### You will get:
83
+
84
+ - top supplier ranking (health score)
85
+
86
+ - governance findings (data quality + representation proxy checks)
87
+
88
+ - a provider-agnostic GenAI prompt you can paste into any LLM/copilot
89
+
90
+ ## Programmatic Usage
91
+ ### Supplier feature engineering + ML scoring
92
+
93
+ ```
94
+ import pandas as pd
95
+ from procurelytics_ai.ml.features import build_supplier_features
96
+ from procurelytics_ai.ml.predict import train_and_score_suppliers
97
+
98
+ df = pd.read_csv("suppliers.csv")
99
+ X = build_supplier_features(df)
100
+ ranked = train_and_score_suppliers(X)
101
+
102
+ print(ranked.head(10))
103
+ ```
104
+
105
+ ### ESG scoring (optional)
106
+
107
+ ```
108
+ from procurelytics_ai.esg.scoring import weighted_esg_score
109
+
110
+ score = weighted_esg_score(
111
+ supplier_id="SUP-1",
112
+ factors={"co2_intensity": 0.7, "compliance": 0.9, "labor_risk": 0.8}
113
+ )
114
+
115
+ print(score.score, score.rationale)
116
+ ```
117
+
118
+ ### Responsible AI / Governance
119
+
120
+ - procurelytics-ai includes lightweight governance checks to support safer analytics:
121
+
122
+ #### Data quality report
123
+
124
+ - warns on high missingness
125
+
126
+ - flags invalid ESG score ranges
127
+
128
+ #### Representation proxy check
129
+
130
+ - warns when one group dominates the dataset (e.g., one country is 80%+)
131
+
132
+ - These checks are intentionally lightweight and transparent—useful for production workflows without heavy dependencies.
133
+
134
+ ### GenAI Support
135
+
136
+ - procurelytics-ai generates a provider-agnostic GenAI prompt, so you can feed results into:
137
+
138
+ - internal copilots
139
+
140
+ - chatbots
141
+
142
+ - LLM tools (OpenAI / Vertex / Azure / local models)
143
+
144
+ - This package does not force any vendor lock-in.
145
+
146
+ ### Testing
147
+ ```
148
+ pytest
149
+ ```
150
+
151
+ ## Roadmap
152
+
153
+ #### Planned enhancements:
154
+
155
+ - CSV/BigQuery ingestion utilities
156
+
157
+ - Spend optimization modules (leakage detection, consolidation suggestions)
158
+
159
+ - Process mining expansion (rework loops, conformance scoring)
160
+
161
+ - Optional LLM adapters (kept modular)
162
+
163
+ #### Contributing
164
+
165
+ ##### Contributions are welcome:
166
+
167
+ - new analytics modules
168
+
169
+ - data adapters for ERP platforms
170
+
171
+ - examples and notebooks
172
+
173
+ - documentation improvements
174
+
175
+
176
+ ## MIT License
177
+
178
+ ```
179
+ MIT License
180
+
181
+ Copyright (c) 2026 Jagadeesh Vasanthada
182
+
183
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
184
+
185
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
186
+
187
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
188
+ ```
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.24.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "procurelytics-ai"
7
+ version = "0.1.0"
8
+ description = "AI-powered procurement analytics: predictive sourcing, spend optimization, process mining, ESG scoring, and responsible AI governance."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Jagadeesh Vasanthada"}]
13
+ keywords = ["procurement", "ai", "ml", "process-mining", "esg", "sourcing", "spend-analytics", "governance"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: Financial and Insurance Industry",
18
+ "Intended Audience :: Information Technology",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Topic :: Office/Business",
23
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
24
+ "Topic :: Software Development :: Libraries",
25
+ ]
26
+
27
+ dependencies = [
28
+ "pydantic>=2.7.0",
29
+ "typer>=0.12.3",
30
+ "rich>=13.7.1",
31
+ "pandas>=2.2.0",
32
+ "numpy>=1.26.0",
33
+ "scikit-learn>=1.4.0",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/jvasanthada/procurelytics-ai"
38
+ Issues = "https://github.com/jvasanthada/procurelytics-ai/issues"
39
+
40
+ [project.scripts]
41
+ procurelytics = "procurelytics_ai.cli:app"
42
+
43
+ [tool.hatch.build.targets.wheel]
44
+ packages = ["src/procurelytics_ai"]
45
+
46
+ [tool.pytest.ini_options]
47
+ testpaths = ["tests"]
48
+
@@ -0,0 +1,3 @@
1
+ __all__ = ["__version__"]
2
+ __version__ = "0.1.0"
3
+
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+ from pathlib import Path
3
+ import typer
4
+ from rich import print
5
+
6
+ from .datasets.loaders import load_table
7
+ from .ml.features import build_supplier_features
8
+ from .ml.predict import train_and_score_suppliers
9
+ from .governance.responsible_ai import data_quality_report, representation_proxy_check
10
+ from .insights.cards import InsightCard, render_cards
11
+ from .insights.prompts import genai_prompt
12
+
13
+ app = typer.Typer(help="procurelytics-ai: AI-powered procurement analytics (predictive sourcing + governance + ESG).")
14
+
15
+
16
+ @app.command()
17
+ def score_suppliers(input_file: Path):
18
+ """
19
+ Score suppliers using a baseline ML model + Responsible AI checks.
20
+ Input file: CSV/JSON/JSONL with supplier attributes.
21
+ """
22
+ df = load_table(input_file)
23
+ X = build_supplier_features(df)
24
+ ranked = train_and_score_suppliers(X)
25
+
26
+ dq = data_quality_report(df)
27
+ rep = representation_proxy_check(df)
28
+
29
+ cards = [
30
+ InsightCard(
31
+ title="Supplier health ranking (ML baseline)",
32
+ summary="Suppliers ranked using risk signals: late/quality/ESG risk (explainable baseline model).",
33
+ data={"top_suppliers": ranked.head(10).to_dict(orient="records")},
34
+ )
35
+ ]
36
+
37
+ findings = [f.__dict__ for f in (dq + rep)]
38
+ if findings:
39
+ cards.append(
40
+ InsightCard(
41
+ title="Responsible AI / governance findings",
42
+ summary="Data quality and representation proxy checks for safer decision-making.",
43
+ data={"findings": findings},
44
+ )
45
+ )
46
+
47
+ results = {
48
+ "top_suppliers": ranked.head(10).to_dict(orient="records"),
49
+ "governance_findings": findings,
50
+ }
51
+
52
+ print("[bold]Insights[/bold]")
53
+ print(render_cards(cards))
54
+
55
+ print("\n[bold]GenAI prompt (provider-agnostic)[/bold]")
56
+ print(genai_prompt(results))
57
+
58
+
59
+ if __name__ == "__main__":
60
+ app()
61
+
@@ -0,0 +1,7 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ class ProcurelyticsConfig(BaseModel):
5
+ default_currency: str = Field(default="USD")
6
+ governance_strict: bool = Field(default=False)
7
+
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+ from pathlib import Path
3
+ import pandas as pd
4
+
5
+
6
+ def load_table(path: str | Path) -> pd.DataFrame:
7
+ path = Path(path)
8
+ if path.suffix.lower() == ".csv":
9
+ return pd.read_csv(path)
10
+ if path.suffix.lower() in [".json", ".jsonl"]:
11
+ return pd.read_json(path, lines=path.suffix.lower() == ".jsonl")
12
+ raise ValueError("Unsupported file type. Use .csv, .json, or .jsonl")
13
+
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+ from pydantic import BaseModel
3
+ from typing import Optional
4
+
5
+
6
+ class SupplierRecord(BaseModel):
7
+ supplier_id: str
8
+ country: Optional[str] = None
9
+ category: Optional[str] = None
10
+ esg_score: Optional[float] = None # 0..100
11
+ on_time_rate: Optional[float] = None # 0..1
12
+ defect_rate: Optional[float] = None # 0..1
13
+
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from typing import Dict, Optional
4
+
5
+
6
+ @dataclass
7
+ class ESGScore:
8
+ supplier_id: str
9
+ score: float # 0..100
10
+ rationale: str
11
+
12
+
13
+ def weighted_esg_score(
14
+ supplier_id: str,
15
+ factors: Dict[str, float],
16
+ weights: Optional[Dict[str, float]] = None,
17
+ ) -> ESGScore:
18
+ """
19
+ factors should be normalized 0..1 where higher is better
20
+ e.g. {"co2_intensity": 0.7, "compliance": 0.9}
21
+ """
22
+ if not factors:
23
+ return ESGScore(supplier_id, 0.0, "No ESG factors provided.")
24
+
25
+ if weights is None:
26
+ weights = {k: 1.0 for k in factors.keys()}
27
+
28
+ total_w = 0.0
29
+ total = 0.0
30
+ for k, v in factors.items():
31
+ w = float(weights.get(k, 1.0))
32
+ total_w += w
33
+ total += w * float(v)
34
+
35
+ normalized = (total / total_w) if total_w > 0 else 0.0
36
+ score = max(0.0, min(100.0, normalized * 100.0))
37
+ rationale = "Weighted ESG score from normalized factors: " + ", ".join(f"{k}={v:.2f}" for k, v in factors.items())
38
+ return ESGScore(supplier_id, score, rationale)
39
+
@@ -0,0 +1,11 @@
1
+ class ProcurelyticsError(Exception):
2
+ """Base exception for procurelytics-ai."""
3
+
4
+
5
+ class DataError(ProcurelyticsError):
6
+ """Raised when input data is missing or invalid."""
7
+
8
+
9
+ class ModelError(ProcurelyticsError):
10
+ """Raised when model training or prediction fails."""
11
+
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from typing import List
4
+ import pandas as pd
5
+
6
+
7
+ @dataclass
8
+ class GovernanceFinding:
9
+ severity: str # LOW, MEDIUM, HIGH
10
+ message: str
11
+
12
+
13
+ def data_quality_report(df: pd.DataFrame) -> List[GovernanceFinding]:
14
+ findings: List[GovernanceFinding] = []
15
+
16
+ if df.empty:
17
+ return [GovernanceFinding("HIGH", "Dataset is empty. Cannot compute analytics.")]
18
+
19
+ missing = df.isna().mean().to_dict()
20
+ high_missing = [k for k, v in missing.items() if v >= 0.30]
21
+ if high_missing:
22
+ findings.append(GovernanceFinding(
23
+ "MEDIUM",
24
+ f"High missingness (>=30%) detected in: {', '.join(high_missing)}. Consider improving source completeness."
25
+ ))
26
+
27
+ if "esg_score" in df.columns:
28
+ s = pd.to_numeric(df["esg_score"], errors="coerce").dropna()
29
+ if not s.empty and ((s < 0).any() or (s > 100).any()):
30
+ findings.append(GovernanceFinding("HIGH", "esg_score contains values outside 0..100."))
31
+
32
+ return findings
33
+
34
+
35
+ def representation_proxy_check(df: pd.DataFrame, group_col: str = "country") -> List[GovernanceFinding]:
36
+ findings: List[GovernanceFinding] = []
37
+ if group_col not in df.columns:
38
+ return findings
39
+
40
+ counts = df[group_col].fillna("UNKNOWN").value_counts(normalize=True)
41
+ if not counts.empty and counts.iloc[0] > 0.75:
42
+ findings.append(GovernanceFinding(
43
+ "MEDIUM",
44
+ f"Representation imbalance: '{counts.index[0]}' is {counts.iloc[0]*100:.1f}% of records."
45
+ ))
46
+ return findings
47
+
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from typing import Dict, Any, List
4
+
5
+
6
+ @dataclass
7
+ class InsightCard:
8
+ title: str
9
+ summary: str
10
+ data: Dict[str, Any]
11
+
12
+
13
+ def render_cards(cards: List[InsightCard]) -> str:
14
+ lines = []
15
+ for i, c in enumerate(cards, 1):
16
+ lines.append(f"{i}. {c.title}\n {c.summary}")
17
+ return "\n".join(lines)
18
+
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+ from typing import Dict, Any
3
+
4
+
5
+ def genai_prompt(results: Dict[str, Any]) -> str:
6
+ return (
7
+ "You are an AI procurement copilot. Summarize analytics into actionable recommendations.\n\n"
8
+ f"RESULTS_JSON:\n{results}\n\n"
9
+ "Return:\n"
10
+ "1) Top 3 supplier risks\n"
11
+ "2) Top 3 savings opportunities\n"
12
+ "3) Resilience improvements\n"
13
+ "4) ESG/compliance notes\n"
14
+ "Use simple executive language."
15
+ )
16
+
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+ import pandas as pd
3
+
4
+
5
+ def build_supplier_features(df: pd.DataFrame) -> pd.DataFrame:
6
+ out = df.copy()
7
+
8
+ if "supplier_id" not in out.columns:
9
+ raise ValueError("Missing required column: supplier_id")
10
+
11
+ # Defaults
12
+ defaults = {
13
+ "esg_score": 50.0,
14
+ "on_time_rate": 0.85,
15
+ "defect_rate": 0.02,
16
+ }
17
+ for col, default in defaults.items():
18
+ if col not in out.columns:
19
+ out[col] = default
20
+ out[col] = pd.to_numeric(out[col], errors="coerce").fillna(default)
21
+
22
+ out["late_risk"] = 1.0 - out["on_time_rate"].clip(0, 1)
23
+ out["quality_risk"] = out["defect_rate"].clip(0, 1)
24
+ out["esg_risk"] = 1.0 - (out["esg_score"].clip(0, 100) / 100.0)
25
+
26
+ # category diversity can help approximate resilience
27
+ if "category" in out.columns:
28
+ cat_div = out.groupby("supplier_id")["category"].nunique().rename("category_diversity")
29
+ out = out.merge(cat_div, on="supplier_id", how="left")
30
+ else:
31
+ out["category_diversity"] = 1
32
+
33
+ features = out[["supplier_id", "late_risk", "quality_risk", "esg_risk", "category_diversity"]]
34
+ return features.drop_duplicates("supplier_id").set_index("supplier_id")
35
+
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ import numpy as np
4
+ from sklearn.ensemble import RandomForestRegressor
5
+
6
+
7
+ @dataclass
8
+ class SupplierRiskModel:
9
+ model: RandomForestRegressor
10
+
11
+ @classmethod
12
+ def train_baseline(cls, X, y) -> "SupplierRiskModel":
13
+ m = RandomForestRegressor(
14
+ n_estimators=250,
15
+ random_state=42,
16
+ max_depth=7,
17
+ n_jobs=-1,
18
+ )
19
+ m.fit(X, y)
20
+ return cls(model=m)
21
+
22
+ def predict(self, X) -> np.ndarray:
23
+ return self.model.predict(X)
24
+
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+ import numpy as np
3
+ import pandas as pd
4
+ from .models import SupplierRiskModel
5
+
6
+
7
+ def baseline_labels_from_features(X: pd.DataFrame) -> np.ndarray:
8
+ # Lower is better (risk score)
9
+ return (
10
+ 0.45 * X["late_risk"].to_numpy()
11
+ + 0.35 * X["quality_risk"].to_numpy()
12
+ + 0.20 * X["esg_risk"].to_numpy()
13
+ )
14
+
15
+
16
+ def train_and_score_suppliers(X: pd.DataFrame) -> pd.DataFrame:
17
+ y = baseline_labels_from_features(X)
18
+ model = SupplierRiskModel.train_baseline(X, y)
19
+ pred = model.predict(X)
20
+
21
+ # Convert risk to 0..100 health
22
+ health = (1.0 - np.clip(pred, 0, 1)) * 100.0
23
+
24
+ out = pd.DataFrame(
25
+ {
26
+ "supplier_id": X.index.astype(str),
27
+ "risk_score": pred,
28
+ "supplier_health": health,
29
+ }
30
+ )
31
+ return out.sort_values("supplier_health", ascending=False).reset_index(drop=True)
32
+
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+ from collections import defaultdict, Counter
3
+ from dataclasses import dataclass
4
+ from typing import Iterable, Dict, List, Tuple
5
+ from datetime import timedelta, datetime
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class Event:
10
+ case_id: str
11
+ event_type: str
12
+ ts: datetime
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class VariantResult:
17
+ variant: Tuple[str, ...]
18
+ count: int
19
+
20
+
21
+ def variants(events: Iterable[Event], max_variants: int = 10) -> List[VariantResult]:
22
+ per_case: Dict[str, List[Event]] = defaultdict(list)
23
+ for e in events:
24
+ per_case[e.case_id].append(e)
25
+
26
+ seqs = []
27
+ for _, evs in per_case.items():
28
+ evs_sorted = sorted(evs, key=lambda x: x.ts)
29
+ seqs.append(tuple(e.event_type for e in evs_sorted))
30
+
31
+ c = Counter(seqs)
32
+ return [VariantResult(k, v) for k, v in c.most_common(max_variants)]
33
+
34
+
35
+ def lead_time(events: Iterable[Event], start: str, end: str) -> Dict[str, timedelta]:
36
+ per_case: Dict[str, List[Event]] = defaultdict(list)
37
+ for e in events:
38
+ per_case[e.case_id].append(e)
39
+
40
+ out: Dict[str, timedelta] = {}
41
+ for case_id, evs in per_case.items():
42
+ evs_sorted = sorted(evs, key=lambda x: x.ts)
43
+ s = next((e.ts for e in evs_sorted if e.event_type == start), None)
44
+ t = next((e.ts for e in evs_sorted if e.event_type == end), None)
45
+ if s and t and t >= s:
46
+ out[case_id] = t - s
47
+ return out
48
+
49
+
50
+ def slowest_cases(lead_times: Dict[str, timedelta], top_n: int = 10):
51
+ return sorted(lead_times.items(), key=lambda x: x[1], reverse=True)[:top_n]
52
+
File without changes
@@ -0,0 +1,14 @@
1
+ import pandas as pd
2
+ from procurelytics_ai.governance.responsible_ai import data_quality_report, representation_proxy_check
3
+
4
+ def test_governance():
5
+ df = pd.DataFrame({
6
+ "supplier_id": ["A", "B", "C", "D"],
7
+ "country": ["US", "US", "US", "US"],
8
+ "esg_score": [90, 80, None, 110],
9
+ })
10
+ dq = data_quality_report(df)
11
+ assert len(dq) >= 1
12
+ rep = representation_proxy_check(df, "country")
13
+ assert len(rep) >= 1
14
+