unitysvc-core 0.2.28__tar.gz → 0.2.29__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.
- {unitysvc_core-0.2.28/src/unitysvc_core.egg-info → unitysvc_core-0.2.29}/PKG-INFO +1 -1
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/pyproject.toml +1 -1
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/pricing.py +27 -14
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29/src/unitysvc_core.egg-info}/PKG-INFO +1 -1
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_effective_price.py +27 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/LICENSE +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/README.md +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/setup.cfg +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/__init__.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/__init__.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/base.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/documents.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/listing_data.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/listing_v1.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/offering_data.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/offering_v1.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/promotion_data.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/provider_data.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/provider_v1.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/service.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/service_group_data.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/validators.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/py.typed +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/schema/base.json +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/schema/listing_v1.json +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/schema/offering_v1.json +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/schema/promotion_v1.json +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/schema/provider_v1.json +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/schema/service_group_v1.json +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/utils.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/validator.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core.egg-info/SOURCES.txt +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core.egg-info/dependency_links.txt +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core.egg-info/requires.txt +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core.egg-info/top_level.txt +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_access_interface_routing.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_hidden_path_skip.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_offering_data.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_pricing.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_provider_account_rate_limits.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_service_group.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_service_status.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_utils.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_validator.py +0 -0
- {unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/tests/test_validators.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "unitysvc-core"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.29"
|
|
8
8
|
description = "Shared data models and validation helpers for UnitySVC packages"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Bo Peng", email = "bo.peng@unitysvc.com" }]
|
|
@@ -342,6 +342,23 @@ def upstream_price_from_description(text: str | None) -> Decimal | None:
|
|
|
342
342
|
return _blend(by_label["in"], by_label["out"]) * scale
|
|
343
343
|
|
|
344
344
|
|
|
345
|
+
def _zero_rate(figure: EffectivePrice, description: str | None) -> EffectivePrice | None:
|
|
346
|
+
"""A zero-rate price's effective price: free, or what the customer pays upstream.
|
|
347
|
+
|
|
348
|
+
A ``0`` with a description is a bring-your-own-key price: the customer pays
|
|
349
|
+
the upstream provider, at the rate the description states. A description
|
|
350
|
+
that states no parseable rate, or $0/$0 (what generators write when they
|
|
351
|
+
lack the provider's price), is unknown. A ``0`` with no description is
|
|
352
|
+
genuinely free.
|
|
353
|
+
"""
|
|
354
|
+
if figure.amount != 0 or not description:
|
|
355
|
+
return figure
|
|
356
|
+
upstream = upstream_price_from_description(description)
|
|
357
|
+
if upstream is None or upstream == 0:
|
|
358
|
+
return None
|
|
359
|
+
return EffectivePrice(amount=upstream, unit=UNIT_TOKENS, source="upstream")
|
|
360
|
+
|
|
361
|
+
|
|
345
362
|
# ============================================================================
|
|
346
363
|
# Pricing Models - Discriminated Union for type-safe pricing validation
|
|
347
364
|
# ============================================================================
|
|
@@ -475,14 +492,19 @@ class TokenPriceData(BasePriceData):
|
|
|
475
492
|
return {"one_million_tokens": 1_000_000, "one_thousand_tokens": 1_000, "one_token": 1}[self.type]
|
|
476
493
|
|
|
477
494
|
def effective_price(self) -> EffectivePrice | None:
|
|
478
|
-
"""``(input + 4*output) / 5`` when priced separately, else ``price``; per 1M tokens.
|
|
495
|
+
"""``(input + 4*output) / 5`` when priced separately, else ``price``; per 1M tokens.
|
|
496
|
+
|
|
497
|
+
A zero rate with a description is a bring-your-own-key price: see
|
|
498
|
+
``_zero_rate``.
|
|
499
|
+
"""
|
|
479
500
|
if self.input is not None and self.output is not None:
|
|
480
501
|
per_unit = _blend(Decimal(self.input), Decimal(self.output))
|
|
481
502
|
elif self.price is not None:
|
|
482
503
|
per_unit = Decimal(self.price)
|
|
483
504
|
else:
|
|
484
505
|
return None
|
|
485
|
-
|
|
506
|
+
figure = _per_normalized_unit(self.type, per_unit)
|
|
507
|
+
return None if figure is None else _zero_rate(figure, self.description)
|
|
486
508
|
|
|
487
509
|
def calculate_cost(
|
|
488
510
|
self,
|
|
@@ -931,19 +953,10 @@ class ConstantPriceData(BasePriceData):
|
|
|
931
953
|
def effective_price(self) -> EffectivePrice | None:
|
|
932
954
|
"""``price`` per request.
|
|
933
955
|
|
|
934
|
-
A ``0`` with a description is a bring-your-own-key price:
|
|
935
|
-
|
|
936
|
-
description that states no parseable rate, or $0/$0 (what generators
|
|
937
|
-
write when they lack the provider's price), is unknown. A ``0`` with no
|
|
938
|
-
description is genuinely free.
|
|
956
|
+
A ``0`` with a description is a bring-your-own-key price: see
|
|
957
|
+
``_zero_rate``.
|
|
939
958
|
"""
|
|
940
|
-
|
|
941
|
-
if amount != 0 or not self.description:
|
|
942
|
-
return _rate(amount, UNIT_REQUEST)
|
|
943
|
-
upstream = upstream_price_from_description(self.description)
|
|
944
|
-
if upstream is None or upstream == 0:
|
|
945
|
-
return None
|
|
946
|
-
return EffectivePrice(amount=upstream, unit=UNIT_TOKENS, source="upstream")
|
|
959
|
+
return _zero_rate(_rate(Decimal(self.price), UNIT_REQUEST), self.description)
|
|
947
960
|
|
|
948
961
|
def calculate_cost(
|
|
949
962
|
self,
|
|
@@ -112,6 +112,33 @@ class TestByokConstant:
|
|
|
112
112
|
assert ep(priced) == rate("0.02", "request")
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
class TestByokZeroRateTokens:
|
|
116
|
+
"""Generators also write BYOK as a zero token rate, e.g. Mistral models."""
|
|
117
|
+
|
|
118
|
+
MISTRAL = (
|
|
119
|
+
"Free ~ BYOK | usage is billed by Mistral AI directly at $1.5 / $7.5 / $0.15 per 1M input/output/cached tokens"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def test_zero_split_rate_reads_description(self) -> None:
|
|
123
|
+
byok = {"type": "one_million_tokens", "input": "0", "output": "0", "cached_input": "0"}
|
|
124
|
+
figure = ep({**byok, "description": self.MISTRAL})
|
|
125
|
+
assert figure == rate("6.3", "one_million_tokens", "upstream")
|
|
126
|
+
|
|
127
|
+
def test_zero_unified_rate_reads_description(self) -> None:
|
|
128
|
+
figure = ep({"type": "one_thousand_tokens", "price": "0", "description": HF_UPSTREAM})
|
|
129
|
+
assert figure == rate("3.8", "one_million_tokens", "upstream")
|
|
130
|
+
|
|
131
|
+
def test_zero_rate_without_description_is_free(self) -> None:
|
|
132
|
+
assert ep({"type": "one_million_tokens", "input": "0", "output": "0"}) == rate("0", "one_million_tokens")
|
|
133
|
+
|
|
134
|
+
def test_zero_rate_with_unparseable_description_is_unknown(self) -> None:
|
|
135
|
+
assert ep({"type": "one_million_tokens", "price": "0", "description": "Bring your own key"}) is None
|
|
136
|
+
|
|
137
|
+
def test_nonzero_rate_ignores_description(self) -> None:
|
|
138
|
+
priced = {**TOKENS, "description": self.MISTRAL}
|
|
139
|
+
assert ep(priced) == rate("1.8", "one_million_tokens")
|
|
140
|
+
|
|
141
|
+
|
|
115
142
|
class TestUpstreamPriceFromDescription:
|
|
116
143
|
@pytest.mark.parametrize(
|
|
117
144
|
("text", "expected"),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/models/service_group_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core/schema/service_group_v1.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unitysvc_core-0.2.28 → unitysvc_core-0.2.29}/src/unitysvc_core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|