Skip to content

Commit 80c872f

Browse files
author
Garming
committed
fix(studio): unify source project limits
1 parent abc085e commit 80c872f

82 files changed

Lines changed: 575 additions & 406 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frontend/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ server that `veadk frontend` launches — no separate backend.
238238
root `app.py` as the compatible default. Studio removes a single wrapping
239239
directory, rejects unsafe paths, and shows upload, image build, Runtime
240240
creation, and service publishing as separate deployment stages.
241-
- **Existing-project migration**: upload one local ZIP of at most 50 MiB from
241+
- **Existing-project migration**: upload one local ZIP of at most 20 MiB from
242242
the add-Agent menu. Studio creates one user-owned Dev Sandbox Session with a
243243
one-hour TTL, then asks the preinstalled Codex to perform read-only framework,
244244
entry-point, and migration-boundary analysis. Migration starts only after the

frontend/server/deployment_source.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,16 @@
2727
from pathlib import Path, PurePosixPath
2828

2929
import yaml
30+
from frontend.server.source_project_limits import (
31+
SOURCE_PROJECT_MAX_BYTES,
32+
SOURCE_PROJECT_MAX_FILES,
33+
)
3034

3135
_DEFAULT_ENTRY_POINT = "app.py"
32-
_MAX_ARCHIVE_BYTES = 512 * 1024 * 1024
33-
_MAX_ARCHIVE_FILES = 20_000
34-
_MAX_EXPANDED_BYTES = 512 * 1024 * 1024
35-
_MAX_FILE_BYTES = 128 * 1024 * 1024
36+
_MAX_ARCHIVE_BYTES = SOURCE_PROJECT_MAX_BYTES
37+
_MAX_ARCHIVE_FILES = SOURCE_PROJECT_MAX_FILES
38+
_MAX_EXPANDED_BYTES = SOURCE_PROJECT_MAX_BYTES
39+
_MAX_FILE_BYTES = SOURCE_PROJECT_MAX_BYTES
3640
_MAX_PATH_BYTES = 4 * 1024
3741
_MAX_PATH_DEPTH = 64
3842
_SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
@@ -225,7 +229,7 @@ def _manifest_files(manifest: object) -> tuple[dict[str, tuple[int, str]], str]:
225229
raise DeploymentSourceError("迁移产物文件清单格式无效。")
226230
expanded_bytes += size
227231
if expanded_bytes > _MAX_EXPANDED_BYTES:
228-
raise DeploymentSourceError("迁移产物解压后超过 512 MiB。")
232+
raise DeploymentSourceError("迁移产物解压后超过 20 MiB。")
229233
descriptors[relative] = (size, digest)
230234
_reject_path_collisions(set(descriptors))
231235
entry_point = _relative_path(

frontend/server/intelligent_development_projects/models.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222

2323
from pydantic import BaseModel, ConfigDict, Field
2424

25+
from frontend.server.source_project_limits import (
26+
SOURCE_PROJECT_MAX_BYTES,
27+
SOURCE_PROJECT_MAX_FILES,
28+
)
29+
2530
SourceProjectOrigin = Literal["intelligent-development", "migration"]
2631
SourceVersionProducer = Literal["intelligent-development", "migration"]
2732

@@ -75,8 +80,8 @@ class IntelligentDevelopmentVersion(BaseModel):
7580
validation_report_sha256: str = Field(
7681
alias="validationReportSha256", pattern=r"^[0-9a-f]{64}$"
7782
)
78-
artifact_size: int = Field(alias="artifactSize", ge=1, le=512 * 1024 * 1024)
79-
file_count: int = Field(alias="fileCount", ge=1, le=20_000)
83+
artifact_size: int = Field(alias="artifactSize", ge=1, le=SOURCE_PROJECT_MAX_BYTES)
84+
file_count: int = Field(alias="fileCount", ge=1, le=SOURCE_PROJECT_MAX_FILES)
8085
agent_name: str = Field(alias="agentName", min_length=1, max_length=256)
8186
entry_point: str = Field(alias="entryPoint", min_length=1, max_length=512)
8287
verified: bool

frontend/server/intelligent_development_projects/repository.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626

2727
from pydantic import ValidationError
2828

29+
from frontend.server.source_project_limits import (
30+
SOURCE_PROJECT_MAX_BYTES,
31+
SOURCE_PROJECT_MAX_REPORT_BYTES,
32+
)
2933
from frontend.server.storage import STUDIO_STORAGE_ROOT_PREFIX
3034

3135
from .models import (
@@ -41,8 +45,8 @@
4145
r"/projects/(?P<project>[0-9a-f]{32})/versions/[0-9a-f]{32}/version\.json$"
4246
)
4347
_MAX_JSON_BYTES = 256 * 1024
44-
_MAX_ARTIFACT_BYTES = 512 * 1024 * 1024
45-
_MAX_REPORT_BYTES = 16 * 1024 * 1024
48+
_MAX_ARTIFACT_BYTES = SOURCE_PROJECT_MAX_BYTES
49+
_MAX_REPORT_BYTES = SOURCE_PROJECT_MAX_REPORT_BYTES
4650
T = TypeVar("T")
4751
logger = logging.getLogger(__name__)
4852

frontend/server/intelligent_development_projects/service.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
from frontend.server.intelligent_development import DeliveryReference, release_path
2828
from frontend.server.intelligent_development_task import IntentDecision
2929
from frontend.server.sandbox_remote import SandboxRemoteTransport
30+
from frontend.server.source_project_limits import (
31+
SOURCE_PROJECT_MAX_BYTES,
32+
SOURCE_PROJECT_MAX_FILES,
33+
SOURCE_PROJECT_MAX_REPORT_BYTES,
34+
)
3035
from veadk.cli.frontend_sandbox import SandboxSessionUnavailableError
3136

3237
from .models import (
@@ -43,8 +48,8 @@
4348
TosIntelligentDevelopmentProjectRepository,
4449
)
4550

46-
_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024
47-
_MAX_REPORT_BYTES = 2 * 1024 * 1024
51+
_MAX_ARTIFACT_BYTES = SOURCE_PROJECT_MAX_BYTES
52+
_MAX_REPORT_BYTES = SOURCE_PROJECT_MAX_REPORT_BYTES
4853
logger = logging.getLogger(__name__)
4954

5055

@@ -208,8 +213,8 @@ async def restore_base_version(
208213
" with zipfile.ZipFile(archive) as package:\n"
209214
" files=[item for item in package.infolist() if not item.is_dir()]\n"
210215
" if len(files)!=expected_count: raise ValueError('file count mismatch')\n"
211-
" if len(files)>20000: raise ValueError('too many files')\n"
212-
" if sum(item.file_size for item in files)>512*1024*1024: raise ValueError('archive too large')\n"
216+
f" if len(files)>{SOURCE_PROJECT_MAX_FILES}: raise ValueError('too many files')\n"
217+
f" if sum(item.file_size for item in files)>{SOURCE_PROJECT_MAX_BYTES}: raise ValueError('archive too large')\n"
213218
" for item in files:\n"
214219
" path=PurePosixPath(item.filename)\n"
215220
" if path.is_absolute() or not path.parts or '..' in path.parts: raise ValueError('unsafe path')\n"

frontend/server/intelligent_development_source.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,27 @@
3737
IntelligentDevelopmentVersionNotFound,
3838
)
3939
from frontend.server.sandbox_remote import SandboxRemoteTransport
40+
from frontend.server.source_project_limits import (
41+
SOURCE_PROJECT_MAX_BYTES,
42+
SOURCE_PROJECT_MAX_FILES,
43+
SOURCE_PROJECT_MAX_REPORT_BYTES,
44+
)
4045
from veadk.cli.frontend_sandbox import (
4146
SandboxConversationService,
4247
SandboxSessionNotFoundError,
4348
SandboxSessionUnavailableError,
4449
)
4550

4651
_SHA256 = re.compile(r"^[0-9a-f]{64}$")
47-
_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024
48-
_MAX_EXPANDED_BYTES = 20 * 1024 * 1024
49-
_MAX_FILE_COUNT = 2_000
50-
_MAX_REPORT_BYTES = 2 * 1024 * 1024
52+
_MAX_ARTIFACT_BYTES = SOURCE_PROJECT_MAX_BYTES
53+
_MAX_EXPANDED_BYTES = SOURCE_PROJECT_MAX_BYTES
54+
_MAX_FILE_COUNT = SOURCE_PROJECT_MAX_FILES
55+
_MAX_REPORT_BYTES = SOURCE_PROJECT_MAX_REPORT_BYTES
5156
_MAX_DESCRIPTOR_BYTES = 256 * 1024
5257
_CURRENT_POINTER_BYTES = 4 * 1024
5358
_MAX_PREVIEW_FILE_BYTES = 2 * 1024 * 1024
54-
_MAX_PREVIEW_TOTAL_BYTES = 20 * 1024 * 1024
55-
_MAX_PREVIEW_FILES = 2_000
59+
_MAX_PREVIEW_TOTAL_BYTES = SOURCE_PROJECT_MAX_BYTES
60+
_MAX_PREVIEW_FILES = SOURCE_PROJECT_MAX_FILES
5661
_REQUIRED_GATES = {
5762
"local-checks",
5863
"service-probe",

frontend/server/migration/contracts.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
from datetime import datetime
2121
from pathlib import PurePosixPath
2222

23+
from frontend.server.source_project_limits import (
24+
SOURCE_PROJECT_MAX_BYTES,
25+
SOURCE_PROJECT_MAX_FILES,
26+
)
27+
2328
from .models import (
2429
MIGRATION_FRAMEWORKS,
2530
STRUCTURED_MIGRATION_FRAMEWORKS,
@@ -30,12 +35,12 @@
3035
_MAX_PATH_BYTES = 4 * 1024
3136
_MAX_PATH_DEPTH = 64
3237
_MAX_TEXT_LENGTH = 20_000
33-
_MAX_DELIVERY_FILES = 20_000
34-
_MAX_DELIVERY_BYTES = 512 * 1024 * 1024
35-
_MAX_DELIVERY_FILE_BYTES = 128 * 1024 * 1024
36-
_MAX_ARTIFACT_BYTES = 512 * 1024 * 1024
37-
_MAX_SOURCE_BYTES = 50 * 1024 * 1024
38-
_MAX_SOURCE_EXPANDED_BYTES = 1024 * 1024 * 1024
38+
_MAX_DELIVERY_FILES = SOURCE_PROJECT_MAX_FILES
39+
_MAX_DELIVERY_BYTES = SOURCE_PROJECT_MAX_BYTES
40+
_MAX_DELIVERY_FILE_BYTES = SOURCE_PROJECT_MAX_BYTES
41+
_MAX_ARTIFACT_BYTES = SOURCE_PROJECT_MAX_BYTES
42+
_MAX_SOURCE_BYTES = SOURCE_PROJECT_MAX_BYTES
43+
_MAX_SOURCE_EXPANDED_BYTES = SOURCE_PROJECT_MAX_BYTES
3944
_SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
4045
_FILE_MODE_RE = re.compile(r"^0[0-7]{1,3}$")
4146
_ENVIRONMENT_KEY_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")

frontend/server/migration/routes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ async def upload_source(
321321
if declared_bytes > MIGRATION_UPLOAD_MAX_BYTES:
322322
too_large = MigrationError(
323323
"MIGRATION_SOURCE_TOO_LARGE",
324-
"项目 ZIP 不能超过 50 MiB。",
324+
"项目 ZIP 不能超过 20 MiB。",
325325
status_code=413,
326326
)
327327
raise HTTPException(
@@ -333,7 +333,7 @@ async def upload_source(
333333
if len(content) + len(chunk) > MIGRATION_UPLOAD_MAX_BYTES:
334334
too_large = MigrationError(
335335
"MIGRATION_SOURCE_TOO_LARGE",
336-
"项目 ZIP 不能超过 50 MiB。",
336+
"项目 ZIP 不能超过 20 MiB。",
337337
status_code=413,
338338
)
339339
raise HTTPException(

frontend/server/migration/service.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
DeploymentSourceError,
3939
extract_migration_source,
4040
)
41+
from frontend.server.source_project_limits import (
42+
SOURCE_PROJECT_MAX_BYTES,
43+
SOURCE_PROJECT_MAX_FILES,
44+
SOURCE_PROJECT_MAX_REPORT_BYTES,
45+
)
4146

4247
from .contracts import (
4348
MigrationContractError,
@@ -70,16 +75,16 @@
7075

7176
MIGRATION_ROOT = "/home/gem/.studio/migration/v1"
7277
MIGRATION_SESSION_TTL_SECONDS = 60 * 60
73-
MIGRATION_UPLOAD_MAX_BYTES = 50 * 1024 * 1024
78+
MIGRATION_UPLOAD_MAX_BYTES = SOURCE_PROJECT_MAX_BYTES
7479
MIGRATION_CLI_MIN_VERSION = "0.52.1"
7580
MIGRATION_UNSUPPORTED_MODEL_IDS = frozenset({"deepseek-v4-pro-260425"})
76-
_MAX_EXPANDED_BYTES = 1024 * 1024 * 1024
77-
_MAX_ARCHIVE_FILES = 20_000
81+
_MAX_EXPANDED_BYTES = SOURCE_PROJECT_MAX_BYTES
82+
_MAX_ARCHIVE_FILES = SOURCE_PROJECT_MAX_FILES
7883
_MAX_ARCHIVE_PATH_BYTES = 4 * 1024
7984
_MAX_ARCHIVE_DEPTH = 64
80-
_MAX_JSON_BYTES = 16 * 1024 * 1024
85+
_MAX_JSON_BYTES = SOURCE_PROJECT_MAX_REPORT_BYTES
8186
_MAX_PROVENANCE_BYTES = 64 * 1024
82-
_MAX_ARTIFACT_BYTES = 512 * 1024 * 1024
87+
_MAX_ARTIFACT_BYTES = SOURCE_PROJECT_MAX_BYTES
8388
_MAX_PREVIEW_BYTES = 2 * 1024 * 1024
8489
_FILE_OPERATION_TIMEOUT_SECONDS = 300
8590
_TASK_ID_RE = re.compile(r"^migration-v1-[0-9a-f]{32}$")
@@ -797,7 +802,7 @@ def validate_source_archive(content: bytes) -> SourceArchiveSummary:
797802
if len(content) > MIGRATION_UPLOAD_MAX_BYTES:
798803
raise MigrationError(
799804
"MIGRATION_SOURCE_TOO_LARGE",
800-
"项目 ZIP 不能超过 50 MiB。",
805+
"项目 ZIP 不能超过 20 MiB。",
801806
status_code=413,
802807
)
803808
seen: set[str] = set()
@@ -857,7 +862,7 @@ def validate_source_archive(content: bytes) -> SourceArchiveSummary:
857862
if expanded_bytes > _MAX_EXPANDED_BYTES:
858863
raise MigrationError(
859864
"MIGRATION_SOURCE_EXPANDED_TOO_LARGE",
860-
"项目 ZIP 解压后不能超过 1 GiB。",
865+
"项目 ZIP 解压后不能超过 20 MiB。",
861866
status_code=413,
862867
)
863868
except zipfile.BadZipFile as error:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Shared capacity limits for persisted Studio source projects."""
16+
17+
SOURCE_PROJECT_MAX_BYTES = 20 * 1024 * 1024
18+
SOURCE_PROJECT_MAX_FILES = 2_000
19+
SOURCE_PROJECT_MAX_REPORT_BYTES = 2 * 1024 * 1024
20+
21+
__all__ = [
22+
"SOURCE_PROJECT_MAX_BYTES",
23+
"SOURCE_PROJECT_MAX_FILES",
24+
"SOURCE_PROJECT_MAX_REPORT_BYTES",
25+
]

0 commit comments

Comments
 (0)