|
1 | 1 | # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. |
| 2 | +# isort: skip_file |
2 | 3 |
|
3 | 4 | from __future__ import annotations |
4 | 5 |
|
|
14 | 15 | blueprint_create_from_inspection_params, |
15 | 16 | ) |
16 | 17 | from .._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given |
17 | | -from .._utils import maybe_transform, async_maybe_transform |
| 18 | +from .._utils import maybe_transform, async_maybe_transform, ValidationNotification |
18 | 19 | from .._compat import cached_property |
19 | 20 | from .._resource import SyncAPIResource, AsyncAPIResource |
20 | 21 | from .._response import ( |
@@ -51,32 +52,56 @@ class BlueprintRequestArgs(TypedDict, total=False): |
51 | 52 | __all__ = ["BlueprintsResource", "AsyncBlueprintsResource", "BlueprintRequestArgs"] |
52 | 53 |
|
53 | 54 |
|
54 | | -def _validate_file_mounts(file_mounts: Optional[Dict[str, str]] | Omit) -> None: |
55 | | - """Validate file_mounts are within size constraints. |
| 55 | +def _format_bytes(num_bytes: int) -> str: |
| 56 | + """Format a byte count in a human-friendly way (KB/MB/GB). |
| 57 | +
|
| 58 | + Uses binary units (1024). Avoids decimals when exact. |
| 59 | + """ |
| 60 | + if num_bytes < 1024: |
| 61 | + return f"{num_bytes} bytes" |
| 62 | + for factor, unit in ((1 << 30, "GB"), (1 << 20, "MB"), (1 << 10, "KB")): |
| 63 | + if num_bytes >= factor: |
| 64 | + value = num_bytes / factor |
| 65 | + if float(value).is_integer(): |
| 66 | + return f"{int(value)} {unit}" |
| 67 | + return f"{value:.1f} {unit}" |
| 68 | + return f"{num_bytes} bytes" |
| 69 | + |
| 70 | + |
| 71 | +def _validate_file_mounts(file_mounts: Optional[Dict[str, str]] | Omit) -> ValidationNotification: |
| 72 | + """Validate file_mounts are within size constraints: returns validation failures. |
56 | 73 |
|
57 | 74 | Currently enforces a maximum per-file size to avoid server-side issues with |
58 | 75 | large inline file contents. Also enforces a maximum total size across all |
59 | 76 | file_mounts. |
60 | 77 | """ |
61 | 78 |
|
| 79 | + note = ValidationNotification() |
| 80 | + |
62 | 81 | if file_mounts is omit or file_mounts is None: |
63 | | - return |
| 82 | + return note |
64 | 83 |
|
65 | 84 | total_size_bytes = 0 |
66 | 85 | for mount_path, content in file_mounts.items(): |
67 | 86 | # Measure size in bytes using UTF-8 encoding since payloads are JSON strings |
68 | 87 | size_bytes = len(content.encode("utf-8")) |
69 | 88 | if size_bytes > FILE_MOUNT_MAX_SIZE_BYTES: |
70 | | - raise ValueError( |
71 | | - f"file_mount '{mount_path}' exceeds maximum size of {FILE_MOUNT_MAX_SIZE_BYTES} bytes. Use object_mounts instead." |
| 89 | + over = size_bytes - FILE_MOUNT_MAX_SIZE_BYTES |
| 90 | + note.add_error( |
| 91 | + f"file_mount '{mount_path}' is {_format_bytes(over)} over the limit " |
| 92 | + f"({_format_bytes(size_bytes)} / {_format_bytes(FILE_MOUNT_MAX_SIZE_BYTES)}). Use object_mounts instead." |
72 | 93 | ) |
73 | 94 | total_size_bytes += size_bytes |
74 | 95 |
|
75 | 96 | if total_size_bytes > FILE_MOUNT_TOTAL_MAX_SIZE_BYTES: |
76 | | - raise ValueError( |
77 | | - f"total file_mounts size exceeds maximum of {FILE_MOUNT_TOTAL_MAX_SIZE_BYTES} bytes. Use object_mounts instead." |
| 97 | + total_over = total_size_bytes - FILE_MOUNT_TOTAL_MAX_SIZE_BYTES |
| 98 | + note.add_error( |
| 99 | + f"total file_mounts size is {_format_bytes(total_over)} over the limit " |
| 100 | + f"({_format_bytes(total_size_bytes)} / {_format_bytes(FILE_MOUNT_TOTAL_MAX_SIZE_BYTES)}). Use object_mounts instead." |
78 | 101 | ) |
79 | 102 |
|
| 103 | + return note |
| 104 | + |
80 | 105 |
|
81 | 106 | class BlueprintsResource(SyncAPIResource): |
82 | 107 | @cached_property |
@@ -172,7 +197,9 @@ def create( |
172 | 197 |
|
173 | 198 | idempotency_key: Specify a custom idempotency key for this request |
174 | 199 | """ |
175 | | - _validate_file_mounts(file_mounts) |
| 200 | + note = _validate_file_mounts(file_mounts) |
| 201 | + if note.has_errors(): |
| 202 | + raise ValueError(note.error_message()) |
176 | 203 |
|
177 | 204 | return self._post( |
178 | 205 | "/v1/blueprints", |
@@ -788,7 +815,9 @@ async def create( |
788 | 815 |
|
789 | 816 | idempotency_key: Specify a custom idempotency key for this request |
790 | 817 | """ |
791 | | - _validate_file_mounts(file_mounts) |
| 818 | + note = _validate_file_mounts(file_mounts) |
| 819 | + if note.has_errors(): |
| 820 | + raise ValueError(note.error_message()) |
792 | 821 |
|
793 | 822 | return await self._post( |
794 | 823 | "/v1/blueprints", |
|
0 commit comments