charli3_dendrite 1.4.17.dev0__tar.gz → 1.5.0.dev1__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 (92) hide show
  1. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/PKG-INFO +1 -1
  2. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/pyproject.toml +2 -2
  3. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dataclasses/models.py +167 -33
  4. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/LICENSE +0 -0
  5. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/README.md +0 -0
  6. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/__init__.py +0 -0
  7. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/backend/__init__.py +0 -0
  8. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/backend/backend_base.py +0 -0
  9. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/backend/blockfrost/__init__.py +0 -0
  10. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/backend/blockfrost/models.py +0 -0
  11. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/backend/dbsync/__init__.py +0 -0
  12. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/backend/dbsync/models.py +0 -0
  13. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/backend/ogmios_kupo/__init__.py +0 -0
  14. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/backend/ogmios_kupo/models.py +0 -0
  15. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/backend/utils.py +0 -0
  16. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dataclasses/__init__.py +0 -0
  17. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dataclasses/datums.py +0 -0
  18. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/__init__.py +0 -0
  19. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/__init__.py +0 -0
  20. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/amm_base.py +0 -0
  21. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/amm_types.py +0 -0
  22. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/cswap.py +0 -0
  23. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/dano.py +0 -0
  24. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/minswap.py +0 -0
  25. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/muesli.py +0 -0
  26. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/spectrum.py +0 -0
  27. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/splash.py +0 -0
  28. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/sundae.py +0 -0
  29. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/vyfi.py +0 -0
  30. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/amm/wingriders.py +0 -0
  31. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/core/__init__.py +0 -0
  32. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/core/base.py +0 -0
  33. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/core/errors.py +0 -0
  34. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/ob/__init__.py +0 -0
  35. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/ob/axo.py +0 -0
  36. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/ob/cardanoswaps.py +0 -0
  37. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/ob/chadswap.py +0 -0
  38. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/ob/djed.py +0 -0
  39. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/ob/geniusyield.py +0 -0
  40. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/ob/ob_base.py +0 -0
  41. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/dexs/ob/saturnswap.py +0 -0
  42. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/__init__.py +0 -0
  43. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/base.py +0 -0
  44. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/__init__.py +0 -0
  45. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/constants.py +0 -0
  46. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/datums.py +0 -0
  47. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/loader.py +0 -0
  48. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/market.py +0 -0
  49. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/math.py +0 -0
  50. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/__init__.py +0 -0
  51. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/aggregator.py +0 -0
  52. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/aggregator_datums.py +0 -0
  53. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/deployments.py +0 -0
  54. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/forward.py +0 -0
  55. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/leaves.py +0 -0
  56. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/locator.py +0 -0
  57. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/lp_pricing.py +0 -0
  58. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/recipes.json +0 -0
  59. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/redeemer.py +0 -0
  60. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/reproduce.py +0 -0
  61. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/oracles/structured_datums.py +0 -0
  62. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/state.py +0 -0
  63. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/__init__.py +0 -0
  64. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/_common.py +0 -0
  65. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/build.py +0 -0
  66. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/builder.py +0 -0
  67. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/context.py +0 -0
  68. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/create_loan.py +0 -0
  69. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/datum_synth.py +0 -0
  70. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/increase_loan.py +0 -0
  71. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/loan_ids.py +0 -0
  72. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/modify_collateral.py +0 -0
  73. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/oracle_redeemer.py +0 -0
  74. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/oracle_synth.py +0 -0
  75. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/redeemers.py +0 -0
  76. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/repay.py +0 -0
  77. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/topup_withdraw.py +0 -0
  78. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/danogo/transactions/verify.py +0 -0
  79. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/math.py +0 -0
  80. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/oracles/__init__.py +0 -0
  81. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/oracles/charli3.py +0 -0
  82. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/oracles/dex_pooled.py +0 -0
  83. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/oracles/fluid.py +0 -0
  84. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/oracles/models.py +0 -0
  85. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/oracles/orcfax.py +0 -0
  86. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/oracles/qtoken.py +0 -0
  87. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/registry.py +0 -0
  88. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/transactions/__init__.py +0 -0
  89. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/transactions/base.py +0 -0
  90. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/transactions/infra.py +0 -0
  91. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/lending/transactions/snapshot.py +0 -0
  92. {charli3_dendrite-1.4.17.dev0 → charli3_dendrite-1.5.0.dev1}/src/charli3_dendrite/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: charli3_dendrite
3
- Version: 1.4.17.dev0
3
+ Version: 1.5.0.dev1
4
4
  Summary:
5
5
  License-File: LICENSE
6
6
  Author: Elder Millenial
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "charli3_dendrite"
3
- version = "1.4.17-dev0"
3
+ version = "1.5.0-dev1"
4
4
  description = ""
5
5
  authors = ["Elder Millenial <eldermillenial@protonmail.com>"]
6
6
  readme = "README.md"
@@ -50,7 +50,7 @@ module = ["pycardano", "pycardano.*", "dotenv", "nox", "uplc", "uplc.*"]
50
50
  ignore_missing_imports = true
51
51
 
52
52
  [tool.bumpversion]
53
- current_version = "1.4.17-dev0"
53
+ current_version = "1.5.0-dev1"
54
54
  parse = """(?x)
55
55
  (?P<major>\\d+)\\.
56
56
  (?P<minor>\\d+)\\.
@@ -1,4 +1,5 @@
1
1
  # noqa
2
+ import json
2
3
  from enum import Enum
3
4
 
4
5
  from pycardano import Address
@@ -16,6 +17,7 @@ from pydantic import RootModel
16
17
  from pydantic import model_serializer
17
18
  from pydantic import model_validator
18
19
  from pydantic.alias_generators import to_camel
20
+ from pydantic_core import core_schema
19
21
 
20
22
 
21
23
  class DendriteBaseModel(BaseModel):
@@ -80,52 +82,184 @@ class BaseDict(BaseList):
80
82
  return self.root.get(item, 0)
81
83
 
82
84
 
83
- class Assets(BaseDict):
84
- """Contains all tokens and quantities."""
85
+ def _digest_assets(value: object) -> dict[str, int]:
86
+ """Normalize an ``Assets`` input to a lovelace-first ``dict[str, int]``.
85
87
 
86
- root: dict[str, int]
88
+ Accepts the same shapes the old pydantic ``_digest_assets`` before-validator did:
89
+ another ``Assets`` / anything with ``.root``, a ``{"values": [...]}`` mapping of
90
+ objects with ``unit``/``quantity``, a list of length-1 dicts, a plain dict, or
91
+ kwargs (an items-able).
92
+ """
93
+ if hasattr(value, "root"):
94
+ root = dict(value.root)
95
+ elif (
96
+ isinstance(value, dict)
97
+ and "values" in value
98
+ and isinstance(value["values"], list)
99
+ ):
100
+ root = {v.unit: v.quantity for v in value["values"]}
101
+ elif isinstance(value, list) and value and isinstance(value[0], dict):
102
+ if not all(len(v) == 1 for v in value):
103
+ raise ValueError(
104
+ "For a list of dictionaries, each dictionary must be of length 1.",
105
+ )
106
+ root = {k: v for d in value for k, v in d.items()}
107
+ elif isinstance(value, dict):
108
+ root = dict(value)
109
+ else:
110
+ root = dict(value.items()) # type: ignore[attr-defined]
111
+ return dict(sorted(root.items(), key=lambda x: "" if x[0] == "lovelace" else x[0]))
112
+
113
+
114
+ class Assets:
115
+ """Contains all tokens and quantities.
116
+
117
+ A plain (non-pydantic) ``unit -> quantity`` mapping with lovelace-first ordering.
118
+ ``.root`` is a live, mutable, insertion-ordered ``dict``: the pool math mutates it
119
+ in place (``apply_swap``, finite-difference probes, and the non-ADA min-ADA append
120
+ that must NOT re-sort), so it stays a real dict rather than an opaque model field.
121
+ Dropping the pydantic ``RootModel`` removes the per-construction validation +
122
+ accessor overhead that dominates tight construction/access loops;
123
+ ``__get_pydantic_core_schema__`` keeps it usable as a field type on the pydantic
124
+ pool-state models.
125
+ """
126
+
127
+ __slots__ = ("root",)
128
+
129
+ def __init__(self, *args: object, **kwargs: object) -> None:
130
+ """Build from a positional value, a ``root=`` dict, or unit kwargs."""
131
+ if args:
132
+ value: object = args[0]
133
+ elif len(kwargs) == 1 and "root" in kwargs:
134
+ value = kwargs["root"]
135
+ else:
136
+ value = kwargs
137
+ self.root: dict[str, int] = _digest_assets(value)
138
+
139
+ def items(self): # noqa: ANN201
140
+ """Return iterable of key-value pairs."""
141
+ return self.root.items()
142
+
143
+ def keys(self): # noqa: ANN201
144
+ """Return iterable of keys."""
145
+ return self.root.keys()
146
+
147
+ def values(self): # noqa: ANN201
148
+ """Return iterable of values."""
149
+ return self.root.values()
150
+
151
+ def __iter__(self): # noqa: ANN204
152
+ """Iterate over the units (keys)."""
153
+ return iter(self.root)
154
+
155
+ def __getitem__(self, item: str) -> int:
156
+ """Get quantity by unit (missing -> 0)."""
157
+ return self.root.get(item, 0)
158
+
159
+ def __len__(self) -> int:
160
+ """Number of distinct units."""
161
+ return len(self.root)
162
+
163
+ def __contains__(self, item: str) -> bool:
164
+ """Whether ``item`` is a present unit."""
165
+ return item in self.root
87
166
 
88
167
  def unit(self, index: int = 0) -> str:
89
168
  """Units of asset at `index`."""
90
- return list(self.keys())[index]
169
+ return next(iter(self.root)) if index == 0 else list(self.root)[index]
91
170
 
92
171
  def quantity(self, index: int = 0) -> int:
93
172
  """Quantity of the asset at `index`."""
94
- return list(self.values())[index]
173
+ if index == 0:
174
+ return next(iter(self.root.values()))
175
+ return list(self.root.values())[index]
95
176
 
96
- @model_validator(mode="before")
97
- def _digest_assets(cls, values: dict) -> dict:
98
- if hasattr(values, "root"):
99
- root = values.root
100
- elif "values" in values and isinstance(values["values"], list):
101
- root = {v.unit: v.quantity for v in values["values"]}
102
- elif isinstance(values, list) and isinstance(values[0], dict):
103
- if not all(len(v) == 1 for v in values):
104
- raise ValueError(
105
- "For a list of dictionaries, each dictionary must be of length 1.",
106
- )
107
- root = {k: v for d in values for k, v in d.items()}
108
- else:
109
- root = dict(values.items())
110
- return dict(
111
- sorted(root.items(), key=lambda x: "" if x[0] == "lovelace" else x[0]),
112
- )
113
-
114
- def __add__(a: "Assets", b: "Assets") -> "Assets":
177
+ def __add__(self, b: "Assets") -> "Assets":
115
178
  """Add two assets."""
116
- intersection = set(a.keys()) | set(b.keys())
117
-
118
- result = {key: a[key] + b[key] for key in intersection}
179
+ keys = set(self.keys()) | set(b.keys())
180
+ return Assets(**{key: self[key] + b[key] for key in keys})
119
181
 
120
- return Assets(**result)
121
-
122
- def __sub__(a: "Assets", b: "Assets") -> "Assets":
182
+ def __sub__(self, b: "Assets") -> "Assets":
123
183
  """Subtract two assets."""
124
- intersection = set(a.keys()) | set(b.keys())
184
+ keys = set(self.keys()) | set(b.keys())
185
+ return Assets(**{key: self[key] - b[key] for key in keys})
186
+
187
+ def __eq__(self, other: object) -> bool:
188
+ """Equal iff the same ``unit -> quantity`` mapping."""
189
+ if isinstance(other, Assets):
190
+ return self.root == other.root
191
+ return NotImplemented
125
192
 
126
- result = {key: a[key] - b[key] for key in intersection}
193
+ def __hash__(self) -> int:
194
+ """Hash of the canonical ``(unit, quantity)`` items."""
195
+ return hash(tuple(self.root.items()))
196
+
197
+ def __repr__(self) -> str:
198
+ """Debug representation."""
199
+ return f"Assets(root={self.root!r})"
200
+
201
+ # --- pydantic-model API surface the pool code calls (no pydantic engine) ---
202
+ def model_dump(self, **_kwargs: object) -> dict[str, int]:
203
+ """Return the ``unit -> quantity`` dict (mirrors ``RootModel.model_dump``)."""
204
+ return dict(self.root)
205
+
206
+ def model_dump_json(self, **_kwargs: object) -> str:
207
+ """Return the mapping as a compact JSON object string."""
208
+ return json.dumps(self.root, separators=(",", ":"))
209
+
210
+ def copy(self, **_kwargs: object) -> "Assets":
211
+ """Return an independent copy (fresh dict, order preserved)."""
212
+ return self.model_construct(root=dict(self.root))
213
+
214
+ def model_copy(self, **_kwargs: object) -> "Assets":
215
+ """Return an independent copy (fresh dict, order preserved)."""
216
+ return self.model_construct(root=dict(self.root))
217
+
218
+ @classmethod
219
+ def model_validate(cls, obj: object, **_kwargs: object) -> "Assets":
220
+ """Coerce ``obj`` to ``Assets`` (an existing instance passes through)."""
221
+ return obj if isinstance(obj, cls) else cls(obj)
222
+
223
+ @classmethod
224
+ def model_validate_json(cls, data: str, **_kwargs: object) -> "Assets":
225
+ """Parse a JSON object string into ``Assets``."""
226
+ return cls(json.loads(data))
227
+
228
+ @classmethod
229
+ def model_construct(
230
+ cls,
231
+ root: dict[str, int] | None = None,
232
+ **kwargs: int,
233
+ ) -> "Assets":
234
+ """No-sort, no-validate construction (mirrors pydantic ``model_construct``).
235
+
236
+ Callers pass an already-canonical ``root`` (e.g. ``reset_assets`` in the hot
237
+ path), so this must NOT re-sort — it sets ``.root`` verbatim.
238
+ """
239
+ obj = cls.__new__(cls)
240
+ obj.root = root if root is not None else dict(kwargs)
241
+ return obj
242
+
243
+ # --- pydantic field-type protocol (models with `assets: Assets` still validate) ---
244
+ @classmethod
245
+ def __get_pydantic_core_schema__(
246
+ cls,
247
+ _source: object,
248
+ _handler: object,
249
+ ) -> object:
250
+ """Validate/serialize as a plain dict inside pydantic models."""
251
+ return core_schema.no_info_plain_validator_function(
252
+ cls._pyd_validate,
253
+ serialization=core_schema.plain_serializer_function_ser_schema(
254
+ lambda a: dict(a.root),
255
+ when_used="always",
256
+ ),
257
+ )
127
258
 
128
- return Assets(**result)
259
+ @classmethod
260
+ def _pyd_validate(cls, value: object) -> "Assets":
261
+ """Coerce a validation input to ``Assets`` (instance passthrough)."""
262
+ return value if isinstance(value, cls) else cls(value)
129
263
 
130
264
 
131
265
  class ScriptReference(DendriteBaseModel):