arkhai-vms 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.
- arkhai_vms-0.2.0/.gitignore +232 -0
- arkhai_vms-0.2.0/PKG-INFO +10 -0
- arkhai_vms-0.2.0/pyproject.toml +34 -0
- arkhai_vms-0.2.0/src/arkhai_vms/__init__.py +51 -0
- arkhai_vms-0.2.0/src/arkhai_vms/compute_requirements.py +25 -0
- arkhai_vms-0.2.0/src/arkhai_vms/domain_runtime.py +77 -0
- arkhai_vms-0.2.0/src/arkhai_vms/provision_terms.py +133 -0
- arkhai_vms-0.2.0/src/arkhai_vms/schema.py +170 -0
- arkhai_vms-0.2.0/src/arkhai_vms/storefront_adapter.py +104 -0
- arkhai_vms-0.2.0/tests/test_compute_requirements.py +10 -0
- arkhai_vms-0.2.0/tests/test_domain_runtime.py +92 -0
- arkhai_vms-0.2.0/tests/test_schema.py +100 -0
- arkhai_vms-0.2.0/tests/test_storefront_adapter.py +137 -0
- arkhai_vms-0.2.0/uv.lock +1449 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
.vscode/
|
|
2
|
+
|
|
3
|
+
# Project-local scratch files, private notes, and generated diagnostics.
|
|
4
|
+
.scm-local/
|
|
5
|
+
|
|
6
|
+
# Visible transfer staging for snap-confined tools such as Multipass.
|
|
7
|
+
scm-clean-room-transfer/
|
|
8
|
+
|
|
9
|
+
# Legacy local rehearsal params (private keys, RPC URLs, etc.).
|
|
10
|
+
.rehearsal-params.local.md
|
|
11
|
+
.rehearsal-params.local.*
|
|
12
|
+
|
|
13
|
+
### Python ###
|
|
14
|
+
# Byte-compiled / optimized / DLL files
|
|
15
|
+
__pycache__/
|
|
16
|
+
*.py[cod]
|
|
17
|
+
*$py.class
|
|
18
|
+
|
|
19
|
+
# C extensions
|
|
20
|
+
*.so
|
|
21
|
+
|
|
22
|
+
# Distribution / packaging
|
|
23
|
+
.Python
|
|
24
|
+
build/
|
|
25
|
+
develop-eggs/
|
|
26
|
+
dist/
|
|
27
|
+
downloads/
|
|
28
|
+
eggs/
|
|
29
|
+
.eggs/
|
|
30
|
+
lib/
|
|
31
|
+
lib64/
|
|
32
|
+
parts/
|
|
33
|
+
sdist/
|
|
34
|
+
var/
|
|
35
|
+
wheels/
|
|
36
|
+
share/python-wheels/
|
|
37
|
+
*.egg-info/
|
|
38
|
+
.installed.cfg
|
|
39
|
+
*.egg
|
|
40
|
+
MANIFEST
|
|
41
|
+
|
|
42
|
+
# PyInstaller
|
|
43
|
+
# Usually these files are written by a python script from a template
|
|
44
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
45
|
+
*.manifest
|
|
46
|
+
*.spec
|
|
47
|
+
|
|
48
|
+
# Installer logs
|
|
49
|
+
pip-log.txt
|
|
50
|
+
pip-delete-this-directory.txt
|
|
51
|
+
|
|
52
|
+
# Unit test / coverage reports
|
|
53
|
+
htmlcov/
|
|
54
|
+
.tox/
|
|
55
|
+
.nox/
|
|
56
|
+
.coverage
|
|
57
|
+
.coverage.*
|
|
58
|
+
.cache
|
|
59
|
+
nosetests.xml
|
|
60
|
+
coverage.xml
|
|
61
|
+
*.cover
|
|
62
|
+
*.py,cover
|
|
63
|
+
.hypothesis/
|
|
64
|
+
.pytest_cache/
|
|
65
|
+
cover/
|
|
66
|
+
|
|
67
|
+
# Translations
|
|
68
|
+
*.mo
|
|
69
|
+
*.pot
|
|
70
|
+
|
|
71
|
+
# Django stuff:
|
|
72
|
+
*.log
|
|
73
|
+
*.log.[0-9]*
|
|
74
|
+
local_settings.py
|
|
75
|
+
db.sqlite3
|
|
76
|
+
db.sqlite3-journal
|
|
77
|
+
|
|
78
|
+
# Flask stuff:
|
|
79
|
+
instance/
|
|
80
|
+
.webassets-cache
|
|
81
|
+
|
|
82
|
+
# Scrapy stuff:
|
|
83
|
+
.scrapy
|
|
84
|
+
|
|
85
|
+
# Sphinx documentation
|
|
86
|
+
docs/_build/
|
|
87
|
+
|
|
88
|
+
# PyBuilder
|
|
89
|
+
.pybuilder/
|
|
90
|
+
target/
|
|
91
|
+
|
|
92
|
+
# Jupyter Notebook
|
|
93
|
+
.ipynb_checkpoints
|
|
94
|
+
|
|
95
|
+
# IPython
|
|
96
|
+
profile_default/
|
|
97
|
+
ipython_config.py
|
|
98
|
+
|
|
99
|
+
# pyenv
|
|
100
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
101
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
102
|
+
# .python-version
|
|
103
|
+
|
|
104
|
+
# pipenv
|
|
105
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
106
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
107
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
108
|
+
# install all needed dependencies.
|
|
109
|
+
#Pipfile.lock
|
|
110
|
+
|
|
111
|
+
# poetry
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
113
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
114
|
+
# commonly ignored for libraries.
|
|
115
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
116
|
+
#poetry.lock
|
|
117
|
+
|
|
118
|
+
# pdm
|
|
119
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
120
|
+
#pdm.lock
|
|
121
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
122
|
+
# in version control.
|
|
123
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
124
|
+
.pdm.toml
|
|
125
|
+
|
|
126
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
127
|
+
__pypackages__/
|
|
128
|
+
|
|
129
|
+
# Celery stuff
|
|
130
|
+
celerybeat-schedule
|
|
131
|
+
celerybeat.pid
|
|
132
|
+
|
|
133
|
+
# SageMath parsed files
|
|
134
|
+
*.sage.py
|
|
135
|
+
|
|
136
|
+
# Environments
|
|
137
|
+
.env
|
|
138
|
+
.env.*
|
|
139
|
+
.venv
|
|
140
|
+
env/
|
|
141
|
+
venv/
|
|
142
|
+
ENV/
|
|
143
|
+
env.bak/
|
|
144
|
+
venv.bak/
|
|
145
|
+
|
|
146
|
+
# Spyder project settings
|
|
147
|
+
.spyderproject
|
|
148
|
+
.spyproject
|
|
149
|
+
|
|
150
|
+
# Rope project settings
|
|
151
|
+
.ropeproject
|
|
152
|
+
|
|
153
|
+
# mkdocs documentation
|
|
154
|
+
/site
|
|
155
|
+
|
|
156
|
+
# mypy
|
|
157
|
+
.mypy_cache/
|
|
158
|
+
.dmypy.json
|
|
159
|
+
dmypy.json
|
|
160
|
+
|
|
161
|
+
# Pyre type checker
|
|
162
|
+
.pyre/
|
|
163
|
+
|
|
164
|
+
# pytype static type analyzer
|
|
165
|
+
.pytype/
|
|
166
|
+
|
|
167
|
+
# Cython debug symbols
|
|
168
|
+
cython_debug/
|
|
169
|
+
|
|
170
|
+
# PyCharm
|
|
171
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
172
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
173
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
174
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
175
|
+
#.idea/
|
|
176
|
+
|
|
177
|
+
### Python Patch ###
|
|
178
|
+
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
|
179
|
+
poetry.toml
|
|
180
|
+
|
|
181
|
+
# ruff
|
|
182
|
+
.ruff_cache/
|
|
183
|
+
|
|
184
|
+
# LSP config files
|
|
185
|
+
pyrightconfig.json
|
|
186
|
+
|
|
187
|
+
# Infra
|
|
188
|
+
infra/*/*.tfvars
|
|
189
|
+
infra/*/*.tfstate
|
|
190
|
+
infra/*/*.tfstate.backup
|
|
191
|
+
infra/*/.terraform*
|
|
192
|
+
infra/*/*.plan
|
|
193
|
+
*.db
|
|
194
|
+
# PufferRL Experiments
|
|
195
|
+
experiments/
|
|
196
|
+
wandb/
|
|
197
|
+
!.env.sample
|
|
198
|
+
!.env.docker-compose
|
|
199
|
+
!.env.*.docker-compose
|
|
200
|
+
!.env.*.docker-compose
|
|
201
|
+
!.env.docker-compose
|
|
202
|
+
.cursor/
|
|
203
|
+
|
|
204
|
+
# Cloud Function
|
|
205
|
+
node_modules
|
|
206
|
+
|
|
207
|
+
# Puffer artifacts — ignore any resources/ dir or symlink at any depth
|
|
208
|
+
**/resources
|
|
209
|
+
pr-message-puffer.md
|
|
210
|
+
dev-env/state/state.json
|
|
211
|
+
shared-env/*
|
|
212
|
+
helm/Chart.lock
|
|
213
|
+
helm/charts/*.tgz
|
|
214
|
+
helm/.port-forward.pids
|
|
215
|
+
provisioning-service/settings.local.toml
|
|
216
|
+
provisioning-service/src/config/config-local.yml
|
|
217
|
+
.snapshot/*
|
|
218
|
+
|
|
219
|
+
# Per-env deployment scratch dirs (carry keys + per-env configs)
|
|
220
|
+
deploy-base-sepolia/
|
|
221
|
+
|
|
222
|
+
# Claude Code per-project state; shared skills are repository-owned.
|
|
223
|
+
.claude/*
|
|
224
|
+
!.claude/skills/
|
|
225
|
+
!.claude/skills/**
|
|
226
|
+
|
|
227
|
+
# Local assistant session exports
|
|
228
|
+
omp-session-*.html
|
|
229
|
+
pi-session-*.html
|
|
230
|
+
|
|
231
|
+
# Local-only mise overrides (machine-specific paths).
|
|
232
|
+
mise.local.toml
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: arkhai-vms
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Shared VM-domain models and helpers used by Arkhai VM buyer and storefront packages.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: <3.14,>=3.12
|
|
7
|
+
Requires-Dist: arkhai-core>=0.2.0
|
|
8
|
+
Requires-Dist: pydantic>=2
|
|
9
|
+
Provides-Extra: storefront
|
|
10
|
+
Requires-Dist: arkhai-core-storefront>=0.3.0; extra == 'storefront'
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "arkhai-vms"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
license = "MIT"
|
|
5
|
+
description = "Shared VM-domain models and helpers used by Arkhai VM buyer and storefront packages."
|
|
6
|
+
requires-python = ">=3.12,<3.14"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"pydantic>=2",
|
|
9
|
+
"arkhai-core>=0.2.0",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[project.optional-dependencies]
|
|
13
|
+
storefront = [
|
|
14
|
+
"arkhai-core-storefront>=0.3.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.entry-points."market.storefront_domains"]
|
|
18
|
+
vms = "arkhai_vms.domain_runtime:VM_MARKET_DOMAIN"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/arkhai_vms"]
|
|
26
|
+
|
|
27
|
+
[tool.pytest.ini_options]
|
|
28
|
+
pythonpath = ["src"]
|
|
29
|
+
|
|
30
|
+
[dependency-groups]
|
|
31
|
+
dev = [
|
|
32
|
+
"arkhai-core-storefront>=0.3.0",
|
|
33
|
+
"pytest>=8.0",
|
|
34
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Shared VM-domain helpers used across buyer and storefront packages."""
|
|
2
|
+
|
|
3
|
+
from arkhai_vms.compute_requirements import (
|
|
4
|
+
DIMENSION_KEYS,
|
|
5
|
+
DISK_GB_DIMENSION,
|
|
6
|
+
GPU_COUNT_DIMENSION,
|
|
7
|
+
RAM_GB_DIMENSION,
|
|
8
|
+
VCPU_COUNT_DIMENSION,
|
|
9
|
+
)
|
|
10
|
+
from arkhai_vms.provision_terms import (
|
|
11
|
+
VM_PROVISION_KIND,
|
|
12
|
+
VmProvisionTerms,
|
|
13
|
+
make_vm_provision_terms,
|
|
14
|
+
normalize_vm_provision_terms,
|
|
15
|
+
provision_compute_resource,
|
|
16
|
+
provision_duration_seconds,
|
|
17
|
+
provision_payload,
|
|
18
|
+
provision_ssh_public_key,
|
|
19
|
+
provision_start_utc,
|
|
20
|
+
)
|
|
21
|
+
from arkhai_vms.schema import (
|
|
22
|
+
VmListing,
|
|
23
|
+
VmMaterialization,
|
|
24
|
+
VmMessage,
|
|
25
|
+
VmReceipt,
|
|
26
|
+
VmResult,
|
|
27
|
+
VmTerms,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"DIMENSION_KEYS",
|
|
32
|
+
"DISK_GB_DIMENSION",
|
|
33
|
+
"GPU_COUNT_DIMENSION",
|
|
34
|
+
"RAM_GB_DIMENSION",
|
|
35
|
+
"VCPU_COUNT_DIMENSION",
|
|
36
|
+
"VM_PROVISION_KIND",
|
|
37
|
+
"VmListing",
|
|
38
|
+
"VmMaterialization",
|
|
39
|
+
"VmMessage",
|
|
40
|
+
"VmProvisionTerms",
|
|
41
|
+
"VmReceipt",
|
|
42
|
+
"VmResult",
|
|
43
|
+
"VmTerms",
|
|
44
|
+
"make_vm_provision_terms",
|
|
45
|
+
"normalize_vm_provision_terms",
|
|
46
|
+
"provision_compute_resource",
|
|
47
|
+
"provision_duration_seconds",
|
|
48
|
+
"provision_payload",
|
|
49
|
+
"provision_ssh_public_key",
|
|
50
|
+
"provision_start_utc",
|
|
51
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Canonical VM capacity-dimension vocabulary.
|
|
2
|
+
|
|
3
|
+
These names and units are part of the VM domain contract. Translation into a
|
|
4
|
+
specific provisioning playbook belongs to that provider's requirement delegate.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
GPU_COUNT_DIMENSION = "gpu_count"
|
|
8
|
+
VCPU_COUNT_DIMENSION = "vcpu_count"
|
|
9
|
+
RAM_GB_DIMENSION = "ram_gb"
|
|
10
|
+
DISK_GB_DIMENSION = "disk_gb"
|
|
11
|
+
|
|
12
|
+
DIMENSION_KEYS: tuple[str, ...] = (
|
|
13
|
+
GPU_COUNT_DIMENSION,
|
|
14
|
+
VCPU_COUNT_DIMENSION,
|
|
15
|
+
RAM_GB_DIMENSION,
|
|
16
|
+
DISK_GB_DIMENSION,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"DIMENSION_KEYS",
|
|
21
|
+
"DISK_GB_DIMENSION",
|
|
22
|
+
"GPU_COUNT_DIMENSION",
|
|
23
|
+
"RAM_GB_DIMENSION",
|
|
24
|
+
"VCPU_COUNT_DIMENSION",
|
|
25
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""VM implementation of the core market-domain contract."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from market_core import (
|
|
8
|
+
MARKET_DOMAIN_CONTRACT_VERSION,
|
|
9
|
+
DomainCapability,
|
|
10
|
+
DomainIdentity,
|
|
11
|
+
ImmutableCodecCapability,
|
|
12
|
+
ImmutablePublicationCapability,
|
|
13
|
+
MarketDomainContract,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .provision_terms import VM_PROVISION_KIND
|
|
17
|
+
from .schema import (
|
|
18
|
+
VmListing,
|
|
19
|
+
VmMaterialization,
|
|
20
|
+
VmMessage,
|
|
21
|
+
VmReceipt,
|
|
22
|
+
VmResult,
|
|
23
|
+
VmTerms,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _normalize_listing(value: Any) -> VmListing:
|
|
28
|
+
return VmListing.model_validate(value)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _normalize_message(value: Any) -> VmMessage:
|
|
32
|
+
return VmMessage.model_validate(value)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _normalize_terms(value: Any) -> VmTerms:
|
|
36
|
+
return VmTerms.model_validate(value)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _normalize_materialization(value: Any) -> VmMaterialization:
|
|
40
|
+
return VmMaterialization.model_validate(value)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _normalize_receipt(value: Any) -> VmReceipt:
|
|
44
|
+
return VmReceipt.model_validate(value)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _normalize_result(value: Any) -> VmResult:
|
|
48
|
+
return VmResult.model_validate(value)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _publication_source(**kwargs: Any) -> Any:
|
|
52
|
+
from .storefront_adapter import vm_publication_adapter
|
|
53
|
+
|
|
54
|
+
return vm_publication_adapter(**kwargs)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
VM_MARKET_DOMAIN = MarketDomainContract(
|
|
58
|
+
identity=DomainIdentity(VM_PROVISION_KIND),
|
|
59
|
+
contract_version=MARKET_DOMAIN_CONTRACT_VERSION,
|
|
60
|
+
codecs=ImmutableCodecCapability(
|
|
61
|
+
normalize_listing=_normalize_listing,
|
|
62
|
+
normalize_message=_normalize_message,
|
|
63
|
+
normalize_terms=_normalize_terms,
|
|
64
|
+
normalize_materialization=_normalize_materialization,
|
|
65
|
+
normalize_receipt=_normalize_receipt,
|
|
66
|
+
normalize_result=_normalize_result,
|
|
67
|
+
),
|
|
68
|
+
declared_capabilities=frozenset({DomainCapability.PUBLICATION}),
|
|
69
|
+
publication=ImmutablePublicationCapability(
|
|
70
|
+
source_factory=_publication_source,
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def market_domain() -> MarketDomainContract:
|
|
76
|
+
"""Return the VM market-domain contract."""
|
|
77
|
+
return VM_MARKET_DOMAIN
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""VM provision-term construction."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
VM_PROVISION_KIND = "compute.v1"
|
|
9
|
+
VM_PROVISION_VERSION = 1
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class VmProvisionPayload(BaseModel):
|
|
13
|
+
"""Domain-owned payload schema for VM provision envelope version 1."""
|
|
14
|
+
|
|
15
|
+
model_config = ConfigDict(extra="forbid")
|
|
16
|
+
|
|
17
|
+
duration_seconds: int = Field(gt=0)
|
|
18
|
+
ssh_public_key: str = ""
|
|
19
|
+
start_utc: str | None = None
|
|
20
|
+
compute_resource: dict[str, Any] | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class VmProvisionTerms(BaseModel):
|
|
24
|
+
"""VM provision envelope with independently versioned payload."""
|
|
25
|
+
|
|
26
|
+
model_config = ConfigDict(extra="forbid")
|
|
27
|
+
|
|
28
|
+
kind: Literal["compute.v1"] = VM_PROVISION_KIND
|
|
29
|
+
version: Literal[1] = VM_PROVISION_VERSION
|
|
30
|
+
payload: dict[str, Any]
|
|
31
|
+
|
|
32
|
+
@field_validator("payload")
|
|
33
|
+
@classmethod
|
|
34
|
+
def _validate_payload(cls, value: dict[str, Any]) -> dict[str, Any]:
|
|
35
|
+
return VmProvisionPayload.model_validate(value).model_dump(
|
|
36
|
+
exclude_none=True,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def duration_seconds(self) -> int | None:
|
|
41
|
+
raw = self.payload.get("duration_seconds")
|
|
42
|
+
return int(raw) if raw is not None else None
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def start_utc(self) -> str | None:
|
|
46
|
+
raw = self.payload.get("start_utc")
|
|
47
|
+
if raw is None:
|
|
48
|
+
return None
|
|
49
|
+
text = str(raw).strip()
|
|
50
|
+
return text or None
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def ssh_public_key(self) -> str:
|
|
54
|
+
raw = self.payload.get("ssh_public_key")
|
|
55
|
+
return raw if isinstance(raw, str) else ""
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def compute_resource(self) -> dict[str, Any] | None:
|
|
59
|
+
raw = self.payload.get("compute_resource")
|
|
60
|
+
return raw if isinstance(raw, dict) else None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def make_vm_provision_terms(
|
|
64
|
+
*,
|
|
65
|
+
duration_seconds: int,
|
|
66
|
+
ssh_public_key: str,
|
|
67
|
+
start_utc: str | None = None,
|
|
68
|
+
compute_resource: dict[str, Any] | None = None,
|
|
69
|
+
) -> VmProvisionTerms:
|
|
70
|
+
payload: dict[str, Any] = {
|
|
71
|
+
"duration_seconds": int(duration_seconds),
|
|
72
|
+
"ssh_public_key": ssh_public_key,
|
|
73
|
+
}
|
|
74
|
+
if start_utc is not None:
|
|
75
|
+
payload["start_utc"] = str(start_utc)
|
|
76
|
+
if compute_resource is not None:
|
|
77
|
+
payload["compute_resource"] = compute_resource
|
|
78
|
+
return VmProvisionTerms(payload=payload)
|
|
79
|
+
|
|
80
|
+
def normalize_vm_provision_terms(terms: Any) -> VmProvisionTerms:
|
|
81
|
+
"""Validate a wire envelope, VM payload, or durably decoded VM message."""
|
|
82
|
+
|
|
83
|
+
if isinstance(terms, dict) and "payload" in terms:
|
|
84
|
+
return VmProvisionTerms.model_validate(terms)
|
|
85
|
+
durable = dict(terms) if isinstance(terms, dict) else terms
|
|
86
|
+
if isinstance(durable, dict):
|
|
87
|
+
kind = durable.pop("kind", VM_PROVISION_KIND)
|
|
88
|
+
if kind != VM_PROVISION_KIND:
|
|
89
|
+
raise ValueError("durable VM provision terms have an unsupported kind")
|
|
90
|
+
durable.pop("settlement_selection", None)
|
|
91
|
+
payload = VmProvisionPayload.model_validate(durable).model_dump(exclude_none=True)
|
|
92
|
+
return VmProvisionTerms(payload=payload)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# compute.v1 payload accessors
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# Wire-received provision terms arrive as the core opaque carrier
|
|
99
|
+
# (market_core.schemas.ProvisionTerms) or a plain dict; the core model
|
|
100
|
+
# does not interpret the payload. These functions are the VM domain's
|
|
101
|
+
# interpretation of the compute.v1 payload shape, accepting any carrier
|
|
102
|
+
# with a ``payload`` attribute or key.
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def provision_payload(terms: Any) -> dict[str, Any]:
|
|
106
|
+
if isinstance(terms, dict):
|
|
107
|
+
raw = terms.get("payload")
|
|
108
|
+
else:
|
|
109
|
+
raw = getattr(terms, "payload", None)
|
|
110
|
+
return raw if isinstance(raw, dict) else {}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def provision_duration_seconds(terms: Any) -> int | None:
|
|
114
|
+
raw = provision_payload(terms).get("duration_seconds")
|
|
115
|
+
return int(raw) if raw is not None else None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def provision_start_utc(terms: Any) -> str | None:
|
|
119
|
+
raw = provision_payload(terms).get("start_utc")
|
|
120
|
+
if raw is None:
|
|
121
|
+
return None
|
|
122
|
+
text = str(raw).strip()
|
|
123
|
+
return text or None
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def provision_ssh_public_key(terms: Any) -> str:
|
|
127
|
+
raw = provision_payload(terms).get("ssh_public_key")
|
|
128
|
+
return raw if isinstance(raw, str) else ""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def provision_compute_resource(terms: Any) -> dict[str, Any] | None:
|
|
132
|
+
raw = provision_payload(terms).get("compute_resource")
|
|
133
|
+
return raw if isinstance(raw, dict) else None
|