ubunye-engine 0.1.1__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.
- ubunye_engine-0.1.1/LICENSE +1 -0
- ubunye_engine-0.1.1/PKG-INFO +289 -0
- ubunye_engine-0.1.1/README.md +260 -0
- ubunye_engine-0.1.1/pyproject.toml +72 -0
- ubunye_engine-0.1.1/setup.cfg +4 -0
- ubunye_engine-0.1.1/tests/test_config.py +7 -0
- ubunye_engine-0.1.1/tests/test_rest_api_plugin.py +356 -0
- ubunye_engine-0.1.1/tests/test_runtime.py +6 -0
- ubunye_engine-0.1.1/ubunye/__init__.py +10 -0
- ubunye_engine-0.1.1/ubunye/backends/__init__.py +1 -0
- ubunye_engine-0.1.1/ubunye/backends/spark_backend.py +127 -0
- ubunye_engine-0.1.1/ubunye/cli/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/cli/lineage.py +304 -0
- ubunye_engine-0.1.1/ubunye/cli/main.py +444 -0
- ubunye_engine-0.1.1/ubunye/cli/models.py +301 -0
- ubunye_engine-0.1.1/ubunye/cli/test_cmd.py +220 -0
- ubunye_engine-0.1.1/ubunye/compat/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/compat/analytics_engine_shim.py +1 -0
- ubunye_engine-0.1.1/ubunye/config/__init__.py +6 -0
- ubunye_engine-0.1.1/ubunye/config/loader.py +136 -0
- ubunye_engine-0.1.1/ubunye/config/resolver.py +121 -0
- ubunye_engine-0.1.1/ubunye/config/schema.py +246 -0
- ubunye_engine-0.1.1/ubunye/core/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/core/interfaces.py +67 -0
- ubunye_engine-0.1.1/ubunye/core/runtime.py +373 -0
- ubunye_engine-0.1.1/ubunye/lineage/__init__.py +23 -0
- ubunye_engine-0.1.1/ubunye/lineage/context.py +154 -0
- ubunye_engine-0.1.1/ubunye/lineage/hasher.py +149 -0
- ubunye_engine-0.1.1/ubunye/lineage/recorder.py +175 -0
- ubunye_engine-0.1.1/ubunye/lineage/storage.py +210 -0
- ubunye_engine-0.1.1/ubunye/models/__init__.py +17 -0
- ubunye_engine-0.1.1/ubunye/models/base.py +140 -0
- ubunye_engine-0.1.1/ubunye/models/gates.py +161 -0
- ubunye_engine-0.1.1/ubunye/models/loader.py +130 -0
- ubunye_engine-0.1.1/ubunye/models/registry.py +457 -0
- ubunye_engine-0.1.1/ubunye/orchestration/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/orchestration/airflow_exporter.py +74 -0
- ubunye_engine-0.1.1/ubunye/orchestration/base.py +17 -0
- ubunye_engine-0.1.1/ubunye/orchestration/databricks_expoter.py +69 -0
- ubunye_engine-0.1.1/ubunye/plugins/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/plugins/ml/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/plugins/ml/adapters.py +35 -0
- ubunye_engine-0.1.1/ubunye/plugins/ml/base.py +231 -0
- ubunye_engine-0.1.1/ubunye/plugins/ml/pysparkml.py +47 -0
- ubunye_engine-0.1.1/ubunye/plugins/ml/sklearn.py +58 -0
- ubunye_engine-0.1.1/ubunye/plugins/readers/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/plugins/readers/hive.py +25 -0
- ubunye_engine-0.1.1/ubunye/plugins/readers/jdbc.py +101 -0
- ubunye_engine-0.1.1/ubunye/plugins/readers/rest_api.py +444 -0
- ubunye_engine-0.1.1/ubunye/plugins/readers/s3.py +43 -0
- ubunye_engine-0.1.1/ubunye/plugins/readers/unity.py +46 -0
- ubunye_engine-0.1.1/ubunye/plugins/transforms/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/plugins/transforms/model_transform.py +188 -0
- ubunye_engine-0.1.1/ubunye/plugins/transforms/noop.py +14 -0
- ubunye_engine-0.1.1/ubunye/plugins/writers/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/plugins/writers/jdbc.py +76 -0
- ubunye_engine-0.1.1/ubunye/plugins/writers/rest_api.py +244 -0
- ubunye_engine-0.1.1/ubunye/plugins/writers/s3.py +22 -0
- ubunye_engine-0.1.1/ubunye/plugins/writers/unity.py +98 -0
- ubunye_engine-0.1.1/ubunye/telemetry/__init__.py +0 -0
- ubunye_engine-0.1.1/ubunye/telemetry/events.py +131 -0
- ubunye_engine-0.1.1/ubunye/telemetry/mlflow.py +75 -0
- ubunye_engine-0.1.1/ubunye/telemetry/monitors.py +54 -0
- ubunye_engine-0.1.1/ubunye/telemetry/otel.py +106 -0
- ubunye_engine-0.1.1/ubunye/telemetry/prometheus.py +128 -0
- ubunye_engine-0.1.1/ubunye_engine.egg-info/PKG-INFO +289 -0
- ubunye_engine-0.1.1/ubunye_engine.egg-info/SOURCES.txt +69 -0
- ubunye_engine-0.1.1/ubunye_engine.egg-info/dependency_links.txt +1 -0
- ubunye_engine-0.1.1/ubunye_engine.egg-info/entry_points.txt +27 -0
- ubunye_engine-0.1.1/ubunye_engine.egg-info/requires.txt +22 -0
- ubunye_engine-0.1.1/ubunye_engine.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
MIT
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ubunye-engine
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Config-first, Spark-native ETL/ML engine with a modular plugin system
|
|
5
|
+
Author: Ubunye Team
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: pyyaml>=6
|
|
10
|
+
Requires-Dist: jinja2>=3
|
|
11
|
+
Requires-Dist: pydantic>=2
|
|
12
|
+
Requires-Dist: typer[all]>=0.12
|
|
13
|
+
Provides-Extra: spark
|
|
14
|
+
Requires-Dist: pyspark>=3.3; extra == "spark"
|
|
15
|
+
Provides-Extra: ml
|
|
16
|
+
Requires-Dist: scikit-learn; extra == "ml"
|
|
17
|
+
Requires-Dist: torch; extra == "ml"
|
|
18
|
+
Requires-Dist: mlflow; extra == "ml"
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest-cov>=4; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-timeout; extra == "dev"
|
|
23
|
+
Requires-Dist: hypothesis>=6; extra == "dev"
|
|
24
|
+
Requires-Dist: requests>=2.28; extra == "dev"
|
|
25
|
+
Requires-Dist: black; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff; extra == "dev"
|
|
27
|
+
Requires-Dist: build; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
<p align="center">
|
|
31
|
+
<img src="docs/assets/ubunye-logo-white-v2.png" alt="Ubunye Engine" width="400"/>
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<em>Ubunye (oo-BOON-yeh) β isiZulu for "unity"</em>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<h3 align="center">One framework. Every pipeline. Any environment.</h3>
|
|
39
|
+
|
|
40
|
+
<p align="center">
|
|
41
|
+
<a href="https://ubunye-ai-ecosystems.github.io/ubunye_engine">Docs</a> β’
|
|
42
|
+
<a href="#quickstart">Quickstart</a> β’
|
|
43
|
+
<a href="#why-ubunye">Why Ubunye</a> β’
|
|
44
|
+
<a href="https://github.com/ubunye-ai-ecosystems/ubunye_engine/discussions">Community</a>
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Hey there π
|
|
50
|
+
|
|
51
|
+
If you've ever spent more time wiring up Spark boilerplate than writing actual data logic β this is for you.
|
|
52
|
+
|
|
53
|
+
**Ubunye Engine** lets you define your entire data pipeline in a simple YAML config and a Python file. No Spark session setup. No connection management. No environment-specific scripts. Just tell the engine *what* you want, and it handles the *how*.
|
|
54
|
+
|
|
55
|
+
It works on your laptop, on YARN, on Kubernetes, on Databricks β same config, same code, everywhere.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Quickstart
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install ubunye-engine
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Scaffold your first pipeline:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
ubunye init -d ./pipelines -u fraud_detection -p ingestion -t claim_etl
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
You'll get:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
pipelines/
|
|
75
|
+
fraud_detection/
|
|
76
|
+
ingestion/
|
|
77
|
+
claim_etl/
|
|
78
|
+
config.yaml β tell the engine what to do
|
|
79
|
+
transformations.py β your logic goes here
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Open `config.yaml` and describe your pipeline:
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
MODEL: "etl"
|
|
86
|
+
VERSION: "0.1.0"
|
|
87
|
+
|
|
88
|
+
ENGINE:
|
|
89
|
+
profiles:
|
|
90
|
+
dev:
|
|
91
|
+
spark_conf:
|
|
92
|
+
spark.master: "local[*]"
|
|
93
|
+
prod:
|
|
94
|
+
spark_conf:
|
|
95
|
+
spark.master: "yarn"
|
|
96
|
+
|
|
97
|
+
CONFIG:
|
|
98
|
+
inputs:
|
|
99
|
+
raw_claims:
|
|
100
|
+
format: hive
|
|
101
|
+
db_name: fraud_db
|
|
102
|
+
tbl_name: raw_claims
|
|
103
|
+
|
|
104
|
+
transform:
|
|
105
|
+
type: noop
|
|
106
|
+
|
|
107
|
+
outputs:
|
|
108
|
+
bronze:
|
|
109
|
+
format: delta
|
|
110
|
+
table: main.fraud.bronze_claims
|
|
111
|
+
mode: overwrite
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Add your logic in `transformations.py`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
def transform(df):
|
|
118
|
+
return df.filter("claim_amount > 0").dropDuplicates(["claim_id"])
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Run it:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
ubunye run -d ./pipelines -u fraud_detection -p ingestion -t claim_etl --profile dev
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
That's it. You just built and ran a pipeline. Same config runs in production β just swap `--profile prod`.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Why Ubunye
|
|
132
|
+
|
|
133
|
+
We've all been there. You join a new team, open the repo, and find five Spark projects β each structured differently, each with its own way of handling configs, credentials, and deployment. One uses a JSON file, another has everything hardcoded, a third has a 300-line bash script that "Dave wrote and it just works."
|
|
134
|
+
|
|
135
|
+
Ubunye says: **let's agree on how pipelines look.** One folder structure. One config format. One CLI. Whether you're building an ETL job, a feature pipeline, or an ML training run.
|
|
136
|
+
|
|
137
|
+
| Without Ubunye | With Ubunye |
|
|
138
|
+
|---|---|
|
|
139
|
+
| Every project looks different | One standard: `use_case / pipeline / task` |
|
|
140
|
+
| Spark setup scattered everywhere | Engine handles it from YAML config |
|
|
141
|
+
| Credentials hardcoded or inconsistent | `{{ env.DB_PASSWORD }}` everywhere |
|
|
142
|
+
| "Works on my machine" | Same config runs local, YARN, K8s, Databricks |
|
|
143
|
+
| New teammate needs a week to onboard | `ubunye init` and they're running in minutes |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## How It Works
|
|
148
|
+
|
|
149
|
+
Three simple ideas:
|
|
150
|
+
|
|
151
|
+
**Config over code.** Your pipeline is a YAML file. Inputs, outputs, Spark settings, orchestration β all declared, not coded.
|
|
152
|
+
|
|
153
|
+
**Plugins for everything.** The `format` field in your config selects a connector β `hive`, `jdbc`, `delta`, `s3`, `unity`, `rest_api`, and more. Need a new data source? Add a plugin.
|
|
154
|
+
|
|
155
|
+
**Folders as architecture.** Pipelines are organized as `project / use_case / pipeline / task`. The CLI uses this for scaffolding, execution, and discovery:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
pipelines/
|
|
159
|
+
fraud_detection/
|
|
160
|
+
ingestion/
|
|
161
|
+
claim_etl/
|
|
162
|
+
policy_etl/
|
|
163
|
+
feature_engineering/
|
|
164
|
+
claim_features/
|
|
165
|
+
risk_scoring/
|
|
166
|
+
train_model/
|
|
167
|
+
score_claims/
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## What Can You Build With It
|
|
173
|
+
|
|
174
|
+
**ETL pipelines** β move data between Hive, JDBC databases, Delta Lake, S3, REST APIs. Config-driven, scheduled, reproducible.
|
|
175
|
+
|
|
176
|
+
**ML training and inference** β define your model behind a simple contract, let the engine handle versioning, storage, and deployment.
|
|
177
|
+
|
|
178
|
+
**RAG document pipelines** β ingest documents, extract text, chunk, compute embeddings, load into a vector store. All from YAML.
|
|
179
|
+
|
|
180
|
+
**Feature engineering** β compute features once, write to a shared table, reuse across use cases.
|
|
181
|
+
|
|
182
|
+
**Data drift detection** β monitor feature distributions between runs, flag when things shift.
|
|
183
|
+
|
|
184
|
+
Check out the [Patterns](https://ubunye-ai-ecosystems.github.io/ubunye_engine) section in our docs for full examples.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Connectors
|
|
189
|
+
|
|
190
|
+
| Format | Read | Write | Description |
|
|
191
|
+
|---|:---:|:---:|---|
|
|
192
|
+
| `hive` | β | β | Apache Hive tables |
|
|
193
|
+
| `jdbc` | β | β | PostgreSQL, MySQL, Teradata, and more |
|
|
194
|
+
| `delta` | β | β | Delta Lake (standalone or Unity Catalog) |
|
|
195
|
+
| `s3` | β | β | S3, HDFS, or local filesystem |
|
|
196
|
+
| `unity` | β | β | Databricks Unity Catalog |
|
|
197
|
+
| `binary` | β | | Binary files (images, PDFs) |
|
|
198
|
+
| `rest_api` | β | β | REST APIs with pagination and auth |
|
|
199
|
+
|
|
200
|
+
Want to add one? See the [plugin guide](https://ubunye-ai-ecosystems.github.io/ubunye_engine).
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Run Anywhere
|
|
205
|
+
|
|
206
|
+
Same pipeline, no changes:
|
|
207
|
+
|
|
208
|
+
| Environment | Just set |
|
|
209
|
+
|---|---|
|
|
210
|
+
| Local | `spark.master: "local[*]"` |
|
|
211
|
+
| YARN / Hadoop | `spark.master: "yarn"` |
|
|
212
|
+
| Kubernetes | `spark.master: "k8s://..."` |
|
|
213
|
+
| Databricks | Via ORCHESTRATION config |
|
|
214
|
+
| AWS EMR | Via EMR Steps |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Jinja Templating
|
|
219
|
+
|
|
220
|
+
All config values support Jinja2:
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
# Environment variables
|
|
224
|
+
password: "{{ env.DB_PASSWORD }}"
|
|
225
|
+
|
|
226
|
+
# CLI variables (--var ds=2025-01-01)
|
|
227
|
+
path: "s3a://bucket/{{ ds }}/"
|
|
228
|
+
|
|
229
|
+
# Defaults
|
|
230
|
+
path: "s3a://bucket/{{ ds | default('2025-01-01') }}/"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## CLI
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
ubunye init -d ./pipelines -u <use_case> -p <pipeline> -t <task> # scaffold
|
|
239
|
+
ubunye run -d ./pipelines -u <use_case> -p <pipeline> -t <task> # execute
|
|
240
|
+
ubunye validate -d ./pipelines -u <use_case> -p <pipeline> -t <task> # check config
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## What Ubunye Is Not
|
|
246
|
+
|
|
247
|
+
It's not an agent framework β use LangChain or CrewAI for that.
|
|
248
|
+
It's not an orchestrator β use Airflow, Prefect, or Dagster.
|
|
249
|
+
It's not a compute engine β it runs on Spark.
|
|
250
|
+
|
|
251
|
+
Ubunye is the **standardization layer** between your data sources and your applications. It makes the plumbing boring so you can focus on what matters.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Roadmap
|
|
256
|
+
|
|
257
|
+
- [x] Config-driven ETL pipelines
|
|
258
|
+
- [x] Multi-environment profiles
|
|
259
|
+
- [x] Jinja templating
|
|
260
|
+
- [x] Plugin-based connectors
|
|
261
|
+
- [x] CLI scaffolding and execution
|
|
262
|
+
- [ ] Pydantic config validation
|
|
263
|
+
- [ ] ML model contract
|
|
264
|
+
- [ ] Model registry with versioning
|
|
265
|
+
- [ ] Data drift detection
|
|
266
|
+
- [ ] Lineage tracking
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Get Involved
|
|
271
|
+
|
|
272
|
+
We'd love your help. Whether it's a new connector, a bug fix, a typo, or just telling us what you're building β all contributions matter.
|
|
273
|
+
|
|
274
|
+
- π [Report a bug](https://github.com/ubunye-ai-ecosystems/ubunye_engine/issues)
|
|
275
|
+
- π‘ [Request a feature](https://github.com/ubunye-ai-ecosystems/ubunye_engine/discussions)
|
|
276
|
+
- π [Read the contributing guide](CONTRIBUTING.md)
|
|
277
|
+
- β Star the repo if you find it useful β it helps more than you'd think
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
[MIT License](LICENSE)
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
<p align="center">
|
|
288
|
+
Built with πΏπ¦ by <a href="https://github.com/ubunye-ai-ecosystems">Ubunye AI Ecosystems</a>
|
|
289
|
+
</p>
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/ubunye-logo-white-v2.png" alt="Ubunye Engine" width="400"/>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<em>Ubunye (oo-BOON-yeh) β isiZulu for "unity"</em>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<h3 align="center">One framework. Every pipeline. Any environment.</h3>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://ubunye-ai-ecosystems.github.io/ubunye_engine">Docs</a> β’
|
|
13
|
+
<a href="#quickstart">Quickstart</a> β’
|
|
14
|
+
<a href="#why-ubunye">Why Ubunye</a> β’
|
|
15
|
+
<a href="https://github.com/ubunye-ai-ecosystems/ubunye_engine/discussions">Community</a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Hey there π
|
|
21
|
+
|
|
22
|
+
If you've ever spent more time wiring up Spark boilerplate than writing actual data logic β this is for you.
|
|
23
|
+
|
|
24
|
+
**Ubunye Engine** lets you define your entire data pipeline in a simple YAML config and a Python file. No Spark session setup. No connection management. No environment-specific scripts. Just tell the engine *what* you want, and it handles the *how*.
|
|
25
|
+
|
|
26
|
+
It works on your laptop, on YARN, on Kubernetes, on Databricks β same config, same code, everywhere.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quickstart
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install ubunye-engine
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Scaffold your first pipeline:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
ubunye init -d ./pipelines -u fraud_detection -p ingestion -t claim_etl
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
You'll get:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
pipelines/
|
|
46
|
+
fraud_detection/
|
|
47
|
+
ingestion/
|
|
48
|
+
claim_etl/
|
|
49
|
+
config.yaml β tell the engine what to do
|
|
50
|
+
transformations.py β your logic goes here
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Open `config.yaml` and describe your pipeline:
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
MODEL: "etl"
|
|
57
|
+
VERSION: "0.1.0"
|
|
58
|
+
|
|
59
|
+
ENGINE:
|
|
60
|
+
profiles:
|
|
61
|
+
dev:
|
|
62
|
+
spark_conf:
|
|
63
|
+
spark.master: "local[*]"
|
|
64
|
+
prod:
|
|
65
|
+
spark_conf:
|
|
66
|
+
spark.master: "yarn"
|
|
67
|
+
|
|
68
|
+
CONFIG:
|
|
69
|
+
inputs:
|
|
70
|
+
raw_claims:
|
|
71
|
+
format: hive
|
|
72
|
+
db_name: fraud_db
|
|
73
|
+
tbl_name: raw_claims
|
|
74
|
+
|
|
75
|
+
transform:
|
|
76
|
+
type: noop
|
|
77
|
+
|
|
78
|
+
outputs:
|
|
79
|
+
bronze:
|
|
80
|
+
format: delta
|
|
81
|
+
table: main.fraud.bronze_claims
|
|
82
|
+
mode: overwrite
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Add your logic in `transformations.py`:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
def transform(df):
|
|
89
|
+
return df.filter("claim_amount > 0").dropDuplicates(["claim_id"])
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Run it:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
ubunye run -d ./pipelines -u fraud_detection -p ingestion -t claim_etl --profile dev
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
That's it. You just built and ran a pipeline. Same config runs in production β just swap `--profile prod`.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Why Ubunye
|
|
103
|
+
|
|
104
|
+
We've all been there. You join a new team, open the repo, and find five Spark projects β each structured differently, each with its own way of handling configs, credentials, and deployment. One uses a JSON file, another has everything hardcoded, a third has a 300-line bash script that "Dave wrote and it just works."
|
|
105
|
+
|
|
106
|
+
Ubunye says: **let's agree on how pipelines look.** One folder structure. One config format. One CLI. Whether you're building an ETL job, a feature pipeline, or an ML training run.
|
|
107
|
+
|
|
108
|
+
| Without Ubunye | With Ubunye |
|
|
109
|
+
|---|---|
|
|
110
|
+
| Every project looks different | One standard: `use_case / pipeline / task` |
|
|
111
|
+
| Spark setup scattered everywhere | Engine handles it from YAML config |
|
|
112
|
+
| Credentials hardcoded or inconsistent | `{{ env.DB_PASSWORD }}` everywhere |
|
|
113
|
+
| "Works on my machine" | Same config runs local, YARN, K8s, Databricks |
|
|
114
|
+
| New teammate needs a week to onboard | `ubunye init` and they're running in minutes |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## How It Works
|
|
119
|
+
|
|
120
|
+
Three simple ideas:
|
|
121
|
+
|
|
122
|
+
**Config over code.** Your pipeline is a YAML file. Inputs, outputs, Spark settings, orchestration β all declared, not coded.
|
|
123
|
+
|
|
124
|
+
**Plugins for everything.** The `format` field in your config selects a connector β `hive`, `jdbc`, `delta`, `s3`, `unity`, `rest_api`, and more. Need a new data source? Add a plugin.
|
|
125
|
+
|
|
126
|
+
**Folders as architecture.** Pipelines are organized as `project / use_case / pipeline / task`. The CLI uses this for scaffolding, execution, and discovery:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
pipelines/
|
|
130
|
+
fraud_detection/
|
|
131
|
+
ingestion/
|
|
132
|
+
claim_etl/
|
|
133
|
+
policy_etl/
|
|
134
|
+
feature_engineering/
|
|
135
|
+
claim_features/
|
|
136
|
+
risk_scoring/
|
|
137
|
+
train_model/
|
|
138
|
+
score_claims/
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## What Can You Build With It
|
|
144
|
+
|
|
145
|
+
**ETL pipelines** β move data between Hive, JDBC databases, Delta Lake, S3, REST APIs. Config-driven, scheduled, reproducible.
|
|
146
|
+
|
|
147
|
+
**ML training and inference** β define your model behind a simple contract, let the engine handle versioning, storage, and deployment.
|
|
148
|
+
|
|
149
|
+
**RAG document pipelines** β ingest documents, extract text, chunk, compute embeddings, load into a vector store. All from YAML.
|
|
150
|
+
|
|
151
|
+
**Feature engineering** β compute features once, write to a shared table, reuse across use cases.
|
|
152
|
+
|
|
153
|
+
**Data drift detection** β monitor feature distributions between runs, flag when things shift.
|
|
154
|
+
|
|
155
|
+
Check out the [Patterns](https://ubunye-ai-ecosystems.github.io/ubunye_engine) section in our docs for full examples.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Connectors
|
|
160
|
+
|
|
161
|
+
| Format | Read | Write | Description |
|
|
162
|
+
|---|:---:|:---:|---|
|
|
163
|
+
| `hive` | β | β | Apache Hive tables |
|
|
164
|
+
| `jdbc` | β | β | PostgreSQL, MySQL, Teradata, and more |
|
|
165
|
+
| `delta` | β | β | Delta Lake (standalone or Unity Catalog) |
|
|
166
|
+
| `s3` | β | β | S3, HDFS, or local filesystem |
|
|
167
|
+
| `unity` | β | β | Databricks Unity Catalog |
|
|
168
|
+
| `binary` | β | | Binary files (images, PDFs) |
|
|
169
|
+
| `rest_api` | β | β | REST APIs with pagination and auth |
|
|
170
|
+
|
|
171
|
+
Want to add one? See the [plugin guide](https://ubunye-ai-ecosystems.github.io/ubunye_engine).
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Run Anywhere
|
|
176
|
+
|
|
177
|
+
Same pipeline, no changes:
|
|
178
|
+
|
|
179
|
+
| Environment | Just set |
|
|
180
|
+
|---|---|
|
|
181
|
+
| Local | `spark.master: "local[*]"` |
|
|
182
|
+
| YARN / Hadoop | `spark.master: "yarn"` |
|
|
183
|
+
| Kubernetes | `spark.master: "k8s://..."` |
|
|
184
|
+
| Databricks | Via ORCHESTRATION config |
|
|
185
|
+
| AWS EMR | Via EMR Steps |
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Jinja Templating
|
|
190
|
+
|
|
191
|
+
All config values support Jinja2:
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
# Environment variables
|
|
195
|
+
password: "{{ env.DB_PASSWORD }}"
|
|
196
|
+
|
|
197
|
+
# CLI variables (--var ds=2025-01-01)
|
|
198
|
+
path: "s3a://bucket/{{ ds }}/"
|
|
199
|
+
|
|
200
|
+
# Defaults
|
|
201
|
+
path: "s3a://bucket/{{ ds | default('2025-01-01') }}/"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## CLI
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
ubunye init -d ./pipelines -u <use_case> -p <pipeline> -t <task> # scaffold
|
|
210
|
+
ubunye run -d ./pipelines -u <use_case> -p <pipeline> -t <task> # execute
|
|
211
|
+
ubunye validate -d ./pipelines -u <use_case> -p <pipeline> -t <task> # check config
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## What Ubunye Is Not
|
|
217
|
+
|
|
218
|
+
It's not an agent framework β use LangChain or CrewAI for that.
|
|
219
|
+
It's not an orchestrator β use Airflow, Prefect, or Dagster.
|
|
220
|
+
It's not a compute engine β it runs on Spark.
|
|
221
|
+
|
|
222
|
+
Ubunye is the **standardization layer** between your data sources and your applications. It makes the plumbing boring so you can focus on what matters.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Roadmap
|
|
227
|
+
|
|
228
|
+
- [x] Config-driven ETL pipelines
|
|
229
|
+
- [x] Multi-environment profiles
|
|
230
|
+
- [x] Jinja templating
|
|
231
|
+
- [x] Plugin-based connectors
|
|
232
|
+
- [x] CLI scaffolding and execution
|
|
233
|
+
- [ ] Pydantic config validation
|
|
234
|
+
- [ ] ML model contract
|
|
235
|
+
- [ ] Model registry with versioning
|
|
236
|
+
- [ ] Data drift detection
|
|
237
|
+
- [ ] Lineage tracking
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Get Involved
|
|
242
|
+
|
|
243
|
+
We'd love your help. Whether it's a new connector, a bug fix, a typo, or just telling us what you're building β all contributions matter.
|
|
244
|
+
|
|
245
|
+
- π [Report a bug](https://github.com/ubunye-ai-ecosystems/ubunye_engine/issues)
|
|
246
|
+
- π‘ [Request a feature](https://github.com/ubunye-ai-ecosystems/ubunye_engine/discussions)
|
|
247
|
+
- π [Read the contributing guide](CONTRIBUTING.md)
|
|
248
|
+
- β Star the repo if you find it useful β it helps more than you'd think
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
[MIT License](LICENSE)
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
<p align="center">
|
|
259
|
+
Built with πΏπ¦ by <a href="https://github.com/ubunye-ai-ecosystems">Ubunye AI Ecosystems</a>
|
|
260
|
+
</p>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ubunye-engine"
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
description = "Config-first, Spark-native ETL/ML engine with a modular plugin system"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
authors = [{name="Ubunye Team"}]
|
|
12
|
+
dependencies = [
|
|
13
|
+
"pyyaml>=6",
|
|
14
|
+
"jinja2>=3",
|
|
15
|
+
"pydantic>=2",
|
|
16
|
+
"typer[all]>=0.12",
|
|
17
|
+
# pyspark is large; declare as optional so users can choose version
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
spark = ["pyspark>=3.3"]
|
|
22
|
+
ml = ["scikit-learn", "torch", "mlflow"]
|
|
23
|
+
dev = ["pytest", "pytest-cov>=4", "pytest-timeout", "hypothesis>=6", "requests>=2.28", "black", "ruff", "build"]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
ubunye = "ubunye.cli.main:app"
|
|
27
|
+
|
|
28
|
+
[project.entry-points."ubunye.readers"]
|
|
29
|
+
hive = "ubunye.plugins.readers.hive:HiveReader"
|
|
30
|
+
jdbc = "ubunye.plugins.readers.jdbc:JdbcReader"
|
|
31
|
+
s3 = "ubunye.plugins.readers.s3:S3Reader"
|
|
32
|
+
unity = "ubunye.plugins.readers.unity:UnityTableReader"
|
|
33
|
+
rest_api = "ubunye.plugins.readers.rest_api:RestApiReader"
|
|
34
|
+
|
|
35
|
+
[project.entry-points."ubunye.writers"]
|
|
36
|
+
s3 = "ubunye.plugins.writers.s3:S3Writer"
|
|
37
|
+
jdbc = "ubunye.plugins.writers.jdbc:JdbcWriter"
|
|
38
|
+
unity = "ubunye.plugins.writers.unity:UnityTableWriter"
|
|
39
|
+
rest_api = "ubunye.plugins.writers.rest_api:RestApiWriter"
|
|
40
|
+
|
|
41
|
+
[project.entry-points."ubunye.transforms"]
|
|
42
|
+
noop = "ubunye.plugins.transforms.noop:NoOpTransform"
|
|
43
|
+
model = "ubunye.plugins.transforms.model_transform:ModelTransform"
|
|
44
|
+
|
|
45
|
+
[project.entry-points."ubunye.ml"]
|
|
46
|
+
sklearn = "ubunye.plugins.ml.sklearn:SklearnModel"
|
|
47
|
+
sparkml = "ubunye.plugins.ml.pysparkml:SparkMLModel"
|
|
48
|
+
|
|
49
|
+
[project.entry-points."ubunye.monitors"]
|
|
50
|
+
mlflow = "ubunye.telemetry.mlflow:MLflowMonitor"
|
|
51
|
+
lineage = "ubunye.lineage.recorder:LineageRecorder"
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.packages.find]
|
|
54
|
+
include = ["ubunye*"]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
markers = [
|
|
59
|
+
"integration: requires a local Spark session and Java 11",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.black]
|
|
63
|
+
line-length = 100
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
line-length = 100
|
|
67
|
+
|
|
68
|
+
[tool.ruff.lint]
|
|
69
|
+
select = ["E", "F", "I"]
|
|
70
|
+
ignore = [
|
|
71
|
+
"E501", # line too long β handled by Black; long help strings in CLI are acceptable
|
|
72
|
+
]
|