thestage 0.6.4__py3-none-any.whl → 0.6.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 (73) hide show
  1. thestage/__init__.py +1 -1
  2. thestage/color_scheme/color_scheme.py +1 -0
  3. thestage/controllers/base_controller.py +4 -3
  4. thestage/controllers/config_controller.py +16 -4
  5. thestage/controllers/container_controller.py +147 -49
  6. thestage/controllers/instance_controller.py +35 -9
  7. thestage/controllers/project_controller.py +334 -86
  8. thestage/entities/container.py +5 -3
  9. thestage/entities/project_inference_simulator.py +2 -1
  10. thestage/entities/project_inference_simulator_model.py +1 -1
  11. thestage/entities/project_task.py +2 -3
  12. thestage/entities/rented_instance.py +2 -2
  13. thestage/entities/self_hosted_instance.py +2 -2
  14. thestage/helpers/error_handler.py +3 -3
  15. thestage/services/clients/git/git_client.py +8 -12
  16. thestage/services/clients/thestage_api/api_client.py +144 -109
  17. thestage/services/clients/thestage_api/dtos/base_controller/connect_resolve_response.py +21 -0
  18. thestage/services/clients/thestage_api/dtos/container_param_request.py +1 -1
  19. thestage/services/clients/thestage_api/dtos/container_response.py +1 -21
  20. thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_request.py +2 -1
  21. thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_request.py +7 -1
  22. thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_response.py +0 -1
  23. thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_request.py +1 -0
  24. thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_request.py +2 -1
  25. thestage/services/clients/thestage_api/dtos/inference_controller/{inference_simulator_list_for_project_request.py → inference_simulator_list_request.py} +3 -2
  26. thestage/services/clients/thestage_api/dtos/inference_controller/{inference_simulator_list_for_project_response.py → inference_simulator_list_response.py} +1 -1
  27. thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_request.py +2 -1
  28. thestage/services/clients/thestage_api/dtos/instance_rented_response.py +4 -37
  29. thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_request.py +3 -3
  30. thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_request.py +3 -11
  31. thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_request.py +1 -0
  32. thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_request.py +2 -1
  33. thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_request.py +2 -4
  34. thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_request.py +5 -3
  35. thestage/services/clients/thestage_api/dtos/project_response.py +3 -15
  36. thestage/services/clients/thestage_api/dtos/selfhosted_instance_response.py +2 -20
  37. thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_response.py +1 -1
  38. thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_request.py +4 -2
  39. thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_response.py +1 -1
  40. thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_request.py +4 -1
  41. thestage/services/clients/thestage_api/dtos/task_controller/task_view_response.py +0 -2
  42. thestage/services/config_provider/config_provider.py +2 -2
  43. thestage/services/connect/connect_service.py +76 -73
  44. thestage/services/container/container_service.py +23 -19
  45. thestage/services/container/mapper/container_mapper.py +2 -1
  46. thestage/services/instance/instance_service.py +13 -20
  47. thestage/services/instance/mapper/instance_mapper.py +1 -3
  48. thestage/services/instance/mapper/selfhosted_mapper.py +3 -4
  49. thestage/services/logging/logging_service.py +40 -40
  50. thestage/services/project/dto/inference_simulator_dto.py +1 -10
  51. thestage/services/project/dto/inference_simulator_model_dto.py +2 -10
  52. thestage/services/project/dto/project_config.py +3 -2
  53. thestage/services/project/mapper/project_inference_simulator_mapper.py +1 -0
  54. thestage/services/project/mapper/project_inference_simulator_model_mapper.py +3 -3
  55. thestage/services/project/mapper/project_task_mapper.py +2 -3
  56. thestage/services/project/project_service.py +161 -131
  57. thestage/services/remote_server_service.py +1 -0
  58. thestage/services/task/dto/task_dto.py +3 -23
  59. {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info}/METADATA +3 -2
  60. {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info}/RECORD +63 -72
  61. {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info}/WHEEL +1 -1
  62. thestage/services/clients/thestage_api/dtos/cloud_provider_region.py +0 -19
  63. thestage/services/clients/thestage_api/dtos/docker_container_assigned_device.py +0 -10
  64. thestage/services/clients/thestage_api/dtos/enums/currency_type.py +0 -10
  65. thestage/services/clients/thestage_api/dtos/enums/daemon_status.py +0 -9
  66. thestage/services/clients/thestage_api/dtos/enums/disk_type.py +0 -7
  67. thestage/services/clients/thestage_api/dtos/enums/drive_type.py +0 -7
  68. thestage/services/clients/thestage_api/dtos/enums/instance_type.py +0 -7
  69. thestage/services/clients/thestage_api/dtos/enums/location_region.py +0 -11
  70. thestage/services/clients/thestage_api/dtos/enums/power_status.py +0 -10
  71. thestage/services/clients/thestage_api/dtos/price_definition.py +0 -14
  72. {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info}/entry_points.txt +0 -0
  73. {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info/licenses}/LICENSE.txt +0 -0
@@ -43,9 +43,9 @@ class LoggingService:
43
43
 
44
44
 
45
45
  @error_handler()
46
- def print_last_task_logs(self, task_id: int, logs_number: Optional[int]):
46
+ def print_last_task_logs(self, task_public_id: str, logs_number: Optional[int]):
47
47
  logs = self.__thestage_api_client.query_user_logs(
48
- task_id=task_id,
48
+ task_public_id=task_public_id,
49
49
  limit=logs_number
50
50
  )
51
51
  for log_message in reversed(logs.queryResult):
@@ -53,9 +53,9 @@ class LoggingService:
53
53
 
54
54
 
55
55
  @error_handler()
56
- def print_last_inference_simulator_logs(self, inference_simulator_id: int, logs_number: Optional[int]):
56
+ def print_last_inference_simulator_logs(self, inference_simulator_public_id: str, logs_number: Optional[int]):
57
57
  logs = self.__thestage_api_client.query_user_logs(
58
- inference_simulator_id=inference_simulator_id,
58
+ inference_simulator_public_id=inference_simulator_public_id,
59
59
  limit=logs_number
60
60
  )
61
61
  for log_message in reversed(logs.queryResult):
@@ -63,9 +63,10 @@ class LoggingService:
63
63
 
64
64
 
65
65
  @error_handler()
66
- def print_last_container_logs(self, container_uid: str, logs_number: Optional[int]):
66
+ def print_last_container_logs(self, container_public_id: Optional[str], container_slug: Optional[str], logs_number: Optional[int]):
67
67
  container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
68
- container_slug=container_uid,
68
+ container_public_id=container_public_id,
69
+ container_slug=container_slug,
69
70
  )
70
71
 
71
72
  if not container:
@@ -73,7 +74,7 @@ class LoggingService:
73
74
  raise typer.Exit(1)
74
75
 
75
76
  logs = self.__thestage_api_client.query_user_logs(
76
- container_id=container.id,
77
+ container_public_id=container.public_id,
77
78
  limit=logs_number
78
79
  )
79
80
  for log_message in reversed(logs.queryResult):
@@ -81,15 +82,15 @@ class LoggingService:
81
82
 
82
83
 
83
84
  @error_handler()
84
- def stream_task_logs_with_controls(self, task_id: int):
85
+ def stream_task_logs_with_controls(self, task_public_id: str):
85
86
  asyncio.run(
86
- self.__stream_task_logs_with_controls_async(task_id=task_id)
87
+ self.__stream_task_logs_with_controls_async(task_public_id=task_public_id)
87
88
  )
88
89
 
89
90
 
90
91
  @error_handler()
91
- async def __stream_task_logs_with_controls_async(self, task_id: int):
92
- task_view_response: Optional[TaskViewResponse] = self.__thestage_api_client.get_task(task_id=task_id,)
92
+ async def __stream_task_logs_with_controls_async(self, task_public_id: str):
93
+ task_view_response: Optional[TaskViewResponse] = self.__thestage_api_client.get_task(task_public_id=task_public_id,)
93
94
 
94
95
  task_status_map: Dict[str, str] = self.__thestage_api_client.get_task_localized_status_map()
95
96
 
@@ -98,25 +99,20 @@ class LoggingService:
98
99
  if task:
99
100
  if task.frontend_status.status_key not in [TaskStatus.RUNNING, TaskStatus.SCHEDULED]:
100
101
  typer.echo(__("Task must be in status '%required_status%' to stream real-time logs. Task %task_id% status: '%status%'.", {
101
- 'task_id': str(task.id),
102
+ 'task_id': str(task.public_id),
102
103
  'status': task.frontend_status.status_translation,
103
104
  'required_status': task_status_map.get(TaskStatus.RUNNING) or TaskStatus.RUNNING
104
105
  }))
105
106
  raise typer.Exit(1)
106
107
  else:
107
- typer.echo(__("Task with ID %task_id% was not found", {'task_id': task.id}))
108
+ typer.echo(f"Task with ID {task_public_id} was not found")
108
109
  raise typer.Exit(1)
109
110
 
110
- typer.echo(__(
111
- f"Log stream for task %task_id% started",
112
- {
113
- 'task_id': str(task.id),
114
- }
115
- ))
111
+ typer.echo(f"Log stream for task {task.public_id} started")
116
112
 
117
113
  typer.echo(__("CTRL+C to cancel the task. CTRL+D to disconnect from log stream."))
118
114
 
119
- print_logs_task = asyncio.create_task(self.print_realtime_logs(task_id=task.id))
115
+ print_logs_task = asyncio.create_task(self.print_realtime_logs(task_public_id=task.public_id))
120
116
  input_task = asyncio.create_task(self.read_log_stream_input())
121
117
 
122
118
  def sigint_handler():
@@ -131,22 +127,24 @@ class LoggingService:
131
127
  if input_task in done:
132
128
  print_logs_task.cancel()
133
129
  if not input_task.result(): # result is only expected if ctrl+D triggered EOFError
134
- typer.echo(f"\rTask {task_id} will be canceled")
130
+ typer.echo(f"\rTask {task_public_id} will be canceled")
135
131
  self.__thestage_api_client.cancel_task(
136
- task_id=task.id,
132
+ task_public_id=task.public_id,
137
133
  )
138
134
 
139
135
  @error_handler()
140
- def stream_inference_simulator_logs_with_controls(self, slug: str):
136
+ def stream_inference_simulator_logs_with_controls(self, public_id: Optional[str] = None, slug: Optional[str] = None):
141
137
  asyncio.run(
142
138
  self.__stream_inference_simulator_logs_with_controls_async(
139
+ public_id=public_id,
143
140
  slug=slug
144
141
  )
145
142
  )
146
143
 
147
144
  @error_handler()
148
- async def __stream_inference_simulator_logs_with_controls_async(self, slug: str):
145
+ async def __stream_inference_simulator_logs_with_controls_async(self, public_id: Optional[str], slug: Optional[str]):
149
146
  get_inference_simulator_response: Optional[GetInferenceSimulatorResponse] = self.__thestage_api_client.get_inference_simulator(
147
+ public_id=public_id,
150
148
  slug=slug,
151
149
  )
152
150
 
@@ -177,7 +175,7 @@ class LoggingService:
177
175
  typer.echo(__("CTRL+D to disconnect from log stream."))
178
176
 
179
177
  print_task_or_inference_simulator_logs = asyncio.create_task(
180
- self.print_realtime_logs(inference_simulator_id=inference_simulator.id)
178
+ self.print_realtime_logs(inference_simulator_public_id=inference_simulator.public_id)
181
179
  )
182
180
  input_task = asyncio.create_task(self.read_log_stream_input())
183
181
 
@@ -190,18 +188,20 @@ class LoggingService:
190
188
 
191
189
 
192
190
  @error_handler()
193
- def stream_container_logs_with_controls(self, container_uid: str):
191
+ def stream_container_logs_with_controls(self, container_public_id: Optional[str], container_slug: Optional[str]):
194
192
  asyncio.run(
195
193
  self.__stream_container_logs_with_controls_async(
196
- container_uid=container_uid
194
+ container_public_id=container_public_id,
195
+ container_slug=container_slug
197
196
  )
198
197
  )
199
198
 
200
199
 
201
200
  @error_handler()
202
- async def __stream_container_logs_with_controls_async(self, container_uid: str):
201
+ async def __stream_container_logs_with_controls_async(self, container_public_id: Optional[str], container_slug: Optional[str]):
203
202
  container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
204
- container_slug=container_uid,
203
+ container_public_id=container_public_id,
204
+ container_slug=container_slug,
205
205
  )
206
206
 
207
207
  if container:
@@ -214,7 +214,7 @@ class LoggingService:
214
214
  typer.echo(f"Log stream for Docker container started")
215
215
  typer.echo("CTRL+D to disconnect from log stream.")
216
216
 
217
- print_logs_task = asyncio.create_task(self.print_realtime_logs(docker_container_id=container.id))
217
+ print_logs_task = asyncio.create_task(self.print_realtime_logs(docker_container_public_id=container.public_id))
218
218
  input_task = asyncio.create_task(self.read_log_stream_input())
219
219
 
220
220
  def sigint_handler():
@@ -243,9 +243,9 @@ class LoggingService:
243
243
 
244
244
  async def print_realtime_logs(
245
245
  self,
246
- task_id: Optional[int] = None,
247
- inference_simulator_id: Optional[int] = None,
248
- docker_container_id: Optional[int] = None,
246
+ task_public_id: Optional[str] = None,
247
+ inference_simulator_public_id: Optional[str] = None,
248
+ docker_container_public_id: Optional[str] = None,
249
249
  ):
250
250
  polling_interval_seconds: float = 4 # also adjust polling api method timeout if changed
251
251
  between_logs_sleeping_coef: float = 1 # we emulate delay between logs, but if for any reason code runs for too long - delays will be controlled with this coef
@@ -269,9 +269,9 @@ class LoggingService:
269
269
  # print_nonblocking(f'TDIFF {stream_to_logs_diff.total_seconds()}', writer)
270
270
  try:
271
271
  logs_response = await self.__thestage_api_client.poll_logs_httpx(
272
- task_id=task_id,
273
- inference_simulator_id=inference_simulator_id,
274
- docker_container_id=docker_container_id,
272
+ task_public_id=task_public_id,
273
+ inference_simulator_public_id=inference_simulator_public_id,
274
+ docker_container_public_id=docker_container_public_id,
275
275
  last_log_timestamp=last_iteration_log_timestamp,
276
276
  last_log_id=last_log_id
277
277
  )
@@ -314,11 +314,11 @@ class LoggingService:
314
314
 
315
315
  if consecutive_error_count > 7:
316
316
  seconds_with_error = (datetime.utcnow() - errors_started_at).total_seconds()
317
- if inference_simulator_id:
317
+ if inference_simulator_public_id:
318
318
  print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds). Try 'thestage inference-simulator logs <inference-simulator-UID>' to reconnect.", writer)
319
- elif task_id:
320
- print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds). Try 'thestage project task logs {task_id}' to reconnect.", writer)
321
- elif docker_container_id:
319
+ elif task_public_id:
320
+ print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds). Try 'thestage project task logs {task_public_id}' to reconnect.", writer)
321
+ elif docker_container_public_id:
322
322
  print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds). Try 'thestage container logs <docker-container-UID>' to reconnect.", writer)
323
323
  else:
324
324
  print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds)", writer)
@@ -4,20 +4,11 @@ from pydantic import BaseModel, ConfigDict, Field
4
4
  class InferenceSimulatorDto(BaseModel):
5
5
  model_config = ConfigDict(use_enum_values=True)
6
6
 
7
- id: Optional[int] = Field(None, alias="id")
8
- client_id: Optional[int] = Field(None, alias="clientId")
9
- title: Optional[str] = Field(None, alias="title")
7
+ public_id: Optional[str] = Field(None, alias="publicId")
10
8
  slug: Optional[str] = Field(None, alias="slug")
11
- docker_container_id: Optional[int] = Field(None, alias="dockerContainerId")
12
- instance_rented_id: Optional[int] = Field(None, alias="instanceRentedId")
13
- selfhosted_instance_id: Optional[int] = Field(None, alias="selfhostedInstanceId")
14
- project_id: Optional[int] = Field(None, alias="projectId")
15
9
  status: Optional[str] = Field(None, alias="status")
16
- commit_hash: Optional[str] = Field(None, alias="commitHash")
17
10
  http_endpoint: Optional[str] = Field(None, alias="httpEndpoint")
18
11
  grpc_endpoint: Optional[str] = Field(None, alias="grpcEndpoint")
19
12
  metrics_endpoint: Optional[str] = Field(None, alias="metricsEndpoint")
20
- inference_metadata: Optional[Dict[str, Any]] = Field(None, alias="inferenceMetadata")
21
13
  qlip_serve_metadata: Optional[Dict[str, Any]] = Field(None, alias="qlipServeMetadata")
22
14
  created_at: Optional[str] = Field(None, alias="createdAt")
23
- updated_at: Optional[str] = Field(None, alias="updatedAt")
@@ -4,18 +4,10 @@ from pydantic import BaseModel, ConfigDict, Field
4
4
  class InferenceSimulatorModelDto(BaseModel):
5
5
  model_config = ConfigDict(use_enum_values=True)
6
6
 
7
- id: Optional[int] = Field(None, alias='id')
7
+ public_id: Optional[str] = Field(None, alias='publicId')
8
8
  slug: Optional[str] = Field(None, alias='slug')
9
- client_id: Optional[int] = Field(None, alias='clientId')
10
- instance_rented_id: Optional[int] = Field(None, alias='instanceRentedId')
11
- selfhosted_instance_id: Optional[int] = Field(None, alias='selfhostedInstanceId')
12
- project_id: Optional[int] = Field(None, alias='projectId')
13
9
  status: Optional[str] = Field(None, alias='status')
14
10
  environment_metadata: Optional[Dict[str, Any]] = Field(None, alias='environmentMetadata')
15
11
  commit_hash: Optional[str] = Field(None, alias='commitHash')
16
- cpu_architecture: Optional[str] = Field(None, alias='cpuArchitecture')
17
- gpu_model: Optional[str] = Field(None, alias='gpuModel')
18
- ecr_image_url: Optional[str] = Field(None, alias='ecrImageUrl')
19
- s3_artifacts_url: Optional[str] = Field(None, alias='s3ArtifactsUrl')
20
12
  created_at: Optional[str] = Field(None, alias='createdAt')
21
- updated_at: Optional[str] = Field(None, alias='updatedAt')
13
+ updated_at: Optional[str] = Field(None, alias='updatedAt')
@@ -6,9 +6,10 @@ from pydantic import Field, ConfigDict, BaseModel
6
6
  class ProjectConfig(BaseModel):
7
7
  model_config = ConfigDict(use_enum_values=True)
8
8
 
9
- id: int = Field(None, alias='id')
10
9
  slug: str = Field(None, alias='slug')
10
+ public_id: str = Field(None, alias='public_id')
11
11
  git_repository_url: str = Field(None, alias='git_repository_url')
12
12
  deploy_key_path: str = Field(None, alias='deploy_key_path')
13
- default_container_uid: Optional[str] = Field(None, alias='default_container_uid')
13
+ default_container_uid: Optional[str] = Field(None, alias='default_container_uid') # TODO delete
14
+ default_container_public_id: Optional[str] = Field(None, alias='default_container_public_id') # TODO use
14
15
  prompt_for_default_container: bool = Field(True, alias='prompt_for_default_container')
@@ -12,6 +12,7 @@ class ProjectInferenceSimulatorMapper(AbstractMapper):
12
12
  return None
13
13
 
14
14
  return ProjectInferenceSimulatorEntity(
15
+ public_id=item.public_id or '',
15
16
  slug=item.slug or '',
16
17
  status=item.status or '',
17
18
  http_endpoint=item.http_endpoint or '',
@@ -11,11 +11,11 @@ class ProjectInferenceSimulatorModelMapper(AbstractMapper):
11
11
  return None
12
12
 
13
13
  return ProjectInferenceSimulatorModelEntity(
14
- id=item.id,
14
+ public_id=item.public_id,
15
15
  slug=item.slug,
16
16
  status=item.status or '',
17
17
  commit_hash=item.commit_hash or '',
18
- environment_metadata=item.environment_metadata or '',
18
+ environment_metadata=item.environment_metadata or {},
19
19
  started_at=item.created_at or '',
20
- finished_at=item.updated_at or ''
20
+ finished_at=item.updated_at or '' # TODO updated_at cannot be finished_at
21
21
  )
@@ -12,11 +12,10 @@ class ProjectTaskMapper(AbstractMapper):
12
12
  return None
13
13
 
14
14
  return ProjectTaskEntity(
15
- id=item.id or '',
15
+ public_id=item.public_id or '',
16
16
  title=item.title or '',
17
17
  status=item.frontend_status.status_translation or '',
18
- docker_container_id=item.docker_container_id or '',
19
- docker_container_slug=(str(item.docker_container.slug) or '') if item.docker_container else '',
18
+ docker_container_public_id=item.docker_container_public_id,
20
19
  started_at=item.started_at or '',
21
20
  finished_at=item.finished_at or '',
22
21
  )