unitysvc-core 0.2.4__tar.gz → 0.2.5__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 (40) hide show
  1. {unitysvc_core-0.2.4/src/unitysvc_core.egg-info → unitysvc_core-0.2.5}/PKG-INFO +1 -1
  2. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/pyproject.toml +1 -1
  3. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/__init__.py +2 -2
  4. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/base.py +2 -2
  5. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/offering_data.py +2 -2
  6. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/offering_v1.py +1 -1
  7. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/pricing.py +54 -54
  8. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/service.py +12 -1
  9. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/schema/listing_v1.json +74 -74
  10. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/schema/offering_v1.json +76 -76
  11. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5/src/unitysvc_core.egg-info}/PKG-INFO +1 -1
  12. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/tests/test_pricing.py +38 -38
  13. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/LICENSE +0 -0
  14. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/README.md +0 -0
  15. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/setup.cfg +0 -0
  16. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/__init__.py +0 -0
  17. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/documents.py +0 -0
  18. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/listing_data.py +0 -0
  19. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/listing_v1.py +0 -0
  20. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/promotion_data.py +0 -0
  21. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/provider_data.py +0 -0
  22. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/provider_v1.py +0 -0
  23. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/service_group_data.py +0 -0
  24. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/models/validators.py +0 -0
  25. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/py.typed +0 -0
  26. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/schema/base.json +0 -0
  27. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/schema/promotion_v1.json +0 -0
  28. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/schema/provider_v1.json +0 -0
  29. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/schema/service_group_v1.json +0 -0
  30. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/utils.py +0 -0
  31. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core/validator.py +0 -0
  32. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core.egg-info/SOURCES.txt +0 -0
  33. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core.egg-info/dependency_links.txt +0 -0
  34. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core.egg-info/requires.txt +0 -0
  35. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/src/unitysvc_core.egg-info/top_level.txt +0 -0
  36. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/tests/test_service_group.py +0 -0
  37. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/tests/test_service_status.py +0 -0
  38. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/tests/test_utils.py +0 -0
  39. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/tests/test_validator.py +0 -0
  40. {unitysvc_core-0.2.4 → unitysvc_core-0.2.5}/tests/test_validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitysvc-core
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Shared data models and validation helpers for UnitySVC packages
5
5
  Author-email: Bo Peng <bo.peng@unitysvc.com>
6
6
  Maintainer-email: Bo Peng <bo.peng@unitysvc.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "unitysvc-core"
7
- version = "0.2.4"
7
+ version = "0.2.5"
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" }]
@@ -33,6 +33,7 @@ from .offering_v1 import OfferingV1
33
33
  from .pricing import (
34
34
  AddPriceData,
35
35
  BasePriceData,
36
+ ChannelPriceData,
36
37
  ConstantPriceData,
37
38
  CountPriceData,
38
39
  DataPriceData,
@@ -43,7 +44,6 @@ from .pricing import (
43
44
  ImagePriceData,
44
45
  MaxPriceData,
45
46
  MinPriceData,
46
- ModePriceData,
47
47
  MultiplyPriceData,
48
48
  PercentageStr,
49
49
  PriceStr,
@@ -159,7 +159,7 @@ __all__ = [
159
159
  "MultiplyPriceData",
160
160
  "MaxPriceData",
161
161
  "MinPriceData",
162
- "ModePriceData",
162
+ "ChannelPriceData",
163
163
  "FirstPriceData",
164
164
  "TieredPriceData",
165
165
  "GraduatedPriceData",
@@ -216,8 +216,8 @@ class PricingTypeEnum(StrEnum):
216
216
  graduated = "graduated" # Graduated tiers (each tier's units at that rate)
217
217
  # Expression-based pricing (payout_price only)
218
218
  expr = "expr" # Arbitrary expression using usage metrics
219
- # Mode-based pricing: select a sub-price by the request's resolved mode
220
- mode = "mode" # Per-mode pricing keyed by mode name (e.g. managed, byok)
219
+ # Channel-based pricing: select a sub-price by the request's resolved channel
220
+ channel = "channel" # Per-channel pricing keyed by channel name (e.g. managed, byok)
221
221
 
222
222
 
223
223
  class QuotaResetCycleEnum(StrEnum):
@@ -91,10 +91,10 @@ class ServiceOfferingData(BaseModel):
91
91
  description="Payout pricing: How to calculate seller payout",
92
92
  )
93
93
 
94
- # Access interfaces (keyed by name)
94
+ # Access channels (keyed by channel name)
95
95
  upstream_access_config: dict[str, dict[str, Any]] | None = Field(
96
96
  default=None,
97
- description="Upstream access interfaces, keyed by name",
97
+ description="Upstream access channels, keyed by channel name",
98
98
  )
99
99
 
100
100
  # Documents (keyed by title)
@@ -46,7 +46,7 @@ class OfferingV1(ServiceOfferingData):
46
46
 
47
47
  # Override with typed models for file validation
48
48
  upstream_access_config: dict[str, UpstreamAccessConfigData] = Field( # type: ignore[assignment]
49
- description="Upstream access interfaces, keyed by name",
49
+ description="Upstream access channels, keyed by channel name",
50
50
  )
51
51
 
52
52
  documents: dict[str, DocumentData] | None = Field( # type: ignore[assignment]
@@ -280,7 +280,7 @@ class TokenPriceData(BasePriceData):
280
280
  usage: UsageData,
281
281
  customer_charge: Decimal | None = None,
282
282
  request_count: int | None = None,
283
- mode: str | None = None,
283
+ channel: str | None = None,
284
284
  ) -> Decimal:
285
285
  """Calculate cost for token-based pricing.
286
286
 
@@ -347,7 +347,7 @@ class TimePriceData(BasePriceData):
347
347
  usage: UsageData,
348
348
  customer_charge: Decimal | None = None,
349
349
  request_count: int | None = None,
350
- mode: str | None = None,
350
+ channel: str | None = None,
351
351
  ) -> Decimal:
352
352
  """Calculate cost for time-based pricing.
353
353
 
@@ -388,7 +388,7 @@ class ImagePriceData(BasePriceData):
388
388
  usage: UsageData,
389
389
  customer_charge: Decimal | None = None,
390
390
  request_count: int | None = None,
391
- mode: str | None = None,
391
+ channel: str | None = None,
392
392
  ) -> Decimal:
393
393
  """Calculate cost for image-based pricing.
394
394
 
@@ -425,7 +425,7 @@ class StepPriceData(BasePriceData):
425
425
  usage: UsageData,
426
426
  customer_charge: Decimal | None = None,
427
427
  request_count: int | None = None,
428
- mode: str | None = None,
428
+ channel: str | None = None,
429
429
  ) -> Decimal:
430
430
  """Calculate cost for step-based pricing.
431
431
 
@@ -465,7 +465,7 @@ class DataPriceData(BasePriceData):
465
465
  usage: UsageData,
466
466
  customer_charge: Decimal | None = None,
467
467
  request_count: int | None = None,
468
- mode: str | None = None,
468
+ channel: str | None = None,
469
469
  ) -> Decimal:
470
470
  """Calculate cost for data-volume pricing.
471
471
 
@@ -509,7 +509,7 @@ class CountPriceData(BasePriceData):
509
509
  usage: UsageData,
510
510
  customer_charge: Decimal | None = None,
511
511
  request_count: int | None = None,
512
- mode: str | None = None,
512
+ channel: str | None = None,
513
513
  ) -> Decimal:
514
514
  """Calculate cost for count-scaled pricing.
515
515
 
@@ -572,7 +572,7 @@ class RevenueSharePriceData(BasePriceData):
572
572
  usage: UsageData,
573
573
  customer_charge: Decimal | None = None,
574
574
  request_count: int | None = None,
575
- mode: str | None = None,
575
+ channel: str | None = None,
576
576
  ) -> Decimal:
577
577
  """Calculate cost for revenue share pricing.
578
578
 
@@ -612,7 +612,7 @@ class ConstantPriceData(BasePriceData):
612
612
  usage: UsageData,
613
613
  customer_charge: Decimal | None = None,
614
614
  request_count: int | None = None,
615
- mode: str | None = None,
615
+ channel: str | None = None,
616
616
  ) -> Decimal:
617
617
  """Return the constant price regardless of usage.
618
618
 
@@ -685,7 +685,7 @@ class AddPriceData(BasePriceData):
685
685
  usage: UsageData,
686
686
  customer_charge: Decimal | None = None,
687
687
  request_count: int | None = None,
688
- mode: str | None = None,
688
+ channel: str | None = None,
689
689
  ) -> Decimal:
690
690
  """Calculate total cost by summing all price components.
691
691
 
@@ -700,7 +700,7 @@ class AddPriceData(BasePriceData):
700
700
  total = Decimal("0")
701
701
  for price_data in self.prices:
702
702
  component = validate_pricing(price_data)
703
- total += component.calculate_cost(usage, customer_charge, request_count, mode)
703
+ total += component.calculate_cost(usage, customer_charge, request_count, channel)
704
704
  return total
705
705
 
706
706
 
@@ -745,7 +745,7 @@ class MultiplyPriceData(BasePriceData):
745
745
  usage: UsageData,
746
746
  customer_charge: Decimal | None = None,
747
747
  request_count: int | None = None,
748
- mode: str | None = None,
748
+ channel: str | None = None,
749
749
  ) -> Decimal:
750
750
  """Calculate cost by multiplying base price by factor.
751
751
 
@@ -758,7 +758,7 @@ class MultiplyPriceData(BasePriceData):
758
758
  Base cost multiplied by factor
759
759
  """
760
760
  base_pricing = validate_pricing(self.base)
761
- base_cost = base_pricing.calculate_cost(usage, customer_charge, request_count, mode)
761
+ base_cost = base_pricing.calculate_cost(usage, customer_charge, request_count, channel)
762
762
  return base_cost * Decimal(self.factor)
763
763
 
764
764
 
@@ -799,7 +799,7 @@ class MaxPriceData(BasePriceData):
799
799
  usage: UsageData,
800
800
  customer_charge: Decimal | None = None,
801
801
  request_count: int | None = None,
802
- mode: str | None = None,
802
+ channel: str | None = None,
803
803
  ) -> Decimal:
804
804
  """Return the highest cost among applicable children.
805
805
 
@@ -810,7 +810,7 @@ class MaxPriceData(BasePriceData):
810
810
  for price_data in self.prices:
811
811
  try:
812
812
  component = validate_pricing(price_data)
813
- costs.append(component.calculate_cost(usage, customer_charge, request_count, mode))
813
+ costs.append(component.calculate_cost(usage, customer_charge, request_count, channel))
814
814
  except ValueError:
815
815
  continue
816
816
  if not costs:
@@ -857,7 +857,7 @@ class MinPriceData(BasePriceData):
857
857
  usage: UsageData,
858
858
  customer_charge: Decimal | None = None,
859
859
  request_count: int | None = None,
860
- mode: str | None = None,
860
+ channel: str | None = None,
861
861
  ) -> Decimal:
862
862
  """Return the lowest cost among applicable children.
863
863
 
@@ -868,7 +868,7 @@ class MinPriceData(BasePriceData):
868
868
  for price_data in self.prices:
869
869
  try:
870
870
  component = validate_pricing(price_data)
871
- costs.append(component.calculate_cost(usage, customer_charge, request_count, mode))
871
+ costs.append(component.calculate_cost(usage, customer_charge, request_count, channel))
872
872
  except ValueError:
873
873
  continue
874
874
  if not costs:
@@ -914,7 +914,7 @@ class FirstPriceData(BasePriceData):
914
914
  usage: UsageData,
915
915
  customer_charge: Decimal | None = None,
916
916
  request_count: int | None = None,
917
- mode: str | None = None,
917
+ channel: str | None = None,
918
918
  ) -> Decimal:
919
919
  """Return the cost from the first child that can handle the usage.
920
920
 
@@ -925,7 +925,7 @@ class FirstPriceData(BasePriceData):
925
925
  for price_data in self.prices:
926
926
  try:
927
927
  component = validate_pricing(price_data)
928
- return component.calculate_cost(usage, customer_charge, request_count, mode)
928
+ return component.calculate_cost(usage, customer_charge, request_count, channel)
929
929
  except ValueError:
930
930
  continue
931
931
  raise ValueError("No child pricing could handle the provided usage data")
@@ -1140,7 +1140,7 @@ class ExprPriceData(BasePriceData):
1140
1140
  usage: UsageData,
1141
1141
  customer_charge: Decimal | None = None,
1142
1142
  request_count: int | None = None,
1143
- mode: str | None = None,
1143
+ channel: str | None = None,
1144
1144
  ) -> Decimal:
1145
1145
  """Calculate cost by evaluating the expression with usage data.
1146
1146
 
@@ -1214,7 +1214,7 @@ class TieredPriceData(BasePriceData):
1214
1214
  usage: UsageData,
1215
1215
  customer_charge: Decimal | None = None,
1216
1216
  request_count: int | None = None,
1217
- mode: str | None = None,
1217
+ channel: str | None = None,
1218
1218
  ) -> Decimal:
1219
1219
  """Calculate cost based on which tier the usage falls into.
1220
1220
 
@@ -1232,7 +1232,7 @@ class TieredPriceData(BasePriceData):
1232
1232
  for tier in self.tiers:
1233
1233
  if tier.up_to is None or metric_value <= tier.up_to:
1234
1234
  tier_pricing = validate_pricing(tier.price)
1235
- return tier_pricing.calculate_cost(usage, customer_charge, request_count, mode)
1235
+ return tier_pricing.calculate_cost(usage, customer_charge, request_count, channel)
1236
1236
 
1237
1237
  # Should not reach here if tiers are properly configured
1238
1238
  raise ValueError("No matching tier found")
@@ -1297,7 +1297,7 @@ class GraduatedPriceData(BasePriceData):
1297
1297
  usage: UsageData,
1298
1298
  customer_charge: Decimal | None = None,
1299
1299
  request_count: int | None = None,
1300
- mode: str | None = None,
1300
+ channel: str | None = None,
1301
1301
  ) -> Decimal:
1302
1302
  """Calculate cost with graduated pricing across tiers.
1303
1303
 
@@ -1333,56 +1333,56 @@ class GraduatedPriceData(BasePriceData):
1333
1333
  return total_cost
1334
1334
 
1335
1335
 
1336
- class ModePriceData(BasePriceData):
1336
+ class ChannelPriceData(BasePriceData):
1337
1337
  """
1338
- Mode-based pricing that selects a sub-price by the request's resolved mode.
1338
+ Channel-based pricing that selects a sub-price by the request's resolved channel.
1339
1339
 
1340
- The active mode is decided elsewhere — e.g. the gateway picks which keyed
1341
- ``upstream_access_config`` a request routes through (``"managed"``,
1342
- ``"byok"``, ``"byoe"``, …) — and threads it into
1343
- ``calculate_cost(..., mode=...)``. The matching entry in ``modes`` is
1344
- evaluated; when the mode is unknown or unset, the ``default`` mode is used.
1340
+ The active channel is decided elsewhere — e.g. the gateway picks which keyed
1341
+ ``upstream_access_config`` (upstream access channel) a request routes through
1342
+ (``"managed"``, ``"byok"``, ``"byoe"``, …) — and threads it into
1343
+ ``calculate_cost(..., channel=...)``. The matching entry in ``channels`` is
1344
+ evaluated; when the channel is unknown or unset, the ``default`` channel is used.
1345
1345
 
1346
1346
  There is no special-casing on the consumer side: every pricing type accepts
1347
- an optional ``mode`` and a mode-independent price simply ignores it, so a
1348
- plain price behaves identically for all modes. Mode-dependent pricing is
1347
+ an optional ``channel`` and a channel-independent price simply ignores it, so a
1348
+ plain price behaves identically for all channels. Channel-dependent pricing is
1349
1349
  just this one extra optional pricing type.
1350
1350
 
1351
- Each ``modes`` value is itself any pricing object (flat, composite, tiered,
1352
- or even nested mode-based), validated lazily — mirroring how tiered pricing
1351
+ Each ``channels`` value is itself any pricing object (flat, composite, tiered,
1352
+ or even nested channel-based), validated lazily — mirroring how tiered pricing
1353
1353
  holds per-tier prices.
1354
1354
 
1355
1355
  Example:
1356
1356
  {
1357
- "type": "mode",
1357
+ "type": "channel",
1358
1358
  "default": "managed",
1359
- "modes": {
1359
+ "channels": {
1360
1360
  "managed": {"type": "one_million_tokens", "input": "0.50", "output": "1.50"},
1361
1361
  "byok": {"type": "constant", "price": "0"}
1362
1362
  }
1363
1363
  }
1364
1364
  """
1365
1365
 
1366
- type: Literal["mode"] = "mode"
1366
+ type: Literal["channel"] = "channel"
1367
1367
 
1368
1368
  default: str = Field(
1369
- description="Mode key evaluated when the request's mode is unknown or unset. "
1370
- "Must be one of the keys in `modes`.",
1369
+ description="Channel name evaluated when the request's channel is unknown or unset. "
1370
+ "Must be one of the keys in `channels`.",
1371
1371
  )
1372
1372
 
1373
- modes: dict[str, dict[str, Any]] = Field(
1374
- description="Per-mode pricing keyed by mode name (e.g. 'managed', 'byok'). "
1373
+ channels: dict[str, dict[str, Any]] = Field(
1374
+ description="Per-channel pricing keyed by channel name (e.g. 'managed', 'byok'). "
1375
1375
  "Each value is a nested pricing object of any type.",
1376
1376
  min_length=1,
1377
1377
  )
1378
1378
 
1379
1379
  @model_validator(mode="after")
1380
- def _validate_default_and_price(self) -> ModePriceData:
1380
+ def _validate_default_and_price(self) -> ChannelPriceData:
1381
1381
  """Require ``default`` to exist and auto-compute the nominal price from it."""
1382
- if self.default not in self.modes:
1383
- raise ValueError(f"default mode {self.default!r} is not defined in modes ({sorted(self.modes)})")
1382
+ if self.default not in self.channels:
1383
+ raise ValueError(f"default channel {self.default!r} is not defined in channels ({sorted(self.channels)})")
1384
1384
  if self.price is None:
1385
- self.price = _extract_nominal_price(self.modes[self.default])
1385
+ self.price = _extract_nominal_price(self.channels[self.default])
1386
1386
  return self
1387
1387
 
1388
1388
  def calculate_cost(
@@ -1390,24 +1390,24 @@ class ModePriceData(BasePriceData):
1390
1390
  usage: UsageData,
1391
1391
  customer_charge: Decimal | None = None,
1392
1392
  request_count: int | None = None,
1393
- mode: str | None = None,
1393
+ channel: str | None = None,
1394
1394
  ) -> Decimal:
1395
- """Evaluate the price for the resolved ``mode`` (falling back to ``default``).
1395
+ """Evaluate the price for the resolved ``channel`` (falling back to ``default``).
1396
1396
 
1397
1397
  Args:
1398
- usage: Usage data passed to the selected mode's price.
1398
+ usage: Usage data passed to the selected channel's price.
1399
1399
  customer_charge: Customer charge passed through.
1400
1400
  request_count: Request count passed through.
1401
- mode: The resolved mode; unknown/``None`` falls back to ``default``.
1401
+ channel: The resolved channel; unknown/``None`` falls back to ``default``.
1402
1402
 
1403
1403
  Returns:
1404
- Cost from the selected mode's pricing.
1404
+ Cost from the selected channel's pricing.
1405
1405
  """
1406
- selected = self.modes.get(mode) if mode is not None else None
1406
+ selected = self.channels.get(channel) if channel is not None else None
1407
1407
  if selected is None:
1408
- selected = self.modes[self.default]
1408
+ selected = self.channels[self.default]
1409
1409
  pricing = validate_pricing(selected)
1410
- return pricing.calculate_cost(usage, customer_charge, request_count, mode)
1410
+ return pricing.calculate_cost(usage, customer_charge, request_count, channel)
1411
1411
 
1412
1412
 
1413
1413
  # Discriminated union of all pricing types
@@ -1430,7 +1430,7 @@ Pricing = Annotated[
1430
1430
  | TieredPriceData
1431
1431
  | GraduatedPriceData
1432
1432
  | ExprPriceData
1433
- | ModePriceData,
1433
+ | ChannelPriceData,
1434
1434
  Field(discriminator="type"),
1435
1435
  ]
1436
1436
 
@@ -1454,7 +1454,7 @@ def validate_pricing(
1454
1454
  | TieredPriceData
1455
1455
  | GraduatedPriceData
1456
1456
  | ExprPriceData
1457
- | ModePriceData
1457
+ | ChannelPriceData
1458
1458
  ):
1459
1459
  """
1460
1460
  Validate pricing dict and return the appropriate typed model.
@@ -116,7 +116,18 @@ class AccessInterfaceData(BaseModel):
116
116
 
117
117
 
118
118
  class UpstreamAccessConfigData(AccessInterfaceData):
119
- """Upstream (seller-facing) access config data.
119
+ """One upstream access channel — a named entry in ``upstream_access_config``.
120
+
121
+ Each channel is a complete way for the gateway to reach the upstream: a wire
122
+ protocol (``access_method``), endpoint (``base_url``), credential (``api_key``),
123
+ routing key, and rate-limit/quality restrictions. A service may declare several
124
+ channels (keyed by free-form channel name, e.g. ``"managed"``, ``"byok"``,
125
+ ``"managed-eu"``); the gateway selects one per request.
126
+
127
+ The channel's *type* is derived from its credential/endpoint provenance:
128
+ ``managed`` (seller's key, ``${ secrets.* }``), ``byok`` (customer's key,
129
+ ``${ customer_secrets.* }``), or ``byoe`` (customer's key + customer-templated
130
+ ``base_url``).
120
131
 
121
132
  Extends AccessInterfaceData with extra="allow" to support protocol-specific
122
133
  configuration fields (e.g., S3 bucket/region, SMTP host/port) that the
@@ -207,6 +207,78 @@
207
207
  "title": "AuthMethodEnum",
208
208
  "type": "string"
209
209
  },
210
+ "ChannelPriceData": {
211
+ "additionalProperties": false,
212
+ "description": "Channel-based pricing that selects a sub-price by the request's resolved channel.\n\nThe active channel is decided elsewhere — e.g. the gateway picks which keyed\n``upstream_access_config`` (upstream access channel) a request routes through\n(``\"managed\"``, ``\"byok\"``, ``\"byoe\"``, …) — and threads it into\n``calculate_cost(..., channel=...)``. The matching entry in ``channels`` is\nevaluated; when the channel is unknown or unset, the ``default`` channel is used.\n\nThere is no special-casing on the consumer side: every pricing type accepts\nan optional ``channel`` and a channel-independent price simply ignores it, so a\nplain price behaves identically for all channels. Channel-dependent pricing is\njust this one extra optional pricing type.\n\nEach ``channels`` value is itself any pricing object (flat, composite, tiered,\nor even nested channel-based), validated lazily — mirroring how tiered pricing\nholds per-tier prices.\n\nExample:\n {\n \"type\": \"channel\",\n \"default\": \"managed\",\n \"channels\": {\n \"managed\": {\"type\": \"one_million_tokens\", \"input\": \"0.50\", \"output\": \"1.50\"},\n \"byok\": {\"type\": \"constant\", \"price\": \"0\"}\n }\n }",
213
+ "properties": {
214
+ "channels": {
215
+ "additionalProperties": {
216
+ "additionalProperties": true,
217
+ "type": "object"
218
+ },
219
+ "description": "Per-channel pricing keyed by channel name (e.g. 'managed', 'byok'). Each value is a nested pricing object of any type.",
220
+ "minProperties": 1,
221
+ "title": "Channels",
222
+ "type": "object"
223
+ },
224
+ "default": {
225
+ "description": "Channel name evaluated when the request's channel is unknown or unset. Must be one of the keys in `channels`.",
226
+ "title": "Default",
227
+ "type": "string"
228
+ },
229
+ "description": {
230
+ "anyOf": [
231
+ {
232
+ "type": "string"
233
+ },
234
+ {
235
+ "type": "null"
236
+ }
237
+ ],
238
+ "default": null,
239
+ "description": "Human-readable description of the pricing model",
240
+ "title": "Description"
241
+ },
242
+ "price": {
243
+ "anyOf": [
244
+ {
245
+ "type": "string"
246
+ },
247
+ {
248
+ "type": "null"
249
+ }
250
+ ],
251
+ "default": null,
252
+ "description": "Summary price for marketplace comparison and sorting. For simple pricing types this is the billing rate. For composite types (add, multiply, tiered, graduated) this is a seller-provided nominal value for marketplace display.",
253
+ "title": "Price"
254
+ },
255
+ "reference": {
256
+ "anyOf": [
257
+ {
258
+ "type": "string"
259
+ },
260
+ {
261
+ "type": "null"
262
+ }
263
+ ],
264
+ "default": null,
265
+ "description": "URL to upstream provider's pricing page",
266
+ "title": "Reference"
267
+ },
268
+ "type": {
269
+ "const": "channel",
270
+ "default": "channel",
271
+ "title": "Type",
272
+ "type": "string"
273
+ }
274
+ },
275
+ "required": [
276
+ "default",
277
+ "channels"
278
+ ],
279
+ "title": "ChannelPriceData",
280
+ "type": "object"
281
+ },
210
282
  "ConstantPriceData": {
211
283
  "additionalProperties": false,
212
284
  "description": "Price data for a constant/fixed price.\n\nUsed for fixed fees, discounts, or adjustments that don't depend on usage.\nPrice can be positive (charge) or negative (discount/credit).",
@@ -964,78 +1036,6 @@
964
1036
  "title": "MinPriceData",
965
1037
  "type": "object"
966
1038
  },
967
- "ModePriceData": {
968
- "additionalProperties": false,
969
- "description": "Mode-based pricing that selects a sub-price by the request's resolved mode.\n\nThe active mode is decided elsewhere — e.g. the gateway picks which keyed\n``upstream_access_config`` a request routes through (``\"managed\"``,\n``\"byok\"``, ``\"byoe\"``, …) — and threads it into\n``calculate_cost(..., mode=...)``. The matching entry in ``modes`` is\nevaluated; when the mode is unknown or unset, the ``default`` mode is used.\n\nThere is no special-casing on the consumer side: every pricing type accepts\nan optional ``mode`` and a mode-independent price simply ignores it, so a\nplain price behaves identically for all modes. Mode-dependent pricing is\njust this one extra optional pricing type.\n\nEach ``modes`` value is itself any pricing object (flat, composite, tiered,\nor even nested mode-based), validated lazily — mirroring how tiered pricing\nholds per-tier prices.\n\nExample:\n {\n \"type\": \"mode\",\n \"default\": \"managed\",\n \"modes\": {\n \"managed\": {\"type\": \"one_million_tokens\", \"input\": \"0.50\", \"output\": \"1.50\"},\n \"byok\": {\"type\": \"constant\", \"price\": \"0\"}\n }\n }",
970
- "properties": {
971
- "default": {
972
- "description": "Mode key evaluated when the request's mode is unknown or unset. Must be one of the keys in `modes`.",
973
- "title": "Default",
974
- "type": "string"
975
- },
976
- "description": {
977
- "anyOf": [
978
- {
979
- "type": "string"
980
- },
981
- {
982
- "type": "null"
983
- }
984
- ],
985
- "default": null,
986
- "description": "Human-readable description of the pricing model",
987
- "title": "Description"
988
- },
989
- "modes": {
990
- "additionalProperties": {
991
- "additionalProperties": true,
992
- "type": "object"
993
- },
994
- "description": "Per-mode pricing keyed by mode name (e.g. 'managed', 'byok'). Each value is a nested pricing object of any type.",
995
- "minProperties": 1,
996
- "title": "Modes",
997
- "type": "object"
998
- },
999
- "price": {
1000
- "anyOf": [
1001
- {
1002
- "type": "string"
1003
- },
1004
- {
1005
- "type": "null"
1006
- }
1007
- ],
1008
- "default": null,
1009
- "description": "Summary price for marketplace comparison and sorting. For simple pricing types this is the billing rate. For composite types (add, multiply, tiered, graduated) this is a seller-provided nominal value for marketplace display.",
1010
- "title": "Price"
1011
- },
1012
- "reference": {
1013
- "anyOf": [
1014
- {
1015
- "type": "string"
1016
- },
1017
- {
1018
- "type": "null"
1019
- }
1020
- ],
1021
- "default": null,
1022
- "description": "URL to upstream provider's pricing page",
1023
- "title": "Reference"
1024
- },
1025
- "type": {
1026
- "const": "mode",
1027
- "default": "mode",
1028
- "title": "Type",
1029
- "type": "string"
1030
- }
1031
- },
1032
- "required": [
1033
- "default",
1034
- "modes"
1035
- ],
1036
- "title": "ModePriceData",
1037
- "type": "object"
1038
- },
1039
1039
  "MultiplyPriceData": {
1040
1040
  "additionalProperties": false,
1041
1041
  "description": "Composite pricing that multiplies a base price by a factor.\n\nUseful for applying percentage-based adjustments to a base price.\n\nExample:\n {\n \"type\": \"multiply\",\n \"factor\": \"0.70\",\n \"base\": {\"type\": \"one_million_tokens\", \"input\": \"0.50\", \"output\": \"1.50\"}\n }",
@@ -1932,6 +1932,7 @@
1932
1932
  "discriminator": {
1933
1933
  "mapping": {
1934
1934
  "add": "#/$defs/AddPriceData",
1935
+ "channel": "#/$defs/ChannelPriceData",
1935
1936
  "constant": "#/$defs/ConstantPriceData",
1936
1937
  "expr": "#/$defs/ExprPriceData",
1937
1938
  "first": "#/$defs/FirstPriceData",
@@ -1939,7 +1940,6 @@
1939
1940
  "image": "#/$defs/ImagePriceData",
1940
1941
  "max": "#/$defs/MaxPriceData",
1941
1942
  "min": "#/$defs/MinPriceData",
1942
- "mode": "#/$defs/ModePriceData",
1943
1943
  "multiply": "#/$defs/MultiplyPriceData",
1944
1944
  "one_byte": "#/$defs/DataPriceData",
1945
1945
  "one_day": "#/$defs/TimePriceData",
@@ -2011,7 +2011,7 @@
2011
2011
  "$ref": "#/$defs/ExprPriceData"
2012
2012
  },
2013
2013
  {
2014
- "$ref": "#/$defs/ModePriceData"
2014
+ "$ref": "#/$defs/ChannelPriceData"
2015
2015
  }
2016
2016
  ]
2017
2017
  },
@@ -87,6 +87,78 @@
87
87
  "title": "AuthMethodEnum",
88
88
  "type": "string"
89
89
  },
90
+ "ChannelPriceData": {
91
+ "additionalProperties": false,
92
+ "description": "Channel-based pricing that selects a sub-price by the request's resolved channel.\n\nThe active channel is decided elsewhere — e.g. the gateway picks which keyed\n``upstream_access_config`` (upstream access channel) a request routes through\n(``\"managed\"``, ``\"byok\"``, ``\"byoe\"``, …) — and threads it into\n``calculate_cost(..., channel=...)``. The matching entry in ``channels`` is\nevaluated; when the channel is unknown or unset, the ``default`` channel is used.\n\nThere is no special-casing on the consumer side: every pricing type accepts\nan optional ``channel`` and a channel-independent price simply ignores it, so a\nplain price behaves identically for all channels. Channel-dependent pricing is\njust this one extra optional pricing type.\n\nEach ``channels`` value is itself any pricing object (flat, composite, tiered,\nor even nested channel-based), validated lazily — mirroring how tiered pricing\nholds per-tier prices.\n\nExample:\n {\n \"type\": \"channel\",\n \"default\": \"managed\",\n \"channels\": {\n \"managed\": {\"type\": \"one_million_tokens\", \"input\": \"0.50\", \"output\": \"1.50\"},\n \"byok\": {\"type\": \"constant\", \"price\": \"0\"}\n }\n }",
93
+ "properties": {
94
+ "channels": {
95
+ "additionalProperties": {
96
+ "additionalProperties": true,
97
+ "type": "object"
98
+ },
99
+ "description": "Per-channel pricing keyed by channel name (e.g. 'managed', 'byok'). Each value is a nested pricing object of any type.",
100
+ "minProperties": 1,
101
+ "title": "Channels",
102
+ "type": "object"
103
+ },
104
+ "default": {
105
+ "description": "Channel name evaluated when the request's channel is unknown or unset. Must be one of the keys in `channels`.",
106
+ "title": "Default",
107
+ "type": "string"
108
+ },
109
+ "description": {
110
+ "anyOf": [
111
+ {
112
+ "type": "string"
113
+ },
114
+ {
115
+ "type": "null"
116
+ }
117
+ ],
118
+ "default": null,
119
+ "description": "Human-readable description of the pricing model",
120
+ "title": "Description"
121
+ },
122
+ "price": {
123
+ "anyOf": [
124
+ {
125
+ "type": "string"
126
+ },
127
+ {
128
+ "type": "null"
129
+ }
130
+ ],
131
+ "default": null,
132
+ "description": "Summary price for marketplace comparison and sorting. For simple pricing types this is the billing rate. For composite types (add, multiply, tiered, graduated) this is a seller-provided nominal value for marketplace display.",
133
+ "title": "Price"
134
+ },
135
+ "reference": {
136
+ "anyOf": [
137
+ {
138
+ "type": "string"
139
+ },
140
+ {
141
+ "type": "null"
142
+ }
143
+ ],
144
+ "default": null,
145
+ "description": "URL to upstream provider's pricing page",
146
+ "title": "Reference"
147
+ },
148
+ "type": {
149
+ "const": "channel",
150
+ "default": "channel",
151
+ "title": "Type",
152
+ "type": "string"
153
+ }
154
+ },
155
+ "required": [
156
+ "default",
157
+ "channels"
158
+ ],
159
+ "title": "ChannelPriceData",
160
+ "type": "object"
161
+ },
90
162
  "ConstantPriceData": {
91
163
  "additionalProperties": false,
92
164
  "description": "Price data for a constant/fixed price.\n\nUsed for fixed fees, discounts, or adjustments that don't depend on usage.\nPrice can be positive (charge) or negative (discount/credit).",
@@ -834,78 +906,6 @@
834
906
  "title": "MinPriceData",
835
907
  "type": "object"
836
908
  },
837
- "ModePriceData": {
838
- "additionalProperties": false,
839
- "description": "Mode-based pricing that selects a sub-price by the request's resolved mode.\n\nThe active mode is decided elsewhere — e.g. the gateway picks which keyed\n``upstream_access_config`` a request routes through (``\"managed\"``,\n``\"byok\"``, ``\"byoe\"``, …) — and threads it into\n``calculate_cost(..., mode=...)``. The matching entry in ``modes`` is\nevaluated; when the mode is unknown or unset, the ``default`` mode is used.\n\nThere is no special-casing on the consumer side: every pricing type accepts\nan optional ``mode`` and a mode-independent price simply ignores it, so a\nplain price behaves identically for all modes. Mode-dependent pricing is\njust this one extra optional pricing type.\n\nEach ``modes`` value is itself any pricing object (flat, composite, tiered,\nor even nested mode-based), validated lazily — mirroring how tiered pricing\nholds per-tier prices.\n\nExample:\n {\n \"type\": \"mode\",\n \"default\": \"managed\",\n \"modes\": {\n \"managed\": {\"type\": \"one_million_tokens\", \"input\": \"0.50\", \"output\": \"1.50\"},\n \"byok\": {\"type\": \"constant\", \"price\": \"0\"}\n }\n }",
840
- "properties": {
841
- "default": {
842
- "description": "Mode key evaluated when the request's mode is unknown or unset. Must be one of the keys in `modes`.",
843
- "title": "Default",
844
- "type": "string"
845
- },
846
- "description": {
847
- "anyOf": [
848
- {
849
- "type": "string"
850
- },
851
- {
852
- "type": "null"
853
- }
854
- ],
855
- "default": null,
856
- "description": "Human-readable description of the pricing model",
857
- "title": "Description"
858
- },
859
- "modes": {
860
- "additionalProperties": {
861
- "additionalProperties": true,
862
- "type": "object"
863
- },
864
- "description": "Per-mode pricing keyed by mode name (e.g. 'managed', 'byok'). Each value is a nested pricing object of any type.",
865
- "minProperties": 1,
866
- "title": "Modes",
867
- "type": "object"
868
- },
869
- "price": {
870
- "anyOf": [
871
- {
872
- "type": "string"
873
- },
874
- {
875
- "type": "null"
876
- }
877
- ],
878
- "default": null,
879
- "description": "Summary price for marketplace comparison and sorting. For simple pricing types this is the billing rate. For composite types (add, multiply, tiered, graduated) this is a seller-provided nominal value for marketplace display.",
880
- "title": "Price"
881
- },
882
- "reference": {
883
- "anyOf": [
884
- {
885
- "type": "string"
886
- },
887
- {
888
- "type": "null"
889
- }
890
- ],
891
- "default": null,
892
- "description": "URL to upstream provider's pricing page",
893
- "title": "Reference"
894
- },
895
- "type": {
896
- "const": "mode",
897
- "default": "mode",
898
- "title": "Type",
899
- "type": "string"
900
- }
901
- },
902
- "required": [
903
- "default",
904
- "modes"
905
- ],
906
- "title": "ModePriceData",
907
- "type": "object"
908
- },
909
909
  "MultiplyPriceData": {
910
910
  "additionalProperties": false,
911
911
  "description": "Composite pricing that multiplies a base price by a factor.\n\nUseful for applying percentage-based adjustments to a base price.\n\nExample:\n {\n \"type\": \"multiply\",\n \"factor\": \"0.70\",\n \"base\": {\"type\": \"one_million_tokens\", \"input\": \"0.50\", \"output\": \"1.50\"}\n }",
@@ -1789,7 +1789,7 @@
1789
1789
  },
1790
1790
  "UpstreamAccessConfigData": {
1791
1791
  "additionalProperties": true,
1792
- "description": "Upstream (seller-facing) access config data.\n\nExtends AccessInterfaceData with extra=\"allow\" to support protocol-specific\nconfiguration fields (e.g., S3 bucket/region, SMTP host/port) that the\ngateway needs to reach the upstream service.",
1792
+ "description": "One upstream access channel — a named entry in ``upstream_access_config``.\n\nEach channel is a complete way for the gateway to reach the upstream: a wire\nprotocol (``access_method``), endpoint (``base_url``), credential (``api_key``),\nrouting key, and rate-limit/quality restrictions. A service may declare several\nchannels (keyed by free-form channel name, e.g. ``\"managed\"``, ``\"byok\"``,\n``\"managed-eu\"``); the gateway selects one per request.\n\nThe channel's *type* is derived from its credential/endpoint provenance:\n``managed`` (seller's key, ``${ secrets.* }``), ``byok`` (customer's key,\n``${ customer_secrets.* }``), or ``byoe`` (customer's key + customer-templated\n``base_url``).\n\nExtends AccessInterfaceData with extra=\"allow\" to support protocol-specific\nconfiguration fields (e.g., S3 bucket/region, SMTP host/port) that the\ngateway needs to reach the upstream service.",
1793
1793
  "properties": {
1794
1794
  "access_method": {
1795
1795
  "$ref": "#/$defs/AccessMethodEnum",
@@ -2014,6 +2014,7 @@
2014
2014
  "discriminator": {
2015
2015
  "mapping": {
2016
2016
  "add": "#/$defs/AddPriceData",
2017
+ "channel": "#/$defs/ChannelPriceData",
2017
2018
  "constant": "#/$defs/ConstantPriceData",
2018
2019
  "expr": "#/$defs/ExprPriceData",
2019
2020
  "first": "#/$defs/FirstPriceData",
@@ -2021,7 +2022,6 @@
2021
2022
  "image": "#/$defs/ImagePriceData",
2022
2023
  "max": "#/$defs/MaxPriceData",
2023
2024
  "min": "#/$defs/MinPriceData",
2024
- "mode": "#/$defs/ModePriceData",
2025
2025
  "multiply": "#/$defs/MultiplyPriceData",
2026
2026
  "one_byte": "#/$defs/DataPriceData",
2027
2027
  "one_day": "#/$defs/TimePriceData",
@@ -2093,7 +2093,7 @@
2093
2093
  "$ref": "#/$defs/ExprPriceData"
2094
2094
  },
2095
2095
  {
2096
- "$ref": "#/$defs/ModePriceData"
2096
+ "$ref": "#/$defs/ChannelPriceData"
2097
2097
  }
2098
2098
  ]
2099
2099
  },
@@ -2153,7 +2153,7 @@
2153
2153
  "additionalProperties": {
2154
2154
  "$ref": "#/$defs/UpstreamAccessConfigData"
2155
2155
  },
2156
- "description": "Upstream access interfaces, keyed by name",
2156
+ "description": "Upstream access channels, keyed by channel name",
2157
2157
  "title": "Upstream Access Config",
2158
2158
  "type": "object"
2159
2159
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitysvc-core
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Shared data models and validation helpers for UnitySVC packages
5
5
  Author-email: Bo Peng <bo.peng@unitysvc.com>
6
6
  Maintainer-email: Bo Peng <bo.peng@unitysvc.com>
@@ -1808,67 +1808,67 @@ class TestNominalPriceAutoCompute:
1808
1808
  assert pricing.price == "9.00"
1809
1809
 
1810
1810
 
1811
- class TestModePriceData:
1812
- """Tests for mode-based pricing (select a sub-price by resolved mode)."""
1811
+ class TestChannelPriceData:
1812
+ """Tests for channel-based pricing (select a sub-price by resolved channel)."""
1813
1813
 
1814
1814
  def _managed_byok(self) -> dict:
1815
1815
  """Canonical managed+byok: managed is paid, byok is free.
1816
1816
 
1817
- Uses ``constant`` sub-prices so the tests exercise mode *selection*
1817
+ Uses ``constant`` sub-prices so the tests exercise channel *selection*
1818
1818
  independently of any usage-metric math.
1819
1819
  """
1820
1820
  return {
1821
- "type": "mode",
1821
+ "type": "channel",
1822
1822
  "default": "managed",
1823
- "modes": {
1823
+ "channels": {
1824
1824
  "managed": {"type": "constant", "price": "6.00"},
1825
1825
  "byok": {"type": "constant", "price": "0"},
1826
1826
  },
1827
1827
  }
1828
1828
 
1829
- def test_selects_matching_mode(self) -> None:
1830
- """The resolved mode picks that mode's price."""
1829
+ def test_selects_matching_channel(self) -> None:
1830
+ """The resolved channel picks that channel's price."""
1831
1831
  pricing = validate_pricing(self._managed_byok())
1832
1832
  usage = UsageData()
1833
1833
 
1834
- assert pricing.calculate_cost(usage, mode="managed") == Decimal("6.00")
1835
- assert pricing.calculate_cost(usage, mode="byok") == Decimal("0")
1834
+ assert pricing.calculate_cost(usage, channel="managed") == Decimal("6.00")
1835
+ assert pricing.calculate_cost(usage, channel="byok") == Decimal("0")
1836
1836
 
1837
- def test_unknown_mode_falls_back_to_default(self) -> None:
1838
- """An unrecognised mode evaluates the default mode."""
1837
+ def test_unknown_channel_falls_back_to_default(self) -> None:
1838
+ """An unrecognised channel evaluates the default channel."""
1839
1839
  pricing = validate_pricing(self._managed_byok())
1840
1840
 
1841
- assert pricing.calculate_cost(UsageData(), mode="nonexistent") == Decimal("6.00")
1841
+ assert pricing.calculate_cost(UsageData(), channel="nonexistent") == Decimal("6.00")
1842
1842
 
1843
- def test_none_mode_falls_back_to_default(self) -> None:
1844
- """No mode (the common case for non-mode-aware callers) uses the default."""
1843
+ def test_none_channel_falls_back_to_default(self) -> None:
1844
+ """No channel (the common case for non-channel-aware callers) uses the default."""
1845
1845
  pricing = validate_pricing(self._managed_byok())
1846
1846
 
1847
1847
  assert pricing.calculate_cost(UsageData()) == Decimal("6.00")
1848
1848
 
1849
- def test_non_mode_price_ignores_mode(self) -> None:
1850
- """A plain (mode-independent) price yields the same cost for every mode."""
1849
+ def test_non_channel_price_ignores_channel(self) -> None:
1850
+ """A plain (channel-independent) price yields the same cost for every channel."""
1851
1851
  pricing = validate_pricing({"type": "constant", "price": "6.00"})
1852
1852
  usage = UsageData()
1853
1853
 
1854
1854
  base = pricing.calculate_cost(usage)
1855
- assert pricing.calculate_cost(usage, mode="byok") == base
1856
- assert pricing.calculate_cost(usage, mode="managed") == base
1855
+ assert pricing.calculate_cost(usage, channel="byok") == base
1856
+ assert pricing.calculate_cost(usage, channel="managed") == base
1857
1857
  assert base == Decimal("6.00")
1858
1858
 
1859
1859
  def test_default_must_exist(self) -> None:
1860
- """`default` must name one of the modes."""
1861
- with pytest.raises(ValueError, match="default mode"):
1860
+ """`default` must name one of the channels."""
1861
+ with pytest.raises(ValueError, match="default channel"):
1862
1862
  validate_pricing(
1863
1863
  {
1864
- "type": "mode",
1864
+ "type": "channel",
1865
1865
  "default": "missing",
1866
- "modes": {"managed": {"type": "constant", "price": "1"}},
1866
+ "channels": {"managed": {"type": "constant", "price": "1"}},
1867
1867
  }
1868
1868
  )
1869
1869
 
1870
- def test_nominal_price_from_default_mode(self) -> None:
1871
- """The summary price auto-computes from the default mode when unset."""
1870
+ def test_nominal_price_from_default_channel(self) -> None:
1871
+ """The summary price auto-computes from the default channel when unset."""
1872
1872
  pricing = validate_pricing(self._managed_byok())
1873
1873
  assert pricing.price == "6.00" # managed (default) summary price
1874
1874
 
@@ -1879,16 +1879,16 @@ class TestModePriceData:
1879
1879
  pricing = validate_pricing(data)
1880
1880
  assert pricing.price == "1.50"
1881
1881
 
1882
- def test_mode_threads_through_composite(self) -> None:
1883
- """`mode` propagates into nested children (e.g. a mode price inside `add`)."""
1882
+ def test_channel_threads_through_composite(self) -> None:
1883
+ """`channel` propagates into nested children (e.g. a channel price inside `add`)."""
1884
1884
  pricing = validate_pricing(
1885
1885
  {
1886
1886
  "type": "add",
1887
1887
  "prices": [
1888
1888
  {
1889
- "type": "mode",
1889
+ "type": "channel",
1890
1890
  "default": "managed",
1891
- "modes": {
1891
+ "channels": {
1892
1892
  "managed": {"type": "constant", "price": "10"},
1893
1893
  "byok": {"type": "constant", "price": "0"},
1894
1894
  },
@@ -1899,17 +1899,17 @@ class TestModePriceData:
1899
1899
  )
1900
1900
  usage = UsageData()
1901
1901
 
1902
- # byok: 0 (mode price) + 1.00 (fee); managed: 10 + 1.00
1903
- assert pricing.calculate_cost(usage, mode="byok") == Decimal("1.00")
1904
- assert pricing.calculate_cost(usage, mode="managed") == Decimal("11.00")
1902
+ # byok: 0 (channel price) + 1.00 (fee); managed: 10 + 1.00
1903
+ assert pricing.calculate_cost(usage, channel="byok") == Decimal("1.00")
1904
+ assert pricing.calculate_cost(usage, channel="managed") == Decimal("11.00")
1905
1905
 
1906
- def test_mode_price_can_nest_tiered(self) -> None:
1907
- """A mode's pricing may itself be any pricing type (here: tiered)."""
1906
+ def test_channel_price_can_nest_tiered(self) -> None:
1907
+ """A channel's pricing may itself be any pricing type (here: tiered)."""
1908
1908
  pricing = validate_pricing(
1909
1909
  {
1910
- "type": "mode",
1910
+ "type": "channel",
1911
1911
  "default": "managed",
1912
- "modes": {
1912
+ "channels": {
1913
1913
  "managed": {
1914
1914
  "type": "tiered",
1915
1915
  "based_on": "request_count",
@@ -1924,6 +1924,6 @@ class TestModePriceData:
1924
1924
  )
1925
1925
  usage = UsageData()
1926
1926
 
1927
- assert pricing.calculate_cost(usage, request_count=10, mode="managed") == Decimal("5")
1928
- assert pricing.calculate_cost(usage, request_count=10_000, mode="managed") == Decimal("50")
1929
- assert pricing.calculate_cost(usage, request_count=10_000, mode="byok") == Decimal("0")
1927
+ assert pricing.calculate_cost(usage, request_count=10, channel="managed") == Decimal("5")
1928
+ assert pricing.calculate_cost(usage, request_count=10_000, channel="managed") == Decimal("50")
1929
+ assert pricing.calculate_cost(usage, request_count=10_000, channel="byok") == Decimal("0")
File without changes
File without changes
File without changes