build(agent): molt-z#db0ec5 iteration
This commit is contained in:
parent
a2787cbe34
commit
27df9bd8a2
|
|
@ -1,9 +1,11 @@
|
||||||
# OpenBench: Privacy-Preserving Benchmarking (MVP)
|
# OpenBench: Privacy-Preserving Benchmarking (MVP)
|
||||||
|
|
||||||
This repository contains a minimal, working MVP of the OpenBench platform focused on:
|
- This repository contains a minimal, working MVP of the OpenBench platform focused on:
|
||||||
- An offline-first KPI data model (Revenue, COGS, inventory turns, lead time, CAC, LTV).
|
- An offline-first KPI data model (Revenue, COGS, inventory turns, lead time, CAC, LTV).
|
||||||
- A simple, privacy-preserving aggregation primitive (Laplace-noise-enabled) for anonymized benchmarking.
|
- A simple, privacy-preserving aggregation primitive (Laplace-noise-enabled) for anonymized benchmarking.
|
||||||
- A lightweight Python packaging setup compatible with pytest-based tests and python -m build packaging checks.
|
- A lightweight Python packaging setup compatible with pytest-based tests and python -m build packaging checks.
|
||||||
|
- A minimal data-contract system (DataContract) and an in-memory contract registry for versioned payload schemas.
|
||||||
|
- A pluggable adapter framework with sample POSAdapter and ERPAdapter to bootstrap data ingestion.
|
||||||
|
|
||||||
How to run
|
How to run
|
||||||
- Install dependencies and run tests: `bash test.sh`
|
- Install dependencies and run tests: `bash test.sh`
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,20 @@ performing privacy-preserving aggregations, and computing simple derived metrics
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .core import KPIRecord, LocalStore, SecureAggregator, GrowthCalculator
|
from .core import KPIRecord, LocalStore, SecureAggregator, GrowthCalculator
|
||||||
|
from .contracts import DataContract, ContractRegistry
|
||||||
|
from .adapters.pos import POSAdapter
|
||||||
|
from .adapters.erp import ERPAdapter
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"KPIRecord",
|
||||||
|
"LocalStore",
|
||||||
|
"SecureAggregator",
|
||||||
|
"GrowthCalculator",
|
||||||
|
"DataContract",
|
||||||
|
"ContractRegistry",
|
||||||
|
"POSAdapter",
|
||||||
|
"ERPAdapter",
|
||||||
|
]
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"KPIRecord",
|
"KPIRecord",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from openbench_privacy_preserving_benchmarkin.core import KPIRecord
|
||||||
|
from openbench_privacy_preserving_benchmarkin.contracts import DataContract
|
||||||
|
|
||||||
|
|
||||||
|
class Adapter:
|
||||||
|
"""Base adapter interface for data sources."""
|
||||||
|
|
||||||
|
def __init__(self, name: str, contract: DataContract):
|
||||||
|
self.name = name
|
||||||
|
self.contract = contract
|
||||||
|
|
||||||
|
def collect(self) -> List[KPIRecord]: # pragma: no cover - abstract in base
|
||||||
|
raise NotImplementedError
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from .base import Adapter
|
||||||
|
from openbench_privacy_preserving_benchmarkin.core import KPIRecord
|
||||||
|
from openbench_privacy_preserving_benchmarkin.contracts import DataContract
|
||||||
|
|
||||||
|
|
||||||
|
class ERPAdapter(Adapter):
|
||||||
|
"""A toy ERP adapter that yields KPIRecords."""
|
||||||
|
|
||||||
|
def __init__(self, name: str, contract: DataContract):
|
||||||
|
super().__init__(name, contract)
|
||||||
|
|
||||||
|
def collect(self) -> List[KPIRecord]:
|
||||||
|
k1 = KPIRecord(
|
||||||
|
revenue=800.0,
|
||||||
|
cogs=500.0,
|
||||||
|
inventory_turns=1.8,
|
||||||
|
lead_time=4.0,
|
||||||
|
cac=40.0,
|
||||||
|
ltv=900.0,
|
||||||
|
region="default",
|
||||||
|
industry="Manufacturing",
|
||||||
|
anon_id=f"{self.name}-erp1",
|
||||||
|
timestamp="2020-01-03T00:00:00Z",
|
||||||
|
)
|
||||||
|
return [k1]
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from .base import Adapter
|
||||||
|
from openbench_privacy_preserving_benchmarkin.core import KPIRecord
|
||||||
|
from openbench_privacy_preserving_benchmarkin.contracts import DataContract
|
||||||
|
|
||||||
|
|
||||||
|
class POSAdapter(Adapter):
|
||||||
|
"""A toy POS (Point-of-Sale) adapter that yields KPIRecords."""
|
||||||
|
|
||||||
|
def __init__(self, name: str, contract: DataContract, seed: int = 42):
|
||||||
|
super().__init__(name, contract)
|
||||||
|
self._seed = seed
|
||||||
|
|
||||||
|
def collect(self) -> List[KPIRecord]:
|
||||||
|
# For MVP: generate two deterministic KPI records as samples
|
||||||
|
# In a real implementation this would pull from a POS feed.
|
||||||
|
k1 = KPIRecord(
|
||||||
|
revenue=1000.0,
|
||||||
|
cogs=600.0,
|
||||||
|
inventory_turns=2.5,
|
||||||
|
lead_time=2.0,
|
||||||
|
cac=50.0,
|
||||||
|
ltv=1200.0,
|
||||||
|
region="default",
|
||||||
|
industry="Retail",
|
||||||
|
anon_id=f"{self.name}-sample1",
|
||||||
|
timestamp="2020-01-01T00:00:00Z",
|
||||||
|
)
|
||||||
|
k2 = KPIRecord(
|
||||||
|
revenue=1500.0,
|
||||||
|
cogs=900.0,
|
||||||
|
inventory_turns=3.0,
|
||||||
|
lead_time=3.0,
|
||||||
|
cac=60.0,
|
||||||
|
ltv=1700.0,
|
||||||
|
region="default",
|
||||||
|
industry="Retail",
|
||||||
|
anon_id=f"{self.name}-sample2",
|
||||||
|
timestamp="2020-01-02T00:00:00Z",
|
||||||
|
)
|
||||||
|
return [k1, k2]
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from .core import KPIRecord
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DataContract:
|
||||||
|
"""A minimal, versioned data contract for KPI payloads.
|
||||||
|
|
||||||
|
This is intentionally lightweight for MVP purposes. Each contract
|
||||||
|
defines a unique id, a version, a JSON schema-like payload description,
|
||||||
|
and privacy controls flags used by aggregators.
|
||||||
|
"""
|
||||||
|
|
||||||
|
contract_id: str
|
||||||
|
version: int
|
||||||
|
schema: Dict[str, Any]
|
||||||
|
privacy_flags: Dict[str, Any]
|
||||||
|
description: str = ""
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_new(schema: Dict[str, Any], privacy_flags: Dict[str, Any], description: str = "") -> DataContract:
|
||||||
|
return DataContract(
|
||||||
|
contract_id=uuid.uuid4().hex,
|
||||||
|
version=1,
|
||||||
|
schema=schema,
|
||||||
|
privacy_flags=privacy_flags,
|
||||||
|
description=description,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ContractRegistry:
|
||||||
|
"""In-memory registry for data contracts."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._contracts: Dict[str, DataContract] = {}
|
||||||
|
|
||||||
|
def register(self, contract: DataContract) -> None:
|
||||||
|
self._contracts[contract.contract_id] = contract
|
||||||
|
|
||||||
|
def get(self, contract_id: str) -> DataContract | None:
|
||||||
|
return self._contracts.get(contract_id)
|
||||||
|
|
||||||
|
def all(self) -> Dict[str, DataContract]:
|
||||||
|
return dict(self._contracts)
|
||||||
Loading…
Reference in New Issue