plato-sdk-v2 2.7.4__py3-none-any.whl → 2.7.6__py3-none-any.whl

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 (28) hide show
  1. plato/_generated/__init__.py +1 -1
  2. plato/_generated/api/v1/testcases/get_testcase_metadata_for_scoring.py +6 -6
  3. plato/_generated/api/v2/__init__.py +15 -1
  4. plato/_generated/api/v2/admin/__init__.py +7 -0
  5. plato/_generated/api/v2/admin/cache/__init__.py +11 -0
  6. plato/_generated/api/v2/admin/cache/clear_all.py +74 -0
  7. plato/_generated/api/v2/admin/cache/clear_single_cache.py +79 -0
  8. plato/_generated/api/v2/admin/cache/delete_key.py +84 -0
  9. plato/_generated/api/v2/admin/cache/get_stats.py +70 -0
  10. plato/_generated/api/v2/admin/cache/list_cache_names.py +67 -0
  11. plato/_generated/api/v2/sessions/__init__.py +3 -1
  12. plato/_generated/api/v2/sessions/add_job.py +97 -0
  13. plato/_generated/models/__init__.py +89 -3
  14. plato/chronos/api/agents/get_agent_schema.py +5 -5
  15. plato/chronos/api/agents/get_agent_versions.py +5 -5
  16. plato/chronos/api/agents/list_agents.py +5 -5
  17. plato/chronos/api/registry/get_agent_schema_api_registry_agents__agent_name__schema_get.py +5 -5
  18. plato/chronos/api/registry/get_agent_versions_api_registry_agents__agent_name__versions_get.py +5 -5
  19. plato/chronos/api/registry/list_registry_agents_api_registry_agents_get.py +5 -5
  20. plato/chronos/api/sessions/__init__.py +2 -0
  21. plato/chronos/api/sessions/complete_session.py +4 -2
  22. plato/chronos/api/sessions/list_session_creators.py +57 -0
  23. plato/chronos/api/sessions/list_sessions.py +30 -2
  24. plato/chronos/models/__init__.py +39 -19
  25. {plato_sdk_v2-2.7.4.dist-info → plato_sdk_v2-2.7.6.dist-info}/METADATA +1 -1
  26. {plato_sdk_v2-2.7.4.dist-info → plato_sdk_v2-2.7.6.dist-info}/RECORD +28 -19
  27. {plato_sdk_v2-2.7.4.dist-info → plato_sdk_v2-2.7.6.dist-info}/WHEEL +0 -0
  28. {plato_sdk_v2-2.7.4.dist-info → plato_sdk_v2-2.7.6.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,4 @@
1
- """Plato API SDK - v0.45.11"""
1
+ """Plato API SDK - v0.47.0"""
2
2
 
3
3
  from . import api, errors, models
4
4
  from .client import AsyncClient, Client
@@ -37,9 +37,9 @@ def sync(
37
37
  x_api_key: str | None = None,
38
38
  ) -> Any:
39
39
  """Get limited test case metadata for the scoring page.
40
- Only returns: publicId, name, scoringTypes.
41
- This endpoint is designed for annotators who should not see sensitive test case data
42
- like prompts, hints, or expected outputs."""
40
+ Returns: publicId, name, scoringTypes, prompt.
41
+ This endpoint is designed for annotators - prompt is included to help them
42
+ understand what they're evaluating."""
43
43
 
44
44
  request_args = _build_request_args(
45
45
  public_id=public_id,
@@ -59,9 +59,9 @@ async def asyncio(
59
59
  x_api_key: str | None = None,
60
60
  ) -> Any:
61
61
  """Get limited test case metadata for the scoring page.
62
- Only returns: publicId, name, scoringTypes.
63
- This endpoint is designed for annotators who should not see sensitive test case data
64
- like prompts, hints, or expected outputs."""
62
+ Returns: publicId, name, scoringTypes, prompt.
63
+ This endpoint is designed for annotators - prompt is included to help them
64
+ understand what they're evaluating."""
65
65
 
66
66
  request_args = _build_request_args(
67
67
  public_id=public_id,
@@ -1,8 +1,22 @@
1
1
  """API version module."""
2
2
 
3
- from . import agents, artifacts, chronos, chronos_packages, cluster, jobs, networks, pypi, releases, sessions, user
3
+ from . import (
4
+ admin,
5
+ agents,
6
+ artifacts,
7
+ chronos,
8
+ chronos_packages,
9
+ cluster,
10
+ jobs,
11
+ networks,
12
+ pypi,
13
+ releases,
14
+ sessions,
15
+ user,
16
+ )
4
17
 
5
18
  __all__ = [
19
+ "admin",
6
20
  "agents",
7
21
  "artifacts",
8
22
  "chronos",
@@ -0,0 +1,7 @@
1
+ """API version module."""
2
+
3
+ from . import cache
4
+
5
+ __all__ = [
6
+ "cache",
7
+ ]
@@ -0,0 +1,11 @@
1
+ """API endpoints."""
2
+
3
+ from . import clear_all, clear_single_cache, delete_key, get_stats, list_cache_names
4
+
5
+ __all__ = [
6
+ "get_stats",
7
+ "clear_single_cache",
8
+ "clear_all",
9
+ "delete_key",
10
+ "list_cache_names",
11
+ ]
@@ -0,0 +1,74 @@
1
+ """Clear All"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ from plato._generated.errors import raise_for_status
10
+ from plato._generated.models import CacheClearAllResponse
11
+
12
+
13
+ def _build_request_args(
14
+ authorization: str | None = None,
15
+ x_api_key: str | None = None,
16
+ ) -> dict[str, Any]:
17
+ """Build request arguments."""
18
+ url = "/api/v2/admin/cache/clear-all"
19
+
20
+ headers: dict[str, str] = {}
21
+ if authorization is not None:
22
+ headers["authorization"] = authorization
23
+ if x_api_key is not None:
24
+ headers["X-API-Key"] = x_api_key
25
+
26
+ return {
27
+ "method": "POST",
28
+ "url": url,
29
+ "headers": headers,
30
+ }
31
+
32
+
33
+ def sync(
34
+ client: httpx.Client,
35
+ authorization: str | None = None,
36
+ x_api_key: str | None = None,
37
+ ) -> CacheClearAllResponse:
38
+ """Clear all registered caches.
39
+
40
+ This clears all caches on the current worker and publishes events
41
+ to clear them on all other workers in the cluster.
42
+
43
+ Requires admin privileges."""
44
+
45
+ request_args = _build_request_args(
46
+ authorization=authorization,
47
+ x_api_key=x_api_key,
48
+ )
49
+
50
+ response = client.request(**request_args)
51
+ raise_for_status(response)
52
+ return CacheClearAllResponse.model_validate(response.json())
53
+
54
+
55
+ async def asyncio(
56
+ client: httpx.AsyncClient,
57
+ authorization: str | None = None,
58
+ x_api_key: str | None = None,
59
+ ) -> CacheClearAllResponse:
60
+ """Clear all registered caches.
61
+
62
+ This clears all caches on the current worker and publishes events
63
+ to clear them on all other workers in the cluster.
64
+
65
+ Requires admin privileges."""
66
+
67
+ request_args = _build_request_args(
68
+ authorization=authorization,
69
+ x_api_key=x_api_key,
70
+ )
71
+
72
+ response = await client.request(**request_args)
73
+ raise_for_status(response)
74
+ return CacheClearAllResponse.model_validate(response.json())
@@ -0,0 +1,79 @@
1
+ """Clear Single Cache"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ from plato._generated.errors import raise_for_status
10
+ from plato._generated.models import CacheClearResponse, CacheName
11
+
12
+
13
+ def _build_request_args(
14
+ cache_name: CacheName,
15
+ authorization: str | None = None,
16
+ x_api_key: str | None = None,
17
+ ) -> dict[str, Any]:
18
+ """Build request arguments."""
19
+ url = f"/api/v2/admin/cache/clear/{cache_name}"
20
+
21
+ headers: dict[str, str] = {}
22
+ if authorization is not None:
23
+ headers["authorization"] = authorization
24
+ if x_api_key is not None:
25
+ headers["X-API-Key"] = x_api_key
26
+
27
+ return {
28
+ "method": "POST",
29
+ "url": url,
30
+ "headers": headers,
31
+ }
32
+
33
+
34
+ def sync(
35
+ client: httpx.Client,
36
+ cache_name: CacheName,
37
+ authorization: str | None = None,
38
+ x_api_key: str | None = None,
39
+ ) -> CacheClearResponse:
40
+ """Clear a specific cache by name.
41
+
42
+ This clears the cache on the current worker and publishes an event
43
+ to clear the cache on all other workers in the cluster.
44
+
45
+ Requires admin privileges."""
46
+
47
+ request_args = _build_request_args(
48
+ cache_name=cache_name,
49
+ authorization=authorization,
50
+ x_api_key=x_api_key,
51
+ )
52
+
53
+ response = client.request(**request_args)
54
+ raise_for_status(response)
55
+ return CacheClearResponse.model_validate(response.json())
56
+
57
+
58
+ async def asyncio(
59
+ client: httpx.AsyncClient,
60
+ cache_name: CacheName,
61
+ authorization: str | None = None,
62
+ x_api_key: str | None = None,
63
+ ) -> CacheClearResponse:
64
+ """Clear a specific cache by name.
65
+
66
+ This clears the cache on the current worker and publishes an event
67
+ to clear the cache on all other workers in the cluster.
68
+
69
+ Requires admin privileges."""
70
+
71
+ request_args = _build_request_args(
72
+ cache_name=cache_name,
73
+ authorization=authorization,
74
+ x_api_key=x_api_key,
75
+ )
76
+
77
+ response = await client.request(**request_args)
78
+ raise_for_status(response)
79
+ return CacheClearResponse.model_validate(response.json())
@@ -0,0 +1,84 @@
1
+ """Delete Key"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ from plato._generated.errors import raise_for_status
10
+ from plato._generated.models import CacheKeyDeleteResponse, CacheName
11
+
12
+
13
+ def _build_request_args(
14
+ cache_name: CacheName,
15
+ key: str,
16
+ authorization: str | None = None,
17
+ x_api_key: str | None = None,
18
+ ) -> dict[str, Any]:
19
+ """Build request arguments."""
20
+ url = f"/api/v2/admin/cache/key/{cache_name}/{key}"
21
+
22
+ headers: dict[str, str] = {}
23
+ if authorization is not None:
24
+ headers["authorization"] = authorization
25
+ if x_api_key is not None:
26
+ headers["X-API-Key"] = x_api_key
27
+
28
+ return {
29
+ "method": "DELETE",
30
+ "url": url,
31
+ "headers": headers,
32
+ }
33
+
34
+
35
+ def sync(
36
+ client: httpx.Client,
37
+ cache_name: CacheName,
38
+ key: str,
39
+ authorization: str | None = None,
40
+ x_api_key: str | None = None,
41
+ ) -> CacheKeyDeleteResponse:
42
+ """Delete a specific key from a cache.
43
+
44
+ Note: This only deletes from the current worker. For distributed
45
+ invalidation of a specific key, consider clearing the entire cache.
46
+
47
+ Requires admin privileges."""
48
+
49
+ request_args = _build_request_args(
50
+ cache_name=cache_name,
51
+ key=key,
52
+ authorization=authorization,
53
+ x_api_key=x_api_key,
54
+ )
55
+
56
+ response = client.request(**request_args)
57
+ raise_for_status(response)
58
+ return CacheKeyDeleteResponse.model_validate(response.json())
59
+
60
+
61
+ async def asyncio(
62
+ client: httpx.AsyncClient,
63
+ cache_name: CacheName,
64
+ key: str,
65
+ authorization: str | None = None,
66
+ x_api_key: str | None = None,
67
+ ) -> CacheKeyDeleteResponse:
68
+ """Delete a specific key from a cache.
69
+
70
+ Note: This only deletes from the current worker. For distributed
71
+ invalidation of a specific key, consider clearing the entire cache.
72
+
73
+ Requires admin privileges."""
74
+
75
+ request_args = _build_request_args(
76
+ cache_name=cache_name,
77
+ key=key,
78
+ authorization=authorization,
79
+ x_api_key=x_api_key,
80
+ )
81
+
82
+ response = await client.request(**request_args)
83
+ raise_for_status(response)
84
+ return CacheKeyDeleteResponse.model_validate(response.json())
@@ -0,0 +1,70 @@
1
+ """Get Stats"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ from plato._generated.errors import raise_for_status
10
+ from plato._generated.models import CacheStatsResponse
11
+
12
+
13
+ def _build_request_args(
14
+ authorization: str | None = None,
15
+ x_api_key: str | None = None,
16
+ ) -> dict[str, Any]:
17
+ """Build request arguments."""
18
+ url = "/api/v2/admin/cache/stats"
19
+
20
+ headers: dict[str, str] = {}
21
+ if authorization is not None:
22
+ headers["authorization"] = authorization
23
+ if x_api_key is not None:
24
+ headers["X-API-Key"] = x_api_key
25
+
26
+ return {
27
+ "method": "GET",
28
+ "url": url,
29
+ "headers": headers,
30
+ }
31
+
32
+
33
+ def sync(
34
+ client: httpx.Client,
35
+ authorization: str | None = None,
36
+ x_api_key: str | None = None,
37
+ ) -> CacheStatsResponse:
38
+ """Get statistics for all registered caches.
39
+
40
+ Returns cache names with their current size and TTL configuration.
41
+ Requires admin privileges."""
42
+
43
+ request_args = _build_request_args(
44
+ authorization=authorization,
45
+ x_api_key=x_api_key,
46
+ )
47
+
48
+ response = client.request(**request_args)
49
+ raise_for_status(response)
50
+ return CacheStatsResponse.model_validate(response.json())
51
+
52
+
53
+ async def asyncio(
54
+ client: httpx.AsyncClient,
55
+ authorization: str | None = None,
56
+ x_api_key: str | None = None,
57
+ ) -> CacheStatsResponse:
58
+ """Get statistics for all registered caches.
59
+
60
+ Returns cache names with their current size and TTL configuration.
61
+ Requires admin privileges."""
62
+
63
+ request_args = _build_request_args(
64
+ authorization=authorization,
65
+ x_api_key=x_api_key,
66
+ )
67
+
68
+ response = await client.request(**request_args)
69
+ raise_for_status(response)
70
+ return CacheStatsResponse.model_validate(response.json())
@@ -0,0 +1,67 @@
1
+ """List Cache Names"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ from plato._generated.errors import raise_for_status
10
+
11
+
12
+ def _build_request_args(
13
+ authorization: str | None = None,
14
+ x_api_key: str | None = None,
15
+ ) -> dict[str, Any]:
16
+ """Build request arguments."""
17
+ url = "/api/v2/admin/cache/names"
18
+
19
+ headers: dict[str, str] = {}
20
+ if authorization is not None:
21
+ headers["authorization"] = authorization
22
+ if x_api_key is not None:
23
+ headers["X-API-Key"] = x_api_key
24
+
25
+ return {
26
+ "method": "GET",
27
+ "url": url,
28
+ "headers": headers,
29
+ }
30
+
31
+
32
+ def sync(
33
+ client: httpx.Client,
34
+ authorization: str | None = None,
35
+ x_api_key: str | None = None,
36
+ ) -> dict[str, Any]:
37
+ """List all registered cache names and their TTL configuration.
38
+
39
+ Requires admin privileges."""
40
+
41
+ request_args = _build_request_args(
42
+ authorization=authorization,
43
+ x_api_key=x_api_key,
44
+ )
45
+
46
+ response = client.request(**request_args)
47
+ raise_for_status(response)
48
+ return response.json()
49
+
50
+
51
+ async def asyncio(
52
+ client: httpx.AsyncClient,
53
+ authorization: str | None = None,
54
+ x_api_key: str | None = None,
55
+ ) -> dict[str, Any]:
56
+ """List all registered cache names and their TTL configuration.
57
+
58
+ Requires admin privileges."""
59
+
60
+ request_args = _build_request_args(
61
+ authorization=authorization,
62
+ x_api_key=x_api_key,
63
+ )
64
+
65
+ response = await client.request(**request_args)
66
+ raise_for_status(response)
67
+ return response.json()
@@ -1,6 +1,7 @@
1
1
  """API endpoints."""
2
2
 
3
3
  from . import (
4
+ add_job,
4
5
  add_ssh_key,
5
6
  checkpoint,
6
7
  close,
@@ -34,6 +35,8 @@ from . import (
34
35
 
35
36
  __all__ = [
36
37
  "make",
38
+ "list_jobs",
39
+ "add_job",
37
40
  "reset",
38
41
  "heartbeat",
39
42
  "connect_network",
@@ -52,7 +55,6 @@ __all__ = [
52
55
  "state",
53
56
  "get_session_flows",
54
57
  "get_session_plato_config",
55
- "list_jobs",
56
58
  "wait_for_ready",
57
59
  "get_public_url",
58
60
  "get_connect_url",
@@ -0,0 +1,97 @@
1
+ """Add Job"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ from plato._generated.errors import raise_for_status
10
+ from plato._generated.models import AddJobRequest, AddJobResponse
11
+
12
+
13
+ def _build_request_args(
14
+ session_id: str,
15
+ body: AddJobRequest,
16
+ authorization: str | None = None,
17
+ x_api_key: str | None = None,
18
+ ) -> dict[str, Any]:
19
+ """Build request arguments."""
20
+ url = f"/api/v2/sessions/{session_id}/jobs"
21
+
22
+ headers: dict[str, str] = {}
23
+ if authorization is not None:
24
+ headers["authorization"] = authorization
25
+ if x_api_key is not None:
26
+ headers["X-API-Key"] = x_api_key
27
+
28
+ return {
29
+ "method": "POST",
30
+ "url": url,
31
+ "json": body.model_dump(mode="json", exclude_none=True),
32
+ "headers": headers,
33
+ }
34
+
35
+
36
+ def sync(
37
+ client: httpx.Client,
38
+ session_id: str,
39
+ body: AddJobRequest,
40
+ authorization: str | None = None,
41
+ x_api_key: str | None = None,
42
+ ) -> AddJobResponse:
43
+ """Add a new job to an existing session.
44
+
45
+ The new job will:
46
+ 1. Become part of the session's job group
47
+ 2. Be matched to an available VM via the resource matcher
48
+ 3. Automatically join the session's WireGuard network if one exists
49
+
50
+ If the session has a network (from a previous connect_network call),
51
+ the new job will automatically be added as a member and connected
52
+ when the VM is allocated.
53
+
54
+ Use wait_for_ready on the job_id to wait for VM allocation and network setup."""
55
+
56
+ request_args = _build_request_args(
57
+ session_id=session_id,
58
+ body=body,
59
+ authorization=authorization,
60
+ x_api_key=x_api_key,
61
+ )
62
+
63
+ response = client.request(**request_args)
64
+ raise_for_status(response)
65
+ return AddJobResponse.model_validate(response.json())
66
+
67
+
68
+ async def asyncio(
69
+ client: httpx.AsyncClient,
70
+ session_id: str,
71
+ body: AddJobRequest,
72
+ authorization: str | None = None,
73
+ x_api_key: str | None = None,
74
+ ) -> AddJobResponse:
75
+ """Add a new job to an existing session.
76
+
77
+ The new job will:
78
+ 1. Become part of the session's job group
79
+ 2. Be matched to an available VM via the resource matcher
80
+ 3. Automatically join the session's WireGuard network if one exists
81
+
82
+ If the session has a network (from a previous connect_network call),
83
+ the new job will automatically be added as a member and connected
84
+ when the VM is allocated.
85
+
86
+ Use wait_for_ready on the job_id to wait for VM allocation and network setup."""
87
+
88
+ request_args = _build_request_args(
89
+ session_id=session_id,
90
+ body=body,
91
+ authorization=authorization,
92
+ x_api_key=x_api_key,
93
+ )
94
+
95
+ response = await client.request(**request_args)
96
+ raise_for_status(response)
97
+ return AddJobResponse.model_validate(response.json())
@@ -1,11 +1,11 @@
1
1
  # generated by datamodel-codegen:
2
- # filename: tmp3gp2q7hj.json
3
- # timestamp: 2026-01-29T10:43:35+00:00
2
+ # filename: tmpy5rfux2_.json
3
+ # timestamp: 2026-01-29T20:47:53+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from enum import Enum
8
- from typing import Annotated, Any, Dict, List, Literal
8
+ from typing import Annotated, Any, Literal
9
9
 
10
10
  from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel
11
11
 
@@ -446,6 +446,57 @@ class BulkArchiveTestCasesRequest(BaseModel):
446
446
  test_case_ids: Annotated[list[int], Field(title="Test Case Ids")]
447
447
 
448
448
 
449
+ class CacheClearAllResponse(BaseModel):
450
+ model_config = ConfigDict(
451
+ extra="allow",
452
+ )
453
+ caches_cleared: Annotated[dict[str, int], Field(title="Caches Cleared")]
454
+ total_cleared: Annotated[int, Field(title="Total Cleared")]
455
+
456
+
457
+ class CacheClearResponse(BaseModel):
458
+ model_config = ConfigDict(
459
+ extra="allow",
460
+ )
461
+ cache_name: Annotated[str, Field(title="Cache Name")]
462
+ entries_cleared: Annotated[int, Field(title="Entries Cleared")]
463
+
464
+
465
+ class CacheKeyDeleteResponse(BaseModel):
466
+ model_config = ConfigDict(
467
+ extra="allow",
468
+ )
469
+ cache_name: Annotated[str, Field(title="Cache Name")]
470
+ key: Annotated[str, Field(title="Key")]
471
+ deleted: Annotated[bool, Field(title="Deleted")]
472
+
473
+
474
+ class CacheName(Enum):
475
+ artifact = "artifact"
476
+ artifact_tag = "artifact_tag"
477
+ simulator = "simulator"
478
+ handler = "handler"
479
+ service_type = "service_type"
480
+ organization = "organization"
481
+ api_key = "api_key"
482
+ access = "access"
483
+
484
+
485
+ class CacheStats(BaseModel):
486
+ model_config = ConfigDict(
487
+ extra="allow",
488
+ )
489
+ size: Annotated[int, Field(title="Size")]
490
+ ttl_seconds: Annotated[int, Field(title="Ttl Seconds")]
491
+
492
+
493
+ class CacheStatsResponse(BaseModel):
494
+ model_config = ConfigDict(
495
+ extra="allow",
496
+ )
497
+ caches: Annotated[dict[str, CacheStats], Field(title="Caches")]
498
+
499
+
449
500
  class Capability(Enum):
450
501
  forms_and_inputs = "forms_and_inputs"
451
502
  search_and_extract = "search_and_extract"
@@ -4087,6 +4138,20 @@ class ModelsScoringConfigJSONSchemaDraft7(BaseModel):
4087
4138
  """
4088
4139
 
4089
4140
 
4141
+ class AddJobResponse(BaseModel):
4142
+ model_config = ConfigDict(
4143
+ extra="allow",
4144
+ )
4145
+ session_id: Annotated[str, Field(title="Session Id")]
4146
+ """
4147
+ Session ID the job was added to
4148
+ """
4149
+ env: EnvInfo
4150
+ """
4151
+ Created environment info
4152
+ """
4153
+
4154
+
4090
4155
  class AddReviewRequest(BaseModel):
4091
4156
  model_config = ConfigDict(
4092
4157
  extra="allow",
@@ -4750,6 +4815,27 @@ class AppSchemasBuildModelsSimConfigDataset(BaseModel):
4750
4815
  """
4751
4816
 
4752
4817
 
4818
+ class AddJobRequest(BaseModel):
4819
+ model_config = ConfigDict(
4820
+ extra="allow",
4821
+ )
4822
+ env: Annotated[
4823
+ EnvFromSimulator | EnvFromArtifact | EnvFromResource,
4824
+ Field(discriminator="type", title="Env"),
4825
+ ]
4826
+ """
4827
+ Environment configuration for the new job
4828
+ """
4829
+ timeout: Annotated[int | None, Field(title="Timeout")] = 1800
4830
+ """
4831
+ VM timeout in seconds (default: 30 minutes)
4832
+ """
4833
+ heartbeat_timeout: Annotated[int | None, Field(title="Heartbeat Timeout")] = None
4834
+ """
4835
+ Per-VM heartbeat timeout. None=use default (300s), 0=disabled.
4836
+ """
4837
+
4838
+
4753
4839
  class AnnotatorAssignmentListItem(BaseModel):
4754
4840
  model_config = ConfigDict(
4755
4841
  extra="allow",