Skip to content

Commit cea8e05

Browse files
authored
Fix json schema reference and invalid properties errors (#2433)
* Replace *Schema models with manually specified extra types * Allow json schema additionalProperties to fix invalid property errors
1 parent 775e656 commit cea8e05

File tree

8 files changed

+79
-90
lines changed

8 files changed

+79
-90
lines changed

docs/docs/reference/dstack.yml/dev-environment.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ The `dev-environment` configuration type allows running [dev environments](../..
2828

2929
### `resources`
3030

31-
#SCHEMA# dstack._internal.core.models.resources.ResourcesSpecSchema
31+
#SCHEMA# dstack._internal.core.models.resources.ResourcesSpec
3232
overrides:
3333
show_root_heading: false
3434
type:
@@ -37,15 +37,15 @@ The `dev-environment` configuration type allows running [dev environments](../..
3737

3838
#### `resources.gpu` { #resources-gpu data-toc-label="gpu" }
3939

40-
#SCHEMA# dstack._internal.core.models.resources.GPUSpecSchema
40+
#SCHEMA# dstack._internal.core.models.resources.GPUSpec
4141
overrides:
4242
show_root_heading: false
4343
type:
4444
required: true
4545

4646
#### `resources.disk` { #resources-disk data-toc-label="disk" }
4747

48-
#SCHEMA# dstack._internal.core.models.resources.DiskSpecSchema
48+
#SCHEMA# dstack._internal.core.models.resources.DiskSpec
4949
overrides:
5050
show_root_heading: false
5151
type:

docs/docs/reference/dstack.yml/fleet.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ The `fleet` configuration type allows creating and updating fleets.
3939

4040
### `resources`
4141

42-
#SCHEMA# dstack._internal.core.models.resources.ResourcesSpecSchema
42+
#SCHEMA# dstack._internal.core.models.resources.ResourcesSpec
4343
overrides:
4444
show_root_heading: false
4545
type:
@@ -48,15 +48,15 @@ The `fleet` configuration type allows creating and updating fleets.
4848

4949
#### `resouces.gpu` { #resources-gpu data-toc-label="gpu" }
5050

51-
#SCHEMA# dstack._internal.core.models.resources.GPUSpecSchema
51+
#SCHEMA# dstack._internal.core.models.resources.GPUSpec
5252
overrides:
5353
show_root_heading: false
5454
type:
5555
required: true
5656

5757
#### `resouces.disk` { #resources-disk data-toc-label="disk" }
5858

59-
#SCHEMA# dstack._internal.core.models.resources.DiskSpecSchema
59+
#SCHEMA# dstack._internal.core.models.resources.DiskSpec
6060
overrides:
6161
show_root_heading: false
6262
type:

docs/docs/reference/dstack.yml/service.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ The `service` configuration type allows running [services](../../concepts/servic
9090

9191
### `resources`
9292

93-
#SCHEMA# dstack._internal.core.models.resources.ResourcesSpecSchema
93+
#SCHEMA# dstack._internal.core.models.resources.ResourcesSpec
9494
overrides:
9595
show_root_heading: false
9696
type:
@@ -99,15 +99,15 @@ The `service` configuration type allows running [services](../../concepts/servic
9999

100100
#### `resouces.gpu` { #resources-gpu data-toc-label="gpu" }
101101

102-
#SCHEMA# dstack._internal.core.models.resources.GPUSpecSchema
102+
#SCHEMA# dstack._internal.core.models.resources.GPUSpec
103103
overrides:
104104
show_root_heading: false
105105
type:
106106
required: true
107107

108108
#### `resouces.disk` { #resources-disk data-toc-label="disk" }
109109

110-
#SCHEMA# dstack._internal.core.models.resources.DiskSpecSchema
110+
#SCHEMA# dstack._internal.core.models.resources.DiskSpec
111111
overrides:
112112
show_root_heading: false
113113
type:

docs/docs/reference/dstack.yml/task.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ The `task` configuration type allows running [tasks](../../concepts/tasks.md).
2828

2929
### `resources`
3030

31-
#SCHEMA# dstack._internal.core.models.resources.ResourcesSpecSchema
31+
#SCHEMA# dstack._internal.core.models.resources.ResourcesSpec
3232
overrides:
3333
show_root_heading: false
3434
type:
@@ -37,15 +37,15 @@ The `task` configuration type allows running [tasks](../../concepts/tasks.md).
3737

3838
#### `resouces.gpu` { #resources-gpu data-toc-label="gpu" }
3939

40-
#SCHEMA# dstack._internal.core.models.resources.GPUSpecSchema
40+
#SCHEMA# dstack._internal.core.models.resources.GPUSpec
4141
overrides:
4242
show_root_heading: false
4343
type:
4444
required: true
4545

4646
#### `resouces.disk` { #resources-disk data-toc-label="disk" }
4747

48-
#SCHEMA# dstack._internal.core.models.resources.DiskSpecSchema
48+
#SCHEMA# dstack._internal.core.models.resources.DiskSpec
4949
overrides:
5050
show_root_heading: false
5151
type:

src/dstack/_internal/core/models/configurations.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import re
22
from enum import Enum
3-
from typing import Any, List, Optional, Union
3+
from typing import Any, Dict, List, Optional, Union
44

55
from pydantic import Field, ValidationError, conint, constr, root_validator, validator
66
from typing_extensions import Annotated, Literal
@@ -425,4 +425,9 @@ class DstackConfiguration(CoreModel):
425425
]
426426

427427
class Config:
428-
schema_extra = {"$schema": "http://json-schema.org/draft-07/schema#"}
428+
@staticmethod
429+
def schema_extra(schema: Dict[str, Any]):
430+
schema["$schema"] = "http://json-schema.org/draft-07/schema#"
431+
# Allow additionalProperties so that vscode and others not supporting
432+
# top-level oneOf do not warn about properties being invalid.
433+
schema["additionalProperties"] = True

src/dstack/_internal/core/models/fleets.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
parse_idle_duration,
2020
)
2121
from dstack._internal.core.models.resources import Range, ResourcesSpec
22+
from dstack._internal.utils.json_schema import add_extra_schema_types
2223

2324

2425
class FleetStatus(str, Enum):
@@ -222,6 +223,14 @@ class InstanceGroupParams(CoreModel):
222223
),
223224
] = None
224225

226+
class Config:
227+
@staticmethod
228+
def schema_extra(schema: Dict[str, Any], model: Type):
229+
add_extra_schema_types(
230+
schema["properties"]["nodes"],
231+
extra_types=[{"type": "integer"}, {"type": "string"}],
232+
)
233+
225234
_validate_idle_duration = validator("idle_duration", pre=True, allow_reuse=True)(
226235
parse_idle_duration
227236
)

src/dstack/_internal/core/models/resources.py

Lines changed: 45 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from dstack._internal.core.models.common import CoreModel
1010
from dstack._internal.utils.common import pretty_resources
11+
from dstack._internal.utils.json_schema import add_extra_schema_types
1112
from dstack._internal.utils.logging import get_logger
1213

1314
logger = get_logger(__name__)
@@ -128,6 +129,22 @@ def __str__(self):
128129

129130

130131
class GPUSpec(CoreModel):
132+
class Config:
133+
@staticmethod
134+
def schema_extra(schema: Dict[str, Any]):
135+
add_extra_schema_types(
136+
schema["properties"]["count"],
137+
extra_types=[{"type": "integer"}, {"type": "string"}],
138+
)
139+
add_extra_schema_types(
140+
schema["properties"]["memory"],
141+
extra_types=[{"type": "integer"}, {"type": "string"}],
142+
)
143+
add_extra_schema_types(
144+
schema["properties"]["total_memory"],
145+
extra_types=[{"type": "integer"}, {"type": "string"}],
146+
)
147+
131148
vendor: Annotated[
132149
Optional[gpuhunt.AcceleratorVendor],
133150
Field(
@@ -233,6 +250,14 @@ def _vendor_from_string(cls, v: str) -> gpuhunt.AcceleratorVendor:
233250

234251

235252
class DiskSpec(CoreModel):
253+
class Config:
254+
@staticmethod
255+
def schema_extra(schema: Dict[str, Any]):
256+
add_extra_schema_types(
257+
schema["properties"]["size"],
258+
extra_types=[{"type": "integer"}, {"type": "string"}],
259+
)
260+
236261
size: Annotated[Range[Memory], Field(description="Disk size")]
237262

238263
@classmethod
@@ -254,11 +279,26 @@ class ResourcesSpec(CoreModel):
254279
class Config:
255280
@staticmethod
256281
def schema_extra(schema: Dict[str, Any]):
257-
schema.clear()
258-
# replace strict schema with a more permissive one
259-
ref_template = "#/definitions/ResourcesSpecRequest/definitions/{model}"
260-
for field, value in ResourcesSpecSchema.schema(ref_template=ref_template).items():
261-
schema[field] = value
282+
add_extra_schema_types(
283+
schema["properties"]["cpu"],
284+
extra_types=[{"type": "integer"}, {"type": "string"}],
285+
)
286+
add_extra_schema_types(
287+
schema["properties"]["memory"],
288+
extra_types=[{"type": "integer"}, {"type": "string"}],
289+
)
290+
add_extra_schema_types(
291+
schema["properties"]["shm_size"],
292+
extra_types=[{"type": "integer"}, {"type": "string"}],
293+
)
294+
add_extra_schema_types(
295+
schema["properties"]["gpu"],
296+
extra_types=[{"type": "integer"}, {"type": "string"}],
297+
)
298+
add_extra_schema_types(
299+
schema["properties"]["disk"],
300+
extra_types=[{"type": "integer"}, {"type": "string"}],
301+
)
262302

263303
cpu: Annotated[Range[int], Field(description="The number of CPU cores")] = DEFAULT_CPU_COUNT
264304
memory: Annotated[Range[Memory], Field(description="The RAM size (e.g., `8GB`)")] = (
@@ -290,74 +330,3 @@ def pretty_format(self) -> str:
290330
resources.update(disk_size=self.disk.size)
291331
res = pretty_resources(**resources)
292332
return res
293-
294-
295-
IntRangeLike = Union[Range[Union[int, str]], int, str]
296-
MemoryRangeLike = Union[Range[Union[Memory, float, int, str]], float, int, str]
297-
MemoryLike = Union[Memory, float, int, str]
298-
GPULike = Union[GPUSpec, "GPUSpecSchema", int, str]
299-
DiskLike = Union[DiskSpec, "DiskSpecSchema", float, int, str]
300-
ComputeCapabilityLike = Union[ComputeCapability, float, str]
301-
302-
303-
class GPUSpecSchema(CoreModel):
304-
vendor: Annotated[
305-
Optional[gpuhunt.AcceleratorVendor],
306-
Field(
307-
description="The vendor of the GPU/accelerator, one of: `nvidia`, `amd`, `google` (alias: `tpu`), `intel`"
308-
),
309-
] = None
310-
name: Annotated[
311-
Optional[Union[List[str], str]], Field(description="The GPU name or list of names")
312-
] = None
313-
count: Annotated[IntRangeLike, Field(description="The number of GPUs")] = DEFAULT_GPU_COUNT
314-
memory: Annotated[
315-
Optional[MemoryRangeLike],
316-
Field(
317-
description="The RAM size (e.g., `16GB`). Can be set to a range (e.g. `16GB..`, or `16GB..80GB`)"
318-
),
319-
] = None
320-
total_memory: Annotated[
321-
Optional[MemoryRangeLike],
322-
Field(
323-
description="The total RAM size (e.g., `32GB`). Can be set to a range (e.g. `16GB..`, or `16GB..80GB`)"
324-
),
325-
] = None
326-
compute_capability: Annotated[
327-
Optional[ComputeCapabilityLike],
328-
Field(description="The minimum compute capability of the GPU (e.g., `7.5`)"),
329-
] = None
330-
331-
332-
class DiskSpecSchema(CoreModel):
333-
size: Annotated[
334-
MemoryRangeLike,
335-
Field(
336-
description="The disk size. Can be set to a range (e.g., `100GB..` or `100GB..200GB`)"
337-
),
338-
]
339-
340-
341-
class ResourcesSpecSchema(CoreModel):
342-
cpu: Annotated[Optional[IntRangeLike], Field(description="The number of CPU cores")] = (
343-
DEFAULT_CPU_COUNT
344-
)
345-
memory: Annotated[
346-
Optional[MemoryRangeLike],
347-
Field(description="The RAM size (e.g., `8GB`)"),
348-
] = DEFAULT_MEMORY_SIZE
349-
shm_size: Annotated[
350-
Optional[MemoryLike],
351-
Field(
352-
description="The size of shared memory (e.g., `8GB`). "
353-
"If you are using parallel communicating processes (e.g., dataloaders in PyTorch), "
354-
"you may need to configure this"
355-
),
356-
] = None
357-
gpu: Annotated[
358-
Optional[GPULike],
359-
Field(
360-
description="The GPU requirements. Can be set to a number, a string (e.g. `A100`, `80GB:2`, etc.), or an object"
361-
),
362-
] = None
363-
disk: Annotated[Optional[DiskLike], Field(description="The disk resources")] = DEFAULT_DISK
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def add_extra_schema_types(schema_property: dict, extra_types: list[dict]):
2+
if "allOf" in schema_property:
3+
ref = schema_property.pop("allOf")[0]
4+
else:
5+
ref = {"type": schema_property.pop("type")}
6+
schema_property["anyOf"] = [ref, *extra_types]

0 commit comments

Comments
 (0)