metergraph-core 0.1.4__tar.gz → 0.2.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.
- {metergraph_core-0.1.4/src/metergraph_core.egg-info → metergraph_core-0.2.0}/PKG-INFO +37 -4
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/README.md +35 -2
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/pyproject.toml +2 -2
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/src/metergraph_core/__init__.py +10 -0
- metergraph_core-0.2.0/src/metergraph_core/billing.py +135 -0
- {metergraph_core-0.1.4 → metergraph_core-0.2.0/src/metergraph_core.egg-info}/PKG-INFO +37 -4
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/src/metergraph_core.egg-info/SOURCES.txt +1 -0
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/MANIFEST.in +0 -0
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/setup.cfg +0 -0
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/src/metergraph_core/catalog.py +0 -0
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/src/metergraph_core/data/prices.yaml +0 -0
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/src/metergraph_core/loader.py +0 -0
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/src/metergraph_core.egg-info/dependency_links.txt +0 -0
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/src/metergraph_core.egg-info/requires.txt +0 -0
- {metergraph_core-0.1.4 → metergraph_core-0.2.0}/src/metergraph_core.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metergraph-core
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Reusable MeterGraph catalog and deterministic
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Reusable MeterGraph catalog and deterministic billing engine
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -12,14 +12,15 @@ Requires-Dist: pytest>=8; extra == "dev"
|
|
|
12
12
|
|
|
13
13
|
# metergraph-core
|
|
14
14
|
|
|
15
|
-
Reusable catalog and deterministic
|
|
15
|
+
Reusable catalog and deterministic billing engine for MeterGraph.
|
|
16
16
|
|
|
17
17
|
`metergraph-core` owns the public effective-dated model catalog and the pricing
|
|
18
18
|
logic shared across MeterGraph systems: catalog parsing and validation, provider
|
|
19
19
|
and model alias resolution, channel and region selection, input/output/cache/
|
|
20
20
|
batch/long-context pricing rules, deterministic cost calculation with reason
|
|
21
21
|
codes, stable logical price identifiers, and catalog version and content-hash
|
|
22
|
-
reporting.
|
|
22
|
+
reporting. It also owns the pure decision that selects a qualified
|
|
23
|
+
gateway-reported charge or a catalog estimate as the effective call cost.
|
|
23
24
|
|
|
24
25
|
It does not own HTTP routes, database access, migrations, authentication,
|
|
25
26
|
tenancy, ingest, dashboard code, or any hosted-only concern, and it never reads
|
|
@@ -83,19 +84,51 @@ exists. It never substitutes a direct-provider price for a gateway price.
|
|
|
83
84
|
`LoadedCatalog.pricing_verified_at` records when the bundled catalog was last
|
|
84
85
|
checked against its linked provider sources.
|
|
85
86
|
|
|
87
|
+
## Billing evidence
|
|
88
|
+
|
|
89
|
+
Servers can pass content-blind, already-extracted gateway fields through the
|
|
90
|
+
shared trust boundary and combine them with a catalog result:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from metergraph_core import normalize_gateway_evidence, resolve_billing
|
|
94
|
+
|
|
95
|
+
evidence = normalize_gateway_evidence({
|
|
96
|
+
"gateway": "openrouter",
|
|
97
|
+
"endpoint": "chat.completions",
|
|
98
|
+
"reported_cost_usd": "0.00482",
|
|
99
|
+
"reported_cost_source": "openrouter.usage.cost",
|
|
100
|
+
})
|
|
101
|
+
decision = resolve_billing(result, evidence)
|
|
102
|
+
print(decision.cost_usd, decision.cost_provenance)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The initial qualified contract is OpenRouter Chat Completions. A finite,
|
|
106
|
+
non-negative `openrouter.usage.cost` value, including zero, takes precedence
|
|
107
|
+
over a catalog estimate. The decision retains both values and never adds the
|
|
108
|
+
separately reported upstream inference cost to the OpenRouter account charge.
|
|
109
|
+
Unsupported or malformed evidence falls back to the catalog result.
|
|
110
|
+
|
|
111
|
+
The billing module validates only gateway, endpoint, fixed source names, and
|
|
112
|
+
decimal cost values. It does not inspect provider response content or own SDK
|
|
113
|
+
capture, HTTP, timestamps, trace context, persistence, or tenant behavior.
|
|
114
|
+
|
|
86
115
|
## Public API
|
|
87
116
|
|
|
88
117
|
```python
|
|
89
118
|
from metergraph_core import (
|
|
90
119
|
Alias,
|
|
120
|
+
BillingDecision,
|
|
91
121
|
CatalogError,
|
|
92
122
|
CatalogSnapshot,
|
|
93
123
|
CostResult,
|
|
124
|
+
GatewayBillingEvidence,
|
|
94
125
|
LoadedCatalog,
|
|
95
126
|
Price,
|
|
96
127
|
ResolvedPrice,
|
|
97
128
|
load_catalog,
|
|
129
|
+
normalize_gateway_evidence,
|
|
98
130
|
parse_catalog,
|
|
131
|
+
resolve_billing,
|
|
99
132
|
)
|
|
100
133
|
```
|
|
101
134
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# metergraph-core
|
|
2
2
|
|
|
3
|
-
Reusable catalog and deterministic
|
|
3
|
+
Reusable catalog and deterministic billing engine for MeterGraph.
|
|
4
4
|
|
|
5
5
|
`metergraph-core` owns the public effective-dated model catalog and the pricing
|
|
6
6
|
logic shared across MeterGraph systems: catalog parsing and validation, provider
|
|
7
7
|
and model alias resolution, channel and region selection, input/output/cache/
|
|
8
8
|
batch/long-context pricing rules, deterministic cost calculation with reason
|
|
9
9
|
codes, stable logical price identifiers, and catalog version and content-hash
|
|
10
|
-
reporting.
|
|
10
|
+
reporting. It also owns the pure decision that selects a qualified
|
|
11
|
+
gateway-reported charge or a catalog estimate as the effective call cost.
|
|
11
12
|
|
|
12
13
|
It does not own HTTP routes, database access, migrations, authentication,
|
|
13
14
|
tenancy, ingest, dashboard code, or any hosted-only concern, and it never reads
|
|
@@ -71,19 +72,51 @@ exists. It never substitutes a direct-provider price for a gateway price.
|
|
|
71
72
|
`LoadedCatalog.pricing_verified_at` records when the bundled catalog was last
|
|
72
73
|
checked against its linked provider sources.
|
|
73
74
|
|
|
75
|
+
## Billing evidence
|
|
76
|
+
|
|
77
|
+
Servers can pass content-blind, already-extracted gateway fields through the
|
|
78
|
+
shared trust boundary and combine them with a catalog result:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from metergraph_core import normalize_gateway_evidence, resolve_billing
|
|
82
|
+
|
|
83
|
+
evidence = normalize_gateway_evidence({
|
|
84
|
+
"gateway": "openrouter",
|
|
85
|
+
"endpoint": "chat.completions",
|
|
86
|
+
"reported_cost_usd": "0.00482",
|
|
87
|
+
"reported_cost_source": "openrouter.usage.cost",
|
|
88
|
+
})
|
|
89
|
+
decision = resolve_billing(result, evidence)
|
|
90
|
+
print(decision.cost_usd, decision.cost_provenance)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The initial qualified contract is OpenRouter Chat Completions. A finite,
|
|
94
|
+
non-negative `openrouter.usage.cost` value, including zero, takes precedence
|
|
95
|
+
over a catalog estimate. The decision retains both values and never adds the
|
|
96
|
+
separately reported upstream inference cost to the OpenRouter account charge.
|
|
97
|
+
Unsupported or malformed evidence falls back to the catalog result.
|
|
98
|
+
|
|
99
|
+
The billing module validates only gateway, endpoint, fixed source names, and
|
|
100
|
+
decimal cost values. It does not inspect provider response content or own SDK
|
|
101
|
+
capture, HTTP, timestamps, trace context, persistence, or tenant behavior.
|
|
102
|
+
|
|
74
103
|
## Public API
|
|
75
104
|
|
|
76
105
|
```python
|
|
77
106
|
from metergraph_core import (
|
|
78
107
|
Alias,
|
|
108
|
+
BillingDecision,
|
|
79
109
|
CatalogError,
|
|
80
110
|
CatalogSnapshot,
|
|
81
111
|
CostResult,
|
|
112
|
+
GatewayBillingEvidence,
|
|
82
113
|
LoadedCatalog,
|
|
83
114
|
Price,
|
|
84
115
|
ResolvedPrice,
|
|
85
116
|
load_catalog,
|
|
117
|
+
normalize_gateway_evidence,
|
|
86
118
|
parse_catalog,
|
|
119
|
+
resolve_billing,
|
|
87
120
|
)
|
|
88
121
|
```
|
|
89
122
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "metergraph-core"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "Reusable MeterGraph catalog and deterministic
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Reusable MeterGraph catalog and deterministic billing engine"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
7
7
|
requires-python = ">=3.10"
|
|
@@ -8,6 +8,12 @@ from .catalog import (
|
|
|
8
8
|
ResolvedPrice,
|
|
9
9
|
direct_channel_for_provider,
|
|
10
10
|
)
|
|
11
|
+
from .billing import (
|
|
12
|
+
BillingDecision,
|
|
13
|
+
GatewayBillingEvidence,
|
|
14
|
+
normalize_gateway_evidence,
|
|
15
|
+
resolve_billing,
|
|
16
|
+
)
|
|
11
17
|
from .loader import (
|
|
12
18
|
CatalogError,
|
|
13
19
|
LoadedCatalog,
|
|
@@ -17,13 +23,17 @@ from .loader import (
|
|
|
17
23
|
|
|
18
24
|
__all__ = [
|
|
19
25
|
"Alias",
|
|
26
|
+
"BillingDecision",
|
|
20
27
|
"CatalogError",
|
|
21
28
|
"CatalogSnapshot",
|
|
22
29
|
"CostResult",
|
|
23
30
|
"LoadedCatalog",
|
|
31
|
+
"GatewayBillingEvidence",
|
|
24
32
|
"Price",
|
|
25
33
|
"ResolvedPrice",
|
|
26
34
|
"direct_channel_for_provider",
|
|
27
35
|
"load_catalog",
|
|
36
|
+
"normalize_gateway_evidence",
|
|
28
37
|
"parse_catalog",
|
|
38
|
+
"resolve_billing",
|
|
29
39
|
]
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Pure selection of effective LLM cost from catalog and gateway evidence."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from decimal import Decimal, InvalidOperation
|
|
5
|
+
from typing import Any, Mapping
|
|
6
|
+
|
|
7
|
+
from .catalog import CostResult
|
|
8
|
+
|
|
9
|
+
_OPENROUTER = "openrouter"
|
|
10
|
+
_CHAT_COMPLETIONS = "chat.completions"
|
|
11
|
+
_OPENROUTER_COST_SOURCE = "openrouter.usage.cost"
|
|
12
|
+
_OPENROUTER_UPSTREAM_COST_SOURCE = (
|
|
13
|
+
"openrouter.usage.cost_details.upstream_inference_cost"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class GatewayBillingEvidence:
|
|
19
|
+
"""Validated gateway billing fields from one content-blind call row."""
|
|
20
|
+
|
|
21
|
+
gateway: str | None
|
|
22
|
+
endpoint: str | None
|
|
23
|
+
reported_cost_usd: Decimal | None
|
|
24
|
+
reported_cost_source: str | None
|
|
25
|
+
reported_upstream_cost_usd: Decimal | None
|
|
26
|
+
reported_upstream_cost_source: str | None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, slots=True)
|
|
30
|
+
class BillingDecision:
|
|
31
|
+
"""Effective cost plus the independent evidence used to select it."""
|
|
32
|
+
|
|
33
|
+
cost_usd: Decimal | None
|
|
34
|
+
cost_status: str
|
|
35
|
+
cost_provenance: str
|
|
36
|
+
reported_cost_usd: Decimal | None
|
|
37
|
+
reported_upstream_cost_usd: Decimal | None
|
|
38
|
+
catalog_cost_usd: Decimal | None
|
|
39
|
+
catalog_price_id: str | None
|
|
40
|
+
catalog_reasons: tuple[str, ...]
|
|
41
|
+
cost_discrepancy_status: str | None = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _text(value: Any, *, limit: int = 128) -> str | None:
|
|
45
|
+
if not isinstance(value, str):
|
|
46
|
+
return None
|
|
47
|
+
stripped = value.strip()
|
|
48
|
+
return stripped[:limit] if stripped else None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _non_negative_decimal(value: Any) -> Decimal | None:
|
|
52
|
+
if value is None or isinstance(value, bool):
|
|
53
|
+
return None
|
|
54
|
+
try:
|
|
55
|
+
result = Decimal(str(value))
|
|
56
|
+
except (InvalidOperation, ValueError, TypeError):
|
|
57
|
+
return None
|
|
58
|
+
if not result.is_finite() or result < 0:
|
|
59
|
+
return None
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def normalize_gateway_evidence(row: Mapping[str, Any]) -> GatewayBillingEvidence:
|
|
64
|
+
"""Allowlist and validate gateway billing evidence from a call mapping."""
|
|
65
|
+
|
|
66
|
+
gateway = _text(row.get("gateway"), limit=64)
|
|
67
|
+
if gateway is not None:
|
|
68
|
+
gateway = gateway.lower()
|
|
69
|
+
endpoint = _text(row.get("endpoint"), limit=64)
|
|
70
|
+
reported_source = _text(row.get("reported_cost_source"))
|
|
71
|
+
upstream_source = _text(row.get("reported_upstream_cost_source"))
|
|
72
|
+
|
|
73
|
+
return GatewayBillingEvidence(
|
|
74
|
+
gateway=gateway,
|
|
75
|
+
endpoint=endpoint,
|
|
76
|
+
reported_cost_usd=_non_negative_decimal(row.get("reported_cost_usd")),
|
|
77
|
+
reported_cost_source=reported_source,
|
|
78
|
+
reported_upstream_cost_usd=_non_negative_decimal(
|
|
79
|
+
row.get("reported_upstream_cost_usd")
|
|
80
|
+
),
|
|
81
|
+
reported_upstream_cost_source=upstream_source,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _is_openrouter_chat_completions(evidence: GatewayBillingEvidence) -> bool:
|
|
86
|
+
return (
|
|
87
|
+
evidence.gateway == _OPENROUTER
|
|
88
|
+
and evidence.endpoint == _CHAT_COMPLETIONS
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def resolve_billing(
|
|
93
|
+
catalog_result: CostResult,
|
|
94
|
+
evidence: GatewayBillingEvidence,
|
|
95
|
+
) -> BillingDecision:
|
|
96
|
+
"""Select effective cost without combining independent reported amounts."""
|
|
97
|
+
|
|
98
|
+
qualified_openrouter = _is_openrouter_chat_completions(evidence)
|
|
99
|
+
reported_cost = (
|
|
100
|
+
evidence.reported_cost_usd
|
|
101
|
+
if qualified_openrouter
|
|
102
|
+
and evidence.reported_cost_source == _OPENROUTER_COST_SOURCE
|
|
103
|
+
else None
|
|
104
|
+
)
|
|
105
|
+
upstream_cost = (
|
|
106
|
+
evidence.reported_upstream_cost_usd
|
|
107
|
+
if qualified_openrouter
|
|
108
|
+
and evidence.reported_upstream_cost_source
|
|
109
|
+
== _OPENROUTER_UPSTREAM_COST_SOURCE
|
|
110
|
+
else None
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if reported_cost is not None:
|
|
114
|
+
cost_usd = reported_cost
|
|
115
|
+
cost_status = "priced"
|
|
116
|
+
provenance = "gateway_reported"
|
|
117
|
+
elif catalog_result.cost_usd is not None:
|
|
118
|
+
cost_usd = catalog_result.cost_usd
|
|
119
|
+
cost_status = catalog_result.status
|
|
120
|
+
provenance = "catalog"
|
|
121
|
+
else:
|
|
122
|
+
cost_usd = None
|
|
123
|
+
cost_status = catalog_result.status
|
|
124
|
+
provenance = "none"
|
|
125
|
+
|
|
126
|
+
return BillingDecision(
|
|
127
|
+
cost_usd=cost_usd,
|
|
128
|
+
cost_status=cost_status,
|
|
129
|
+
cost_provenance=provenance,
|
|
130
|
+
reported_cost_usd=reported_cost,
|
|
131
|
+
reported_upstream_cost_usd=upstream_cost,
|
|
132
|
+
catalog_cost_usd=catalog_result.cost_usd,
|
|
133
|
+
catalog_price_id=catalog_result.price_id,
|
|
134
|
+
catalog_reasons=catalog_result.reasons,
|
|
135
|
+
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metergraph-core
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Reusable MeterGraph catalog and deterministic
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Reusable MeterGraph catalog and deterministic billing engine
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -12,14 +12,15 @@ Requires-Dist: pytest>=8; extra == "dev"
|
|
|
12
12
|
|
|
13
13
|
# metergraph-core
|
|
14
14
|
|
|
15
|
-
Reusable catalog and deterministic
|
|
15
|
+
Reusable catalog and deterministic billing engine for MeterGraph.
|
|
16
16
|
|
|
17
17
|
`metergraph-core` owns the public effective-dated model catalog and the pricing
|
|
18
18
|
logic shared across MeterGraph systems: catalog parsing and validation, provider
|
|
19
19
|
and model alias resolution, channel and region selection, input/output/cache/
|
|
20
20
|
batch/long-context pricing rules, deterministic cost calculation with reason
|
|
21
21
|
codes, stable logical price identifiers, and catalog version and content-hash
|
|
22
|
-
reporting.
|
|
22
|
+
reporting. It also owns the pure decision that selects a qualified
|
|
23
|
+
gateway-reported charge or a catalog estimate as the effective call cost.
|
|
23
24
|
|
|
24
25
|
It does not own HTTP routes, database access, migrations, authentication,
|
|
25
26
|
tenancy, ingest, dashboard code, or any hosted-only concern, and it never reads
|
|
@@ -83,19 +84,51 @@ exists. It never substitutes a direct-provider price for a gateway price.
|
|
|
83
84
|
`LoadedCatalog.pricing_verified_at` records when the bundled catalog was last
|
|
84
85
|
checked against its linked provider sources.
|
|
85
86
|
|
|
87
|
+
## Billing evidence
|
|
88
|
+
|
|
89
|
+
Servers can pass content-blind, already-extracted gateway fields through the
|
|
90
|
+
shared trust boundary and combine them with a catalog result:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from metergraph_core import normalize_gateway_evidence, resolve_billing
|
|
94
|
+
|
|
95
|
+
evidence = normalize_gateway_evidence({
|
|
96
|
+
"gateway": "openrouter",
|
|
97
|
+
"endpoint": "chat.completions",
|
|
98
|
+
"reported_cost_usd": "0.00482",
|
|
99
|
+
"reported_cost_source": "openrouter.usage.cost",
|
|
100
|
+
})
|
|
101
|
+
decision = resolve_billing(result, evidence)
|
|
102
|
+
print(decision.cost_usd, decision.cost_provenance)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The initial qualified contract is OpenRouter Chat Completions. A finite,
|
|
106
|
+
non-negative `openrouter.usage.cost` value, including zero, takes precedence
|
|
107
|
+
over a catalog estimate. The decision retains both values and never adds the
|
|
108
|
+
separately reported upstream inference cost to the OpenRouter account charge.
|
|
109
|
+
Unsupported or malformed evidence falls back to the catalog result.
|
|
110
|
+
|
|
111
|
+
The billing module validates only gateway, endpoint, fixed source names, and
|
|
112
|
+
decimal cost values. It does not inspect provider response content or own SDK
|
|
113
|
+
capture, HTTP, timestamps, trace context, persistence, or tenant behavior.
|
|
114
|
+
|
|
86
115
|
## Public API
|
|
87
116
|
|
|
88
117
|
```python
|
|
89
118
|
from metergraph_core import (
|
|
90
119
|
Alias,
|
|
120
|
+
BillingDecision,
|
|
91
121
|
CatalogError,
|
|
92
122
|
CatalogSnapshot,
|
|
93
123
|
CostResult,
|
|
124
|
+
GatewayBillingEvidence,
|
|
94
125
|
LoadedCatalog,
|
|
95
126
|
Price,
|
|
96
127
|
ResolvedPrice,
|
|
97
128
|
load_catalog,
|
|
129
|
+
normalize_gateway_evidence,
|
|
98
130
|
parse_catalog,
|
|
131
|
+
resolve_billing,
|
|
99
132
|
)
|
|
100
133
|
```
|
|
101
134
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{metergraph_core-0.1.4 → metergraph_core-0.2.0}/src/metergraph_core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|