dstack 0.0.9__py3-none-any.whl → 0.20.7__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 (879) hide show
  1. dstack/_internal/cli/commands/__init__.py +80 -0
  2. dstack/_internal/cli/commands/apply.py +100 -0
  3. dstack/_internal/cli/commands/attach.py +161 -0
  4. dstack/_internal/cli/commands/completion.py +22 -0
  5. dstack/_internal/cli/commands/delete.py +44 -0
  6. dstack/_internal/cli/commands/event.py +168 -0
  7. dstack/_internal/cli/commands/fleet.py +161 -0
  8. dstack/_internal/cli/commands/gateway.py +159 -0
  9. dstack/_internal/cli/commands/init.py +64 -0
  10. dstack/_internal/cli/commands/login.py +352 -0
  11. dstack/_internal/cli/commands/logs.py +62 -0
  12. dstack/_internal/cli/commands/metrics.py +153 -0
  13. dstack/_internal/cli/commands/offer.py +146 -0
  14. dstack/_internal/cli/commands/project.py +259 -0
  15. dstack/_internal/cli/commands/ps.py +81 -0
  16. dstack/_internal/cli/commands/run.py +69 -0
  17. dstack/_internal/cli/commands/secrets.py +92 -0
  18. dstack/_internal/cli/commands/server.py +96 -0
  19. dstack/_internal/cli/commands/stop.py +26 -0
  20. dstack/_internal/cli/commands/volume.py +117 -0
  21. dstack/_internal/cli/main.py +101 -0
  22. dstack/_internal/cli/models/gateways.py +16 -0
  23. dstack/_internal/cli/models/offers.py +47 -0
  24. dstack/_internal/cli/models/runs.py +16 -0
  25. dstack/_internal/cli/services/args.py +31 -0
  26. dstack/_internal/cli/services/completion.py +91 -0
  27. dstack/_internal/cli/services/configurators/__init__.py +86 -0
  28. dstack/_internal/cli/services/configurators/base.py +103 -0
  29. dstack/_internal/cli/services/configurators/fleet.py +475 -0
  30. dstack/_internal/cli/services/configurators/gateway.py +231 -0
  31. dstack/_internal/cli/services/configurators/run.py +882 -0
  32. dstack/_internal/cli/services/configurators/volume.py +222 -0
  33. dstack/_internal/cli/services/events.py +68 -0
  34. dstack/_internal/cli/services/profile.py +182 -0
  35. dstack/_internal/cli/services/repos.py +71 -0
  36. dstack/_internal/cli/services/resources.py +54 -0
  37. dstack/_internal/cli/utils/common.py +159 -0
  38. dstack/_internal/cli/utils/fleet.py +106 -0
  39. dstack/_internal/cli/utils/gateway.py +56 -0
  40. dstack/_internal/cli/utils/gpu.py +178 -0
  41. dstack/_internal/cli/utils/rich.py +156 -0
  42. dstack/_internal/cli/utils/run.py +517 -0
  43. dstack/_internal/cli/utils/secrets.py +25 -0
  44. dstack/_internal/cli/utils/updates.py +98 -0
  45. dstack/_internal/cli/utils/volume.py +58 -0
  46. dstack/_internal/compat.py +3 -0
  47. dstack/_internal/core/backends/amddevcloud/__init__.py +1 -0
  48. dstack/_internal/core/backends/amddevcloud/backend.py +16 -0
  49. dstack/_internal/core/backends/amddevcloud/compute.py +5 -0
  50. dstack/_internal/core/backends/amddevcloud/configurator.py +29 -0
  51. dstack/_internal/core/backends/aws/auth.py +30 -0
  52. dstack/_internal/core/backends/aws/backend.py +31 -0
  53. dstack/_internal/core/backends/aws/compute.py +1153 -0
  54. dstack/_internal/core/backends/aws/configurator.py +191 -0
  55. dstack/_internal/core/backends/aws/models.py +135 -0
  56. dstack/_internal/core/backends/aws/resources.py +700 -0
  57. dstack/_internal/core/backends/azure/auth.py +39 -0
  58. dstack/_internal/core/backends/azure/backend.py +21 -0
  59. dstack/_internal/core/backends/azure/compute.py +676 -0
  60. dstack/_internal/core/backends/azure/configurator.py +472 -0
  61. dstack/_internal/core/backends/azure/models.py +98 -0
  62. dstack/_internal/core/backends/azure/resources.py +116 -0
  63. dstack/_internal/core/backends/azure/utils.py +42 -0
  64. dstack/_internal/core/backends/base/backend.py +18 -0
  65. dstack/_internal/core/backends/base/compute.py +1101 -0
  66. dstack/_internal/core/backends/base/configurator.py +117 -0
  67. dstack/_internal/core/backends/base/models.py +24 -0
  68. dstack/_internal/core/backends/base/offers.py +232 -0
  69. dstack/_internal/core/backends/cloudrift/api_client.py +220 -0
  70. dstack/_internal/core/backends/cloudrift/backend.py +16 -0
  71. dstack/_internal/core/backends/cloudrift/compute.py +138 -0
  72. dstack/_internal/core/backends/cloudrift/configurator.py +72 -0
  73. dstack/_internal/core/backends/cloudrift/models.py +40 -0
  74. dstack/_internal/core/backends/configurators.py +181 -0
  75. dstack/_internal/core/backends/cudo/__init__.py +0 -0
  76. dstack/_internal/core/backends/cudo/api_client.py +111 -0
  77. dstack/_internal/core/backends/cudo/backend.py +16 -0
  78. dstack/_internal/core/backends/cudo/compute.py +174 -0
  79. dstack/_internal/core/backends/cudo/configurator.py +63 -0
  80. dstack/_internal/core/backends/cudo/models.py +37 -0
  81. dstack/_internal/core/backends/datacrunch/__init__.py +1 -0
  82. dstack/_internal/core/backends/datacrunch/backend.py +18 -0
  83. dstack/_internal/core/backends/datacrunch/compute.py +8 -0
  84. dstack/_internal/core/backends/datacrunch/configurator.py +17 -0
  85. dstack/_internal/core/backends/digitalocean/__init__.py +1 -0
  86. dstack/_internal/core/backends/digitalocean/backend.py +16 -0
  87. dstack/_internal/core/backends/digitalocean/compute.py +5 -0
  88. dstack/_internal/core/backends/digitalocean/configurator.py +31 -0
  89. dstack/_internal/core/backends/digitalocean_base/__init__.py +1 -0
  90. dstack/_internal/core/backends/digitalocean_base/api_client.py +104 -0
  91. dstack/_internal/core/backends/digitalocean_base/backend.py +5 -0
  92. dstack/_internal/core/backends/digitalocean_base/compute.py +174 -0
  93. dstack/_internal/core/backends/digitalocean_base/configurator.py +57 -0
  94. dstack/_internal/core/backends/digitalocean_base/models.py +43 -0
  95. dstack/_internal/core/backends/dstack/__init__.py +0 -0
  96. dstack/_internal/core/backends/dstack/models.py +26 -0
  97. dstack/_internal/core/backends/features.py +74 -0
  98. dstack/_internal/core/backends/gcp/__init__.py +0 -0
  99. dstack/_internal/core/backends/gcp/auth.py +57 -0
  100. dstack/_internal/core/backends/gcp/backend.py +17 -0
  101. dstack/_internal/core/backends/gcp/compute.py +1257 -0
  102. dstack/_internal/core/backends/gcp/configurator.py +206 -0
  103. dstack/_internal/core/backends/gcp/features/__init__.py +0 -0
  104. dstack/_internal/core/backends/gcp/features/tcpx.py +65 -0
  105. dstack/_internal/core/backends/gcp/models.py +160 -0
  106. dstack/_internal/core/backends/gcp/resources.py +585 -0
  107. dstack/_internal/core/backends/hotaisle/__init__.py +1 -0
  108. dstack/_internal/core/backends/hotaisle/api_client.py +101 -0
  109. dstack/_internal/core/backends/hotaisle/backend.py +16 -0
  110. dstack/_internal/core/backends/hotaisle/compute.py +188 -0
  111. dstack/_internal/core/backends/hotaisle/configurator.py +66 -0
  112. dstack/_internal/core/backends/hotaisle/models.py +45 -0
  113. dstack/_internal/core/backends/kubernetes/__init__.py +0 -0
  114. dstack/_internal/core/backends/kubernetes/backend.py +16 -0
  115. dstack/_internal/core/backends/kubernetes/compute.py +1077 -0
  116. dstack/_internal/core/backends/kubernetes/configurator.py +61 -0
  117. dstack/_internal/core/backends/kubernetes/models.py +71 -0
  118. dstack/_internal/core/backends/kubernetes/utils.py +81 -0
  119. dstack/_internal/core/backends/lambdalabs/__init__.py +0 -0
  120. dstack/_internal/core/backends/lambdalabs/api_client.py +87 -0
  121. dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
  122. dstack/_internal/core/backends/lambdalabs/compute.py +233 -0
  123. dstack/_internal/core/backends/lambdalabs/configurator.py +65 -0
  124. dstack/_internal/core/backends/lambdalabs/models.py +37 -0
  125. dstack/_internal/core/backends/local/__init__.py +0 -0
  126. dstack/_internal/core/backends/local/backend.py +14 -0
  127. dstack/_internal/core/backends/local/compute.py +130 -0
  128. dstack/_internal/core/backends/models.py +158 -0
  129. dstack/_internal/core/backends/nebius/__init__.py +0 -0
  130. dstack/_internal/core/backends/nebius/backend.py +16 -0
  131. dstack/_internal/core/backends/nebius/compute.py +401 -0
  132. dstack/_internal/core/backends/nebius/configurator.py +98 -0
  133. dstack/_internal/core/backends/nebius/models.py +185 -0
  134. dstack/_internal/core/backends/nebius/resources.py +433 -0
  135. dstack/_internal/core/backends/oci/__init__.py +0 -0
  136. dstack/_internal/core/backends/oci/auth.py +21 -0
  137. dstack/_internal/core/backends/oci/backend.py +16 -0
  138. dstack/_internal/core/backends/oci/compute.py +209 -0
  139. dstack/_internal/core/backends/oci/configurator.py +156 -0
  140. dstack/_internal/core/backends/oci/exceptions.py +15 -0
  141. dstack/_internal/core/backends/oci/models.py +87 -0
  142. dstack/_internal/core/backends/oci/region.py +86 -0
  143. dstack/_internal/core/backends/oci/resources.py +836 -0
  144. dstack/_internal/core/backends/runpod/__init__.py +0 -0
  145. dstack/_internal/core/backends/runpod/api_client.py +627 -0
  146. dstack/_internal/core/backends/runpod/backend.py +16 -0
  147. dstack/_internal/core/backends/runpod/compute.py +444 -0
  148. dstack/_internal/core/backends/runpod/configurator.py +63 -0
  149. dstack/_internal/core/backends/runpod/models.py +54 -0
  150. dstack/_internal/core/backends/template/__init__.py +0 -0
  151. dstack/_internal/core/backends/template/backend.py.jinja +16 -0
  152. dstack/_internal/core/backends/template/compute.py.jinja +95 -0
  153. dstack/_internal/core/backends/template/configurator.py.jinja +69 -0
  154. dstack/_internal/core/backends/template/models.py.jinja +62 -0
  155. dstack/_internal/core/backends/tensordock/models.py +40 -0
  156. dstack/_internal/core/backends/vastai/__init__.py +0 -0
  157. dstack/_internal/core/backends/vastai/api_client.py +143 -0
  158. dstack/_internal/core/backends/vastai/backend.py +16 -0
  159. dstack/_internal/core/backends/vastai/compute.py +141 -0
  160. dstack/_internal/core/backends/vastai/configurator.py +69 -0
  161. dstack/_internal/core/backends/vastai/models.py +37 -0
  162. dstack/_internal/core/backends/verda/__init__.py +0 -0
  163. dstack/_internal/core/backends/verda/backend.py +16 -0
  164. dstack/_internal/core/backends/verda/compute.py +266 -0
  165. dstack/_internal/core/backends/verda/configurator.py +73 -0
  166. dstack/_internal/core/backends/verda/models.py +38 -0
  167. dstack/_internal/core/backends/vultr/__init__.py +0 -0
  168. dstack/_internal/core/backends/vultr/api_client.py +116 -0
  169. dstack/_internal/core/backends/vultr/backend.py +16 -0
  170. dstack/_internal/core/backends/vultr/compute.py +167 -0
  171. dstack/_internal/core/backends/vultr/configurator.py +71 -0
  172. dstack/_internal/core/backends/vultr/models.py +34 -0
  173. dstack/_internal/core/compatibility/__init__.py +0 -0
  174. dstack/_internal/core/compatibility/events.py +13 -0
  175. dstack/_internal/core/compatibility/fleets.py +58 -0
  176. dstack/_internal/core/compatibility/gateways.py +39 -0
  177. dstack/_internal/core/compatibility/gpus.py +13 -0
  178. dstack/_internal/core/compatibility/logs.py +14 -0
  179. dstack/_internal/core/compatibility/runs.py +86 -0
  180. dstack/_internal/core/compatibility/volumes.py +37 -0
  181. dstack/_internal/core/consts.py +8 -0
  182. dstack/_internal/core/errors.py +160 -0
  183. dstack/_internal/core/models/__init__.py +0 -0
  184. dstack/_internal/core/models/auth.py +28 -0
  185. dstack/_internal/core/models/backends/__init__.py +0 -0
  186. dstack/_internal/core/models/backends/base.py +48 -0
  187. dstack/_internal/core/models/common.py +143 -0
  188. dstack/_internal/core/models/compute_groups.py +39 -0
  189. dstack/_internal/core/models/config.py +28 -0
  190. dstack/_internal/core/models/configurations.py +1123 -0
  191. dstack/_internal/core/models/envs.py +149 -0
  192. dstack/_internal/core/models/events.py +98 -0
  193. dstack/_internal/core/models/files.py +67 -0
  194. dstack/_internal/core/models/fleets.py +437 -0
  195. dstack/_internal/core/models/gateways.py +146 -0
  196. dstack/_internal/core/models/gpus.py +45 -0
  197. dstack/_internal/core/models/health.py +28 -0
  198. dstack/_internal/core/models/instances.py +346 -0
  199. dstack/_internal/core/models/logs.py +27 -0
  200. dstack/_internal/core/models/metrics.py +14 -0
  201. dstack/_internal/core/models/placement.py +27 -0
  202. dstack/_internal/core/models/profiles.py +431 -0
  203. dstack/_internal/core/models/projects.py +46 -0
  204. dstack/_internal/core/models/repos/__init__.py +34 -0
  205. dstack/_internal/core/models/repos/base.py +36 -0
  206. dstack/_internal/core/models/repos/local.py +96 -0
  207. dstack/_internal/core/models/repos/remote.py +341 -0
  208. dstack/_internal/core/models/repos/virtual.py +85 -0
  209. dstack/_internal/core/models/resources.py +424 -0
  210. dstack/_internal/core/models/routers.py +24 -0
  211. dstack/_internal/core/models/runs.py +618 -0
  212. dstack/_internal/core/models/secrets.py +16 -0
  213. dstack/_internal/core/models/server.py +7 -0
  214. dstack/_internal/core/models/services.py +76 -0
  215. dstack/_internal/core/models/unix.py +53 -0
  216. dstack/_internal/core/models/users.py +60 -0
  217. dstack/_internal/core/models/volumes.py +221 -0
  218. dstack/_internal/core/services/__init__.py +16 -0
  219. dstack/_internal/core/services/api_client.py +15 -0
  220. dstack/_internal/core/services/configs/__init__.py +116 -0
  221. dstack/_internal/core/services/diff.py +71 -0
  222. dstack/_internal/core/services/logs.py +58 -0
  223. dstack/_internal/core/services/profiles.py +46 -0
  224. dstack/_internal/core/services/repos.py +236 -0
  225. dstack/_internal/core/services/ssh/__init__.py +27 -0
  226. dstack/_internal/core/services/ssh/attach.py +241 -0
  227. dstack/_internal/core/services/ssh/client.py +113 -0
  228. dstack/_internal/core/services/ssh/key_manager.py +53 -0
  229. dstack/_internal/core/services/ssh/ports.py +89 -0
  230. dstack/_internal/core/services/ssh/tunnel.py +337 -0
  231. dstack/_internal/proxy/__init__.py +8 -0
  232. dstack/_internal/proxy/gateway/__init__.py +0 -0
  233. dstack/_internal/proxy/gateway/app.py +89 -0
  234. dstack/_internal/proxy/gateway/auth.py +26 -0
  235. dstack/_internal/proxy/gateway/const.py +7 -0
  236. dstack/_internal/proxy/gateway/deps.py +73 -0
  237. dstack/_internal/proxy/gateway/main.py +17 -0
  238. dstack/_internal/proxy/gateway/models.py +23 -0
  239. dstack/_internal/proxy/gateway/repo/__init__.py +0 -0
  240. dstack/_internal/proxy/gateway/repo/repo.py +121 -0
  241. dstack/_internal/proxy/gateway/repo/state_v1.py +164 -0
  242. dstack/_internal/proxy/gateway/resources/nginx/00-log-format.conf +11 -0
  243. dstack/_internal/proxy/gateway/resources/nginx/entrypoint.jinja2 +27 -0
  244. dstack/_internal/proxy/gateway/resources/nginx/router_workers.jinja2 +23 -0
  245. dstack/_internal/proxy/gateway/resources/nginx/service.jinja2 +105 -0
  246. dstack/_internal/proxy/gateway/routers/__init__.py +0 -0
  247. dstack/_internal/proxy/gateway/routers/auth.py +10 -0
  248. dstack/_internal/proxy/gateway/routers/config.py +28 -0
  249. dstack/_internal/proxy/gateway/routers/registry.py +124 -0
  250. dstack/_internal/proxy/gateway/routers/stats.py +18 -0
  251. dstack/_internal/proxy/gateway/schemas/__init__.py +0 -0
  252. dstack/_internal/proxy/gateway/schemas/common.py +5 -0
  253. dstack/_internal/proxy/gateway/schemas/config.py +9 -0
  254. dstack/_internal/proxy/gateway/schemas/registry.py +63 -0
  255. dstack/_internal/proxy/gateway/schemas/stats.py +15 -0
  256. dstack/_internal/proxy/gateway/services/__init__.py +0 -0
  257. dstack/_internal/proxy/gateway/services/model_routers/__init__.py +18 -0
  258. dstack/_internal/proxy/gateway/services/model_routers/base.py +91 -0
  259. dstack/_internal/proxy/gateway/services/model_routers/sglang.py +269 -0
  260. dstack/_internal/proxy/gateway/services/nginx.py +455 -0
  261. dstack/_internal/proxy/gateway/services/registry.py +426 -0
  262. dstack/_internal/proxy/gateway/services/server_client.py +95 -0
  263. dstack/_internal/proxy/gateway/services/stats.py +170 -0
  264. dstack/_internal/proxy/gateway/testing/__init__.py +0 -0
  265. dstack/_internal/proxy/gateway/testing/common.py +13 -0
  266. dstack/_internal/proxy/lib/__init__.py +0 -0
  267. dstack/_internal/proxy/lib/auth.py +7 -0
  268. dstack/_internal/proxy/lib/deps.py +106 -0
  269. dstack/_internal/proxy/lib/errors.py +14 -0
  270. dstack/_internal/proxy/lib/models.py +112 -0
  271. dstack/_internal/proxy/lib/repo.py +27 -0
  272. dstack/_internal/proxy/lib/routers/__init__.py +0 -0
  273. dstack/_internal/proxy/lib/routers/model_proxy.py +102 -0
  274. dstack/_internal/proxy/lib/schemas/__init__.py +0 -0
  275. dstack/_internal/proxy/lib/schemas/model_proxy.py +77 -0
  276. dstack/_internal/proxy/lib/services/__init__.py +0 -0
  277. dstack/_internal/proxy/lib/services/model_proxy/__init__.py +0 -0
  278. dstack/_internal/proxy/lib/services/model_proxy/clients/__init__.py +0 -0
  279. dstack/_internal/proxy/lib/services/model_proxy/clients/base.py +18 -0
  280. dstack/_internal/proxy/lib/services/model_proxy/clients/openai.py +67 -0
  281. dstack/_internal/proxy/lib/services/model_proxy/clients/tgi.py +208 -0
  282. dstack/_internal/proxy/lib/services/model_proxy/model_proxy.py +23 -0
  283. dstack/_internal/proxy/lib/services/service_connection.py +160 -0
  284. dstack/_internal/proxy/lib/testing/__init__.py +0 -0
  285. dstack/_internal/proxy/lib/testing/auth.py +11 -0
  286. dstack/_internal/proxy/lib/testing/common.py +51 -0
  287. dstack/_internal/server/__init__.py +0 -0
  288. dstack/_internal/server/alembic.ini +100 -0
  289. dstack/_internal/server/app.py +432 -0
  290. dstack/_internal/server/background/__init__.py +142 -0
  291. dstack/_internal/server/background/tasks/__init__.py +0 -0
  292. dstack/_internal/server/background/tasks/common.py +24 -0
  293. dstack/_internal/server/background/tasks/process_compute_groups.py +167 -0
  294. dstack/_internal/server/background/tasks/process_events.py +17 -0
  295. dstack/_internal/server/background/tasks/process_fleets.py +289 -0
  296. dstack/_internal/server/background/tasks/process_gateways.py +188 -0
  297. dstack/_internal/server/background/tasks/process_idle_volumes.py +145 -0
  298. dstack/_internal/server/background/tasks/process_instances.py +1186 -0
  299. dstack/_internal/server/background/tasks/process_metrics.py +172 -0
  300. dstack/_internal/server/background/tasks/process_placement_groups.py +104 -0
  301. dstack/_internal/server/background/tasks/process_probes.py +164 -0
  302. dstack/_internal/server/background/tasks/process_prometheus_metrics.py +150 -0
  303. dstack/_internal/server/background/tasks/process_running_jobs.py +1238 -0
  304. dstack/_internal/server/background/tasks/process_runs.py +842 -0
  305. dstack/_internal/server/background/tasks/process_submitted_jobs.py +1106 -0
  306. dstack/_internal/server/background/tasks/process_terminating_jobs.py +108 -0
  307. dstack/_internal/server/background/tasks/process_volumes.py +129 -0
  308. dstack/_internal/server/compatibility/__init__.py +0 -0
  309. dstack/_internal/server/compatibility/common.py +20 -0
  310. dstack/_internal/server/compatibility/gpus.py +22 -0
  311. dstack/_internal/server/db.py +127 -0
  312. dstack/_internal/server/deps.py +19 -0
  313. dstack/_internal/server/main.py +4 -0
  314. dstack/_internal/server/migrations/__init__.py +0 -0
  315. dstack/_internal/server/migrations/env.py +112 -0
  316. dstack/_internal/server/migrations/script.py.mako +28 -0
  317. dstack/_internal/server/migrations/versions/006512f572b4_add_projects_original_name.py +38 -0
  318. dstack/_internal/server/migrations/versions/065588ec72b8_add_vultr_to_backendtype_enum.py +81 -0
  319. dstack/_internal/server/migrations/versions/06e977bc61c7_add_usermodel_deleted_and_original_name.py +45 -0
  320. dstack/_internal/server/migrations/versions/0e33559e16ed_update_instancestatus.py +64 -0
  321. dstack/_internal/server/migrations/versions/112753bc17dd_remove_nullable_fields.py +50 -0
  322. dstack/_internal/server/migrations/versions/1338b788b612_reverse_job_instance_relationship.py +71 -0
  323. dstack/_internal/server/migrations/versions/14f2cb002fc2_add_jobmodel_removed_flag.py +44 -0
  324. dstack/_internal/server/migrations/versions/1a48dfe44a40_rework_termination_handling.py +42 -0
  325. dstack/_internal/server/migrations/versions/1aa9638ad963_added_email_index.py +31 -0
  326. dstack/_internal/server/migrations/versions/1e3fb39ef74b_add_remote_connection_details.py +26 -0
  327. dstack/_internal/server/migrations/versions/1e76fb0dde87_add_jobmodel_inactivity_secs.py +32 -0
  328. dstack/_internal/server/migrations/versions/20166748b60c_add_jobmodel_disconnected_at.py +100 -0
  329. dstack/_internal/server/migrations/versions/22d74df9897e_add_events_and_event_targets.py +99 -0
  330. dstack/_internal/server/migrations/versions/23e01c56279a_make_blob_nullable.py +32 -0
  331. dstack/_internal/server/migrations/versions/2498ab323443_add_fleetmodel_consolidation_attempt_.py +44 -0
  332. dstack/_internal/server/migrations/versions/252d3743b641_.py +40 -0
  333. dstack/_internal/server/migrations/versions/25479f540245_add_probes.py +43 -0
  334. dstack/_internal/server/migrations/versions/27d3e55759fa_add_pools.py +152 -0
  335. dstack/_internal/server/migrations/versions/29826f417010_remove_instancemodel_retry_policy.py +34 -0
  336. dstack/_internal/server/migrations/versions/29c08c6a8cb3_.py +36 -0
  337. dstack/_internal/server/migrations/versions/35e90e1b0d3e_add_rolling_deployment_fields.py +42 -0
  338. dstack/_internal/server/migrations/versions/35f732ee4cf5_add_projectmodel_is_public.py +39 -0
  339. dstack/_internal/server/migrations/versions/3cf77fb8bcf1_store_repo_clone_url.py +85 -0
  340. dstack/_internal/server/migrations/versions/3d7f6c2ec000_add_jobmodel_registered.py +28 -0
  341. dstack/_internal/server/migrations/versions/3dbdce90d0e0_fix_code_uq_constraint.py +33 -0
  342. dstack/_internal/server/migrations/versions/48ad3ecbaea2_do_not_delete_projects_and_runs.py +46 -0
  343. dstack/_internal/server/migrations/versions/4ae1a5b0e7f1_add_run_list_index.py +34 -0
  344. dstack/_internal/server/migrations/versions/4b4319398164_introduce_runs_processing.py +144 -0
  345. dstack/_internal/server/migrations/versions/50dd7ea98639_index_status_columns.py +55 -0
  346. dstack/_internal/server/migrations/versions/51d45659d574_add_instancemodel_blocks_fields.py +43 -0
  347. dstack/_internal/server/migrations/versions/54a77e19c64c_add_manager_project_role.py +67 -0
  348. dstack/_internal/server/migrations/versions/555138b1f77f_change_instancemodel_for_asynchronous_.py +61 -0
  349. dstack/_internal/server/migrations/versions/58aa5162dcc3_add_gatewaymodel_configuration.py +32 -0
  350. dstack/_internal/server/migrations/versions/5ad8debc8fe6_fixes_for_psql.py +329 -0
  351. dstack/_internal/server/migrations/versions/5ec538b70e71_replace_instansestatus.py +31 -0
  352. dstack/_internal/server/migrations/versions/5f1707c525d2_add_filearchivemodel.py +39 -0
  353. dstack/_internal/server/migrations/versions/5fd659afca82_add_ix_instances_fleet_id.py +31 -0
  354. dstack/_internal/server/migrations/versions/60e444118b6d_add_jobprometheusmetrics.py +40 -0
  355. dstack/_internal/server/migrations/versions/63c3f19cb184_add_jobterminationreason_inactivity_.py +83 -0
  356. dstack/_internal/server/migrations/versions/644b8a114187_add_secretmodel.py +49 -0
  357. dstack/_internal/server/migrations/versions/686fb8341ea5_add_user_emails.py +32 -0
  358. dstack/_internal/server/migrations/versions/6c1a9d6530ee_add_jobmodel_exit_status.py +26 -0
  359. dstack/_internal/server/migrations/versions/706e0acc3a7d_add_runmodel_desired_replica_counts.py +26 -0
  360. dstack/_internal/server/migrations/versions/710e5b3fac8f_add_encryption.py +54 -0
  361. dstack/_internal/server/migrations/versions/728b1488b1b4_add_instance_health.py +50 -0
  362. dstack/_internal/server/migrations/versions/74a1f55209bd_store_enums_as_strings.py +484 -0
  363. dstack/_internal/server/migrations/versions/7b24b1c8eba7_add_instancemodel_last_processed_at.py +68 -0
  364. dstack/_internal/server/migrations/versions/7ba3b59d7ca6_add_runmodel_resubmission_attempt.py +35 -0
  365. dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
  366. dstack/_internal/server/migrations/versions/7d1ec2b920ac_add_computegroupmodel.py +91 -0
  367. dstack/_internal/server/migrations/versions/803c7e9ed85d_add_jobmodel_job_runtime_data.py +32 -0
  368. dstack/_internal/server/migrations/versions/82b32a135ea2_.py +58 -0
  369. dstack/_internal/server/migrations/versions/866ec1d67184_replace_retrypolicy_limit_with_.py +93 -0
  370. dstack/_internal/server/migrations/versions/903c91e24634_add_instances_termination_reason_message.py +34 -0
  371. dstack/_internal/server/migrations/versions/91a12fff6c76_add_repocredsmodel.py +43 -0
  372. dstack/_internal/server/migrations/versions/91ac5e543037_extend_repos_creds_column.py +36 -0
  373. dstack/_internal/server/migrations/versions/98cd9c8b5927_add_volumemodel.py +73 -0
  374. dstack/_internal/server/migrations/versions/98d1b92988bc_add_jobterminationreason_terminated_due_.py +140 -0
  375. dstack/_internal/server/migrations/versions/99b4c8c954ea_add_termination_reason_message.py +71 -0
  376. dstack/_internal/server/migrations/versions/9eea6af28e10_added_fail_reason_for_instancemodel.py +36 -0
  377. dstack/_internal/server/migrations/versions/__init__.py +0 -0
  378. dstack/_internal/server/migrations/versions/a060e2440936_.py +206 -0
  379. dstack/_internal/server/migrations/versions/a751ef183f27_move_attachment_data_to_volumes_.py +34 -0
  380. dstack/_internal/server/migrations/versions/a7b46c073fa1_add_placementgroupmodel.py +58 -0
  381. dstack/_internal/server/migrations/versions/afbc600ff2b2_add_created_at_to_usermodel_and_.py +102 -0
  382. dstack/_internal/server/migrations/versions/b4d6ad60db08_add_instancemodel_unreachable.py +37 -0
  383. dstack/_internal/server/migrations/versions/b88d55c2a07d_replace_instancestatus_ready.py +21 -0
  384. dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
  385. dstack/_internal/server/migrations/versions/bca2fdf130bf_add_runmodel_priority.py +34 -0
  386. dstack/_internal/server/migrations/versions/bfba43f6def2_.py +32 -0
  387. dstack/_internal/server/migrations/versions/c00090eaef21_support_fleets.py +108 -0
  388. dstack/_internal/server/migrations/versions/c154eece89da_add_fields_for_async_gateway_creation.py +74 -0
  389. dstack/_internal/server/migrations/versions/c20626d03cfb_add_jobmetricspoint.py +43 -0
  390. dstack/_internal/server/migrations/versions/c48df7985d57_add_instance_termination_retries.py +38 -0
  391. dstack/_internal/server/migrations/versions/c83d45f9a971_replace_string_with_text.py +150 -0
  392. dstack/_internal/server/migrations/versions/d0bb68e48b9f_add_project_owners_and_quotas.py +106 -0
  393. dstack/_internal/server/migrations/versions/d3e8af4786fa_gateway_compute_flag_deleted.py +34 -0
  394. dstack/_internal/server/migrations/versions/d4d9dc26cf58_add_ix_jobs_run_id.py +31 -0
  395. dstack/_internal/server/migrations/versions/d5863798bf41_add_volumemodel_last_job_processed_at.py +40 -0
  396. dstack/_internal/server/migrations/versions/d6b11105f659_add_usermodel_active.py +36 -0
  397. dstack/_internal/server/migrations/versions/da574e93fee0_add_jobmodel_volumes_detached_at.py +40 -0
  398. dstack/_internal/server/migrations/versions/dfffd6a1165c_add_fields_for_gateways_behind_alb.py +36 -0
  399. dstack/_internal/server/migrations/versions/e2d08cd1b8d9_add_jobmodel_fleet.py +41 -0
  400. dstack/_internal/server/migrations/versions/e3b7db07727f_add_gatewaycomputemodel_app_updated_at.py +61 -0
  401. dstack/_internal/server/migrations/versions/e6391ca6a264_separate_gateways_from_compute.py +72 -0
  402. dstack/_internal/server/migrations/versions/ea60480f82bb_add_membermodel_member_num.py +32 -0
  403. dstack/_internal/server/migrations/versions/ec02a26a256c_add_runmodel_next_triggered_at.py +38 -0
  404. dstack/_internal/server/migrations/versions/ed0ca30e13bb_migrate_instancestatus_provisioning.py +29 -0
  405. dstack/_internal/server/migrations/versions/fe72c4de8376_add_gateways.py +81 -0
  406. dstack/_internal/server/migrations/versions/ff1d94f65b08_user_ssh_key.py +34 -0
  407. dstack/_internal/server/migrations/versions/ffa99edd1988_add_jobterminationreason_max_duration_.py +81 -0
  408. dstack/_internal/server/models.py +930 -0
  409. dstack/_internal/server/routers/__init__.py +0 -0
  410. dstack/_internal/server/routers/auth.py +34 -0
  411. dstack/_internal/server/routers/backends.py +142 -0
  412. dstack/_internal/server/routers/events.py +60 -0
  413. dstack/_internal/server/routers/files.py +68 -0
  414. dstack/_internal/server/routers/fleets.py +202 -0
  415. dstack/_internal/server/routers/gateways.py +109 -0
  416. dstack/_internal/server/routers/gpus.py +32 -0
  417. dstack/_internal/server/routers/instances.py +77 -0
  418. dstack/_internal/server/routers/logs.py +34 -0
  419. dstack/_internal/server/routers/metrics.py +82 -0
  420. dstack/_internal/server/routers/projects.py +205 -0
  421. dstack/_internal/server/routers/prometheus.py +35 -0
  422. dstack/_internal/server/routers/repos.py +118 -0
  423. dstack/_internal/server/routers/runs.py +216 -0
  424. dstack/_internal/server/routers/secrets.py +86 -0
  425. dstack/_internal/server/routers/server.py +19 -0
  426. dstack/_internal/server/routers/users.py +158 -0
  427. dstack/_internal/server/routers/volumes.py +122 -0
  428. dstack/_internal/server/schemas/__init__.py +0 -0
  429. dstack/_internal/server/schemas/auth.py +83 -0
  430. dstack/_internal/server/schemas/backends.py +16 -0
  431. dstack/_internal/server/schemas/common.py +9 -0
  432. dstack/_internal/server/schemas/events.py +211 -0
  433. dstack/_internal/server/schemas/files.py +5 -0
  434. dstack/_internal/server/schemas/fleets.py +49 -0
  435. dstack/_internal/server/schemas/gateways.py +31 -0
  436. dstack/_internal/server/schemas/gpus.py +26 -0
  437. dstack/_internal/server/schemas/health/__init__.py +0 -0
  438. dstack/_internal/server/schemas/health/dcgm.py +56 -0
  439. dstack/_internal/server/schemas/instances.py +47 -0
  440. dstack/_internal/server/schemas/logs.py +17 -0
  441. dstack/_internal/server/schemas/projects.py +81 -0
  442. dstack/_internal/server/schemas/repos.py +24 -0
  443. dstack/_internal/server/schemas/runner.py +269 -0
  444. dstack/_internal/server/schemas/runs.py +66 -0
  445. dstack/_internal/server/schemas/secrets.py +16 -0
  446. dstack/_internal/server/schemas/users.py +72 -0
  447. dstack/_internal/server/schemas/volumes.py +29 -0
  448. dstack/_internal/server/security/__init__.py +0 -0
  449. dstack/_internal/server/security/permissions.py +251 -0
  450. dstack/_internal/server/services/__init__.py +0 -0
  451. dstack/_internal/server/services/auth.py +77 -0
  452. dstack/_internal/server/services/backends/__init__.py +404 -0
  453. dstack/_internal/server/services/backends/handlers.py +105 -0
  454. dstack/_internal/server/services/compute_groups.py +22 -0
  455. dstack/_internal/server/services/config.py +279 -0
  456. dstack/_internal/server/services/docker.py +162 -0
  457. dstack/_internal/server/services/encryption/__init__.py +102 -0
  458. dstack/_internal/server/services/encryption/keys/__init__.py +0 -0
  459. dstack/_internal/server/services/encryption/keys/aes.py +68 -0
  460. dstack/_internal/server/services/encryption/keys/base.py +19 -0
  461. dstack/_internal/server/services/encryption/keys/identity.py +28 -0
  462. dstack/_internal/server/services/events.py +477 -0
  463. dstack/_internal/server/services/files.py +91 -0
  464. dstack/_internal/server/services/fleets.py +1224 -0
  465. dstack/_internal/server/services/gateways/__init__.py +686 -0
  466. dstack/_internal/server/services/gateways/client.py +209 -0
  467. dstack/_internal/server/services/gateways/connection.py +139 -0
  468. dstack/_internal/server/services/gateways/pool.py +58 -0
  469. dstack/_internal/server/services/gpus.py +387 -0
  470. dstack/_internal/server/services/instances.py +731 -0
  471. dstack/_internal/server/services/jobs/__init__.py +840 -0
  472. dstack/_internal/server/services/jobs/configurators/__init__.py +0 -0
  473. dstack/_internal/server/services/jobs/configurators/base.py +469 -0
  474. dstack/_internal/server/services/jobs/configurators/dev.py +69 -0
  475. dstack/_internal/server/services/jobs/configurators/extensions/__init__.py +0 -0
  476. dstack/_internal/server/services/jobs/configurators/extensions/base.py +15 -0
  477. dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
  478. dstack/_internal/server/services/jobs/configurators/extensions/vscode.py +42 -0
  479. dstack/_internal/server/services/jobs/configurators/extensions/windsurf.py +43 -0
  480. dstack/_internal/server/services/jobs/configurators/service.py +28 -0
  481. dstack/_internal/server/services/jobs/configurators/task.py +39 -0
  482. dstack/_internal/server/services/locking.py +187 -0
  483. dstack/_internal/server/services/logging.py +29 -0
  484. dstack/_internal/server/services/logs/__init__.py +122 -0
  485. dstack/_internal/server/services/logs/aws.py +373 -0
  486. dstack/_internal/server/services/logs/base.py +47 -0
  487. dstack/_internal/server/services/logs/filelog.py +261 -0
  488. dstack/_internal/server/services/logs/fluentbit.py +329 -0
  489. dstack/_internal/server/services/logs/gcp.py +181 -0
  490. dstack/_internal/server/services/metrics.py +172 -0
  491. dstack/_internal/server/services/offers.py +249 -0
  492. dstack/_internal/server/services/permissions.py +37 -0
  493. dstack/_internal/server/services/placement.py +234 -0
  494. dstack/_internal/server/services/plugins.py +109 -0
  495. dstack/_internal/server/services/probes.py +10 -0
  496. dstack/_internal/server/services/projects.py +835 -0
  497. dstack/_internal/server/services/prometheus/__init__.py +0 -0
  498. dstack/_internal/server/services/prometheus/client_metrics.py +55 -0
  499. dstack/_internal/server/services/prometheus/custom_metrics.py +327 -0
  500. dstack/_internal/server/services/proxy/__init__.py +3 -0
  501. dstack/_internal/server/services/proxy/auth.py +12 -0
  502. dstack/_internal/server/services/proxy/deps.py +18 -0
  503. dstack/_internal/server/services/proxy/repo.py +189 -0
  504. dstack/_internal/server/services/proxy/routers/__init__.py +0 -0
  505. dstack/_internal/server/services/proxy/routers/service_proxy.py +49 -0
  506. dstack/_internal/server/services/proxy/services/__init__.py +0 -0
  507. dstack/_internal/server/services/proxy/services/service_proxy.py +135 -0
  508. dstack/_internal/server/services/repos.py +362 -0
  509. dstack/_internal/server/services/requirements/__init__.py +0 -0
  510. dstack/_internal/server/services/requirements/combine.py +260 -0
  511. dstack/_internal/server/services/resources.py +21 -0
  512. dstack/_internal/server/services/runner/__init__.py +0 -0
  513. dstack/_internal/server/services/runner/client.py +646 -0
  514. dstack/_internal/server/services/runner/ssh.py +128 -0
  515. dstack/_internal/server/services/runs/__init__.py +1026 -0
  516. dstack/_internal/server/services/runs/plan.py +703 -0
  517. dstack/_internal/server/services/runs/replicas.py +317 -0
  518. dstack/_internal/server/services/runs/spec.py +191 -0
  519. dstack/_internal/server/services/secrets.py +245 -0
  520. dstack/_internal/server/services/services/__init__.py +345 -0
  521. dstack/_internal/server/services/services/autoscalers.py +140 -0
  522. dstack/_internal/server/services/services/options.py +53 -0
  523. dstack/_internal/server/services/ssh.py +67 -0
  524. dstack/_internal/server/services/storage/__init__.py +37 -0
  525. dstack/_internal/server/services/storage/base.py +48 -0
  526. dstack/_internal/server/services/storage/gcs.py +66 -0
  527. dstack/_internal/server/services/storage/s3.py +69 -0
  528. dstack/_internal/server/services/users.py +461 -0
  529. dstack/_internal/server/services/volumes.py +496 -0
  530. dstack/_internal/server/settings.py +161 -0
  531. dstack/_internal/server/statics/00a6e1fb461ed2929fb9.png +0 -0
  532. dstack/_internal/server/statics/0cae4d9f0a36034984a7.png +0 -0
  533. dstack/_internal/server/statics/391de232cc0e30cae513.png +0 -0
  534. dstack/_internal/server/statics/4e0eead8c1a73689ef9d.svg +1 -0
  535. dstack/_internal/server/statics/544afa2f63428c2235b0.png +0 -0
  536. dstack/_internal/server/statics/54a4f50f74c6b9381530.svg +7 -0
  537. dstack/_internal/server/statics/68dd1360a7d2611e0132.svg +4 -0
  538. dstack/_internal/server/statics/69544b4c81973b54a66f.png +0 -0
  539. dstack/_internal/server/statics/77a8b02b17af19e39266.png +0 -0
  540. dstack/_internal/server/statics/83a93a8871c219104367.svg +9 -0
  541. dstack/_internal/server/statics/8f28bb8e9999e5e6a48b.svg +4 -0
  542. dstack/_internal/server/statics/9124086961ab8c366bc4.svg +9 -0
  543. dstack/_internal/server/statics/9a9ebaeb54b025dbac0a.svg +5 -0
  544. dstack/_internal/server/statics/a3428392dc534f3b15c4.svg +7 -0
  545. dstack/_internal/server/statics/ae22625574d69361f72c.png +0 -0
  546. dstack/_internal/server/statics/assets/android-chrome-144x144.png +0 -0
  547. dstack/_internal/server/statics/assets/android-chrome-192x192.png +0 -0
  548. dstack/_internal/server/statics/assets/android-chrome-256x256.png +0 -0
  549. dstack/_internal/server/statics/assets/android-chrome-36x36.png +0 -0
  550. dstack/_internal/server/statics/assets/android-chrome-384x384.png +0 -0
  551. dstack/_internal/server/statics/assets/android-chrome-48x48.png +0 -0
  552. dstack/_internal/server/statics/assets/android-chrome-512x512.png +0 -0
  553. dstack/_internal/server/statics/assets/android-chrome-72x72.png +0 -0
  554. dstack/_internal/server/statics/assets/android-chrome-96x96.png +0 -0
  555. dstack/_internal/server/statics/assets/apple-touch-icon-1024x1024.png +0 -0
  556. dstack/_internal/server/statics/assets/apple-touch-icon-114x114.png +0 -0
  557. dstack/_internal/server/statics/assets/apple-touch-icon-120x120.png +0 -0
  558. dstack/_internal/server/statics/assets/apple-touch-icon-144x144.png +0 -0
  559. dstack/_internal/server/statics/assets/apple-touch-icon-152x152.png +0 -0
  560. dstack/_internal/server/statics/assets/apple-touch-icon-167x167.png +0 -0
  561. dstack/_internal/server/statics/assets/apple-touch-icon-180x180.png +0 -0
  562. dstack/_internal/server/statics/assets/apple-touch-icon-57x57.png +0 -0
  563. dstack/_internal/server/statics/assets/apple-touch-icon-60x60.png +0 -0
  564. dstack/_internal/server/statics/assets/apple-touch-icon-72x72.png +0 -0
  565. dstack/_internal/server/statics/assets/apple-touch-icon-76x76.png +0 -0
  566. dstack/_internal/server/statics/assets/apple-touch-icon-precomposed.png +0 -0
  567. dstack/_internal/server/statics/assets/apple-touch-icon.png +0 -0
  568. dstack/_internal/server/statics/assets/apple-touch-startup-image-1125x2436.png +0 -0
  569. dstack/_internal/server/statics/assets/apple-touch-startup-image-1136x640.png +0 -0
  570. dstack/_internal/server/statics/assets/apple-touch-startup-image-1170x2532.png +0 -0
  571. dstack/_internal/server/statics/assets/apple-touch-startup-image-1179x2556.png +0 -0
  572. dstack/_internal/server/statics/assets/apple-touch-startup-image-1242x2208.png +0 -0
  573. dstack/_internal/server/statics/assets/apple-touch-startup-image-1242x2688.png +0 -0
  574. dstack/_internal/server/statics/assets/apple-touch-startup-image-1284x2778.png +0 -0
  575. dstack/_internal/server/statics/assets/apple-touch-startup-image-1290x2796.png +0 -0
  576. dstack/_internal/server/statics/assets/apple-touch-startup-image-1334x750.png +0 -0
  577. dstack/_internal/server/statics/assets/apple-touch-startup-image-1488x2266.png +0 -0
  578. dstack/_internal/server/statics/assets/apple-touch-startup-image-1536x2048.png +0 -0
  579. dstack/_internal/server/statics/assets/apple-touch-startup-image-1620x2160.png +0 -0
  580. dstack/_internal/server/statics/assets/apple-touch-startup-image-1640x2160.png +0 -0
  581. dstack/_internal/server/statics/assets/apple-touch-startup-image-1668x2224.png +0 -0
  582. dstack/_internal/server/statics/assets/apple-touch-startup-image-1668x2388.png +0 -0
  583. dstack/_internal/server/statics/assets/apple-touch-startup-image-1792x828.png +0 -0
  584. dstack/_internal/server/statics/assets/apple-touch-startup-image-2048x1536.png +0 -0
  585. dstack/_internal/server/statics/assets/apple-touch-startup-image-2048x2732.png +0 -0
  586. dstack/_internal/server/statics/assets/apple-touch-startup-image-2160x1620.png +0 -0
  587. dstack/_internal/server/statics/assets/apple-touch-startup-image-2160x1640.png +0 -0
  588. dstack/_internal/server/statics/assets/apple-touch-startup-image-2208x1242.png +0 -0
  589. dstack/_internal/server/statics/assets/apple-touch-startup-image-2224x1668.png +0 -0
  590. dstack/_internal/server/statics/assets/apple-touch-startup-image-2266x1488.png +0 -0
  591. dstack/_internal/server/statics/assets/apple-touch-startup-image-2388x1668.png +0 -0
  592. dstack/_internal/server/statics/assets/apple-touch-startup-image-2436x1125.png +0 -0
  593. dstack/_internal/server/statics/assets/apple-touch-startup-image-2532x1170.png +0 -0
  594. dstack/_internal/server/statics/assets/apple-touch-startup-image-2556x1179.png +0 -0
  595. dstack/_internal/server/statics/assets/apple-touch-startup-image-2688x1242.png +0 -0
  596. dstack/_internal/server/statics/assets/apple-touch-startup-image-2732x2048.png +0 -0
  597. dstack/_internal/server/statics/assets/apple-touch-startup-image-2778x1284.png +0 -0
  598. dstack/_internal/server/statics/assets/apple-touch-startup-image-2796x1290.png +0 -0
  599. dstack/_internal/server/statics/assets/apple-touch-startup-image-640x1136.png +0 -0
  600. dstack/_internal/server/statics/assets/apple-touch-startup-image-750x1334.png +0 -0
  601. dstack/_internal/server/statics/assets/apple-touch-startup-image-828x1792.png +0 -0
  602. dstack/_internal/server/statics/assets/browserconfig.xml +12 -0
  603. dstack/_internal/server/statics/assets/favicon-16x16.png +0 -0
  604. dstack/_internal/server/statics/assets/favicon-32x32.png +0 -0
  605. dstack/_internal/server/statics/assets/favicon-48x48.png +0 -0
  606. dstack/_internal/server/statics/assets/favicon.ico +0 -0
  607. dstack/{dashboard/statics/assets/manifest.json → _internal/server/statics/assets/manifest.webmanifest} +18 -9
  608. dstack/_internal/server/statics/assets/mstile-144x144.png +0 -0
  609. dstack/_internal/server/statics/assets/mstile-150x150.png +0 -0
  610. dstack/_internal/server/statics/assets/mstile-310x150.png +0 -0
  611. dstack/_internal/server/statics/assets/mstile-310x310.png +0 -0
  612. dstack/_internal/server/statics/assets/mstile-70x70.png +0 -0
  613. dstack/_internal/server/statics/assets/yandex-browser-50x50.png +0 -0
  614. dstack/_internal/server/statics/b7ae68f44193474fc578.png +0 -0
  615. dstack/_internal/server/statics/d2f008c75b2b5b191f3f.png +0 -0
  616. dstack/_internal/server/statics/d44c33e1b92e05c379fd.png +0 -0
  617. dstack/_internal/server/statics/dd43ff0552815179d7ab.png +0 -0
  618. dstack/_internal/server/statics/dd4e7166c0b9aac197d7.png +0 -0
  619. dstack/_internal/server/statics/e30b27916930d43d2271.png +0 -0
  620. dstack/_internal/server/statics/e467d7d60aae81ab198b.svg +6 -0
  621. dstack/_internal/server/statics/eb9b344b73818fe2b71a.png +0 -0
  622. dstack/_internal/server/statics/f517dd626eb964120de0.png +0 -0
  623. dstack/_internal/server/statics/f958aecddee5d8e3222c.png +0 -0
  624. dstack/_internal/server/statics/index.html +3 -0
  625. dstack/_internal/server/statics/logo-notext.svg +116 -0
  626. dstack/_internal/server/statics/main-2e6967bad9f29395eea6.css +3 -0
  627. dstack/_internal/server/statics/main-7dc0f6d20b8b41659acc.js +155547 -0
  628. dstack/_internal/server/statics/main-7dc0f6d20b8b41659acc.js.map +1 -0
  629. dstack/{dashboard → _internal/server}/statics/manifest.json +2 -2
  630. dstack/_internal/server/statics/static/media/entraID.d65d1f3e9486a8e56d24fc07b3230885.svg +9 -0
  631. dstack/_internal/server/statics/static/media/google.b194b06fafd0a52aeb566922160ea514.svg +1 -0
  632. dstack/{dashboard/statics/static/media/logo.f9d7170678f68f796e270698633770ec.svg → _internal/server/statics/static/media/logo.f602feeb138844eda97c8cb641461448.svg} +8 -6
  633. dstack/_internal/server/statics/static/media/okta.12f178e6873a1100965f2a4dbd18fcec.svg +2 -0
  634. dstack/_internal/server/statics/static/media/theme.3994c817bb7dda191c1c9640dee0bf42.svg +3 -0
  635. dstack/_internal/server/testing/__init__.py +0 -0
  636. dstack/_internal/server/testing/common.py +1220 -0
  637. dstack/_internal/server/testing/conf.py +53 -0
  638. dstack/_internal/server/testing/matchers.py +31 -0
  639. dstack/_internal/server/utils/__init__.py +0 -0
  640. dstack/_internal/server/utils/common.py +55 -0
  641. dstack/_internal/server/utils/logging.py +51 -0
  642. dstack/_internal/server/utils/provisioning.py +368 -0
  643. dstack/_internal/server/utils/routers.py +166 -0
  644. dstack/_internal/server/utils/sentry_utils.py +24 -0
  645. dstack/_internal/settings.py +49 -0
  646. dstack/_internal/utils/__init__.py +0 -0
  647. dstack/_internal/utils/common.py +318 -0
  648. dstack/_internal/utils/cron.py +5 -0
  649. dstack/_internal/utils/crypto.py +40 -0
  650. dstack/_internal/utils/env.py +88 -0
  651. dstack/_internal/utils/event_loop.py +30 -0
  652. dstack/_internal/utils/files.py +69 -0
  653. dstack/_internal/utils/gpu.py +59 -0
  654. dstack/_internal/utils/hash.py +31 -0
  655. dstack/_internal/utils/interpolator.py +91 -0
  656. dstack/_internal/utils/json_schema.py +11 -0
  657. dstack/_internal/utils/json_utils.py +54 -0
  658. dstack/_internal/utils/logging.py +5 -0
  659. dstack/_internal/utils/nested_list.py +47 -0
  660. dstack/_internal/utils/network.py +50 -0
  661. dstack/_internal/utils/path.py +57 -0
  662. dstack/_internal/utils/random_names.py +258 -0
  663. dstack/_internal/utils/ssh.py +346 -0
  664. dstack/_internal/utils/tags.py +42 -0
  665. dstack/_internal/utils/typing.py +14 -0
  666. dstack/_internal/utils/version.py +22 -0
  667. dstack/api/__init__.py +46 -0
  668. dstack/api/_public/__init__.py +96 -0
  669. dstack/api/_public/backends.py +42 -0
  670. dstack/api/_public/common.py +5 -0
  671. dstack/api/_public/repos.py +202 -0
  672. dstack/api/_public/runs.py +714 -0
  673. dstack/api/server/__init__.py +206 -0
  674. dstack/api/server/_auth.py +30 -0
  675. dstack/api/server/_backends.py +38 -0
  676. dstack/api/server/_events.py +64 -0
  677. dstack/api/server/_files.py +18 -0
  678. dstack/api/server/_fleets.py +82 -0
  679. dstack/api/server/_gateways.py +54 -0
  680. dstack/api/server/_gpus.py +27 -0
  681. dstack/api/server/_group.py +22 -0
  682. dstack/api/server/_logs.py +15 -0
  683. dstack/api/server/_metrics.py +23 -0
  684. dstack/api/server/_projects.py +124 -0
  685. dstack/api/server/_repos.py +64 -0
  686. dstack/api/server/_runs.py +102 -0
  687. dstack/api/server/_secrets.py +36 -0
  688. dstack/api/server/_users.py +82 -0
  689. dstack/api/server/_volumes.py +39 -0
  690. dstack/api/server/utils.py +34 -0
  691. dstack/api/utils.py +105 -0
  692. dstack/core/__init__.py +0 -0
  693. dstack/plugins/__init__.py +8 -0
  694. dstack/plugins/_base.py +72 -0
  695. dstack/plugins/_models.py +8 -0
  696. dstack/plugins/_utils.py +19 -0
  697. dstack/plugins/builtin/__init__.py +0 -0
  698. dstack/plugins/builtin/rest_plugin/__init__.py +18 -0
  699. dstack/plugins/builtin/rest_plugin/_models.py +48 -0
  700. dstack/plugins/builtin/rest_plugin/_plugin.py +147 -0
  701. dstack/version.py +3 -1
  702. dstack-0.20.7.dist-info/METADATA +519 -0
  703. dstack-0.20.7.dist-info/RECORD +720 -0
  704. {dstack-0.0.9.dist-info → dstack-0.20.7.dist-info}/WHEEL +1 -2
  705. dstack-0.20.7.dist-info/entry_points.txt +2 -0
  706. dstack-0.20.7.dist-info/licenses/LICENSE.md +353 -0
  707. dstack/aws/__init__.py +0 -180
  708. dstack/aws/artifacts.py +0 -111
  709. dstack/aws/config.py +0 -40
  710. dstack/aws/jobs.py +0 -245
  711. dstack/aws/logs.py +0 -186
  712. dstack/aws/repos.py +0 -137
  713. dstack/aws/run_names.py +0 -17
  714. dstack/aws/runners.py +0 -693
  715. dstack/aws/runs.py +0 -79
  716. dstack/aws/secrets.py +0 -99
  717. dstack/aws/tags.py +0 -138
  718. dstack/backend.py +0 -299
  719. dstack/cli/app.py +0 -41
  720. dstack/cli/artifacts.py +0 -87
  721. dstack/cli/common.py +0 -57
  722. dstack/cli/config.py +0 -194
  723. dstack/cli/dashboard.py +0 -26
  724. dstack/cli/delete.py +0 -49
  725. dstack/cli/init.py +0 -33
  726. dstack/cli/logs.py +0 -87
  727. dstack/cli/main.py +0 -81
  728. dstack/cli/restart.py +0 -43
  729. dstack/cli/run.py +0 -223
  730. dstack/cli/schema.py +0 -46
  731. dstack/cli/secrets.py +0 -97
  732. dstack/cli/status.py +0 -140
  733. dstack/cli/stop.py +0 -53
  734. dstack/cli/tags.py +0 -100
  735. dstack/config.py +0 -80
  736. dstack/dashboard/artifacts.py +0 -26
  737. dstack/dashboard/logs.py +0 -73
  738. dstack/dashboard/main.py +0 -45
  739. dstack/dashboard/repos.py +0 -41
  740. dstack/dashboard/runs.py +0 -140
  741. dstack/dashboard/secrets.py +0 -53
  742. dstack/dashboard/statics/4d6a4e032505c1efd23c.png +0 -0
  743. dstack/dashboard/statics/7e018c3e5566d7c349a8.png +0 -0
  744. dstack/dashboard/statics/assets/android-chrome-144x144.png +0 -0
  745. dstack/dashboard/statics/assets/android-chrome-192x192.png +0 -0
  746. dstack/dashboard/statics/assets/android-chrome-256x256.png +0 -0
  747. dstack/dashboard/statics/assets/android-chrome-36x36.png +0 -0
  748. dstack/dashboard/statics/assets/android-chrome-384x384.png +0 -0
  749. dstack/dashboard/statics/assets/android-chrome-48x48.png +0 -0
  750. dstack/dashboard/statics/assets/android-chrome-512x512.png +0 -0
  751. dstack/dashboard/statics/assets/android-chrome-72x72.png +0 -0
  752. dstack/dashboard/statics/assets/android-chrome-96x96.png +0 -0
  753. dstack/dashboard/statics/assets/apple-touch-icon-1024x1024.png +0 -0
  754. dstack/dashboard/statics/assets/apple-touch-icon-114x114.png +0 -0
  755. dstack/dashboard/statics/assets/apple-touch-icon-120x120.png +0 -0
  756. dstack/dashboard/statics/assets/apple-touch-icon-144x144.png +0 -0
  757. dstack/dashboard/statics/assets/apple-touch-icon-152x152.png +0 -0
  758. dstack/dashboard/statics/assets/apple-touch-icon-167x167.png +0 -0
  759. dstack/dashboard/statics/assets/apple-touch-icon-180x180.png +0 -0
  760. dstack/dashboard/statics/assets/apple-touch-icon-57x57.png +0 -0
  761. dstack/dashboard/statics/assets/apple-touch-icon-60x60.png +0 -0
  762. dstack/dashboard/statics/assets/apple-touch-icon-72x72.png +0 -0
  763. dstack/dashboard/statics/assets/apple-touch-icon-76x76.png +0 -0
  764. dstack/dashboard/statics/assets/apple-touch-icon-precomposed.png +0 -0
  765. dstack/dashboard/statics/assets/apple-touch-icon.png +0 -0
  766. dstack/dashboard/statics/assets/apple-touch-startup-image-1125x2436.png +0 -0
  767. dstack/dashboard/statics/assets/apple-touch-startup-image-1136x640.png +0 -0
  768. dstack/dashboard/statics/assets/apple-touch-startup-image-1242x2208.png +0 -0
  769. dstack/dashboard/statics/assets/apple-touch-startup-image-1242x2688.png +0 -0
  770. dstack/dashboard/statics/assets/apple-touch-startup-image-1334x750.png +0 -0
  771. dstack/dashboard/statics/assets/apple-touch-startup-image-1536x2048.png +0 -0
  772. dstack/dashboard/statics/assets/apple-touch-startup-image-1620x2160.png +0 -0
  773. dstack/dashboard/statics/assets/apple-touch-startup-image-1668x2224.png +0 -0
  774. dstack/dashboard/statics/assets/apple-touch-startup-image-1668x2388.png +0 -0
  775. dstack/dashboard/statics/assets/apple-touch-startup-image-1792x828.png +0 -0
  776. dstack/dashboard/statics/assets/apple-touch-startup-image-2048x1536.png +0 -0
  777. dstack/dashboard/statics/assets/apple-touch-startup-image-2048x2732.png +0 -0
  778. dstack/dashboard/statics/assets/apple-touch-startup-image-2160x1620.png +0 -0
  779. dstack/dashboard/statics/assets/apple-touch-startup-image-2208x1242.png +0 -0
  780. dstack/dashboard/statics/assets/apple-touch-startup-image-2224x1668.png +0 -0
  781. dstack/dashboard/statics/assets/apple-touch-startup-image-2388x1668.png +0 -0
  782. dstack/dashboard/statics/assets/apple-touch-startup-image-2436x1125.png +0 -0
  783. dstack/dashboard/statics/assets/apple-touch-startup-image-2688x1242.png +0 -0
  784. dstack/dashboard/statics/assets/apple-touch-startup-image-2732x2048.png +0 -0
  785. dstack/dashboard/statics/assets/apple-touch-startup-image-640x1136.png +0 -0
  786. dstack/dashboard/statics/assets/apple-touch-startup-image-750x1334.png +0 -0
  787. dstack/dashboard/statics/assets/apple-touch-startup-image-828x1792.png +0 -0
  788. dstack/dashboard/statics/assets/browserconfig.xml +0 -15
  789. dstack/dashboard/statics/assets/coast-228x228.png +0 -0
  790. dstack/dashboard/statics/assets/favicon-16x16.png +0 -0
  791. dstack/dashboard/statics/assets/favicon-32x32.png +0 -0
  792. dstack/dashboard/statics/assets/favicon-48x48.png +0 -0
  793. dstack/dashboard/statics/assets/favicon.ico +0 -0
  794. dstack/dashboard/statics/assets/firefox_app_128x128.png +0 -0
  795. dstack/dashboard/statics/assets/firefox_app_512x512.png +0 -0
  796. dstack/dashboard/statics/assets/firefox_app_60x60.png +0 -0
  797. dstack/dashboard/statics/assets/manifest.webapp +0 -14
  798. dstack/dashboard/statics/assets/mstile-144x144.png +0 -0
  799. dstack/dashboard/statics/assets/mstile-150x150.png +0 -0
  800. dstack/dashboard/statics/assets/mstile-310x150.png +0 -0
  801. dstack/dashboard/statics/assets/mstile-310x310.png +0 -0
  802. dstack/dashboard/statics/assets/mstile-70x70.png +0 -0
  803. dstack/dashboard/statics/assets/yandex-browser-50x50.png +0 -0
  804. dstack/dashboard/statics/d0f71e48806e25d72553.png +0 -0
  805. dstack/dashboard/statics/index.html +0 -7
  806. dstack/dashboard/statics/main-1d87e34eb0454da8ebb4.js +0 -3
  807. dstack/dashboard/statics/main-1d87e34eb0454da8ebb4.js.LICENSE.txt +0 -102
  808. dstack/dashboard/statics/main-1d87e34eb0454da8ebb4.js.map +0 -1
  809. dstack/dashboard/statics/main.css +0 -5058
  810. dstack/dashboard/statics/splash_thumbnail.png +0 -0
  811. dstack/dashboard/statics/static/media/check.3f68ffc787a15c0476793a6d18ecb71a.svg +0 -3
  812. dstack/dashboard/statics/static/media/chevron-down.bfd8f22c4a5db4d443e76bca3b02f334.svg +0 -3
  813. dstack/dashboard/statics/static/media/chevron-up.bade0c5d82d741cead615813264140c9.svg +0 -3
  814. dstack/dashboard/statics/static/media/clock.583b744f29b9d143718a55e7c35fe38e.svg +0 -3
  815. dstack/dashboard/statics/static/media/close.a8bb9e47361b03a3b5084dad676ba1da.svg +0 -3
  816. dstack/dashboard/statics/static/media/content-copy.73f5f2a175094757758e315243a4111e.svg +0 -3
  817. dstack/dashboard/statics/static/media/delete-outline.6a8abf4e4f9cb777781967efd56efe9b.svg +0 -3
  818. dstack/dashboard/statics/static/media/dots-vertical.82fc618192e0c7dc4d615ff93269246a.svg +0 -3
  819. dstack/dashboard/statics/static/media/earth.1ad57c7f59f4be5c8bb2fa00439c3149.svg +0 -3
  820. dstack/dashboard/statics/static/media/email.320bc3af24a5f1bb41ebd85f66a5dd70.svg +0 -3
  821. dstack/dashboard/statics/static/media/external-link.99b88e699c15afb820a1779d9a2261ed.svg +0 -3
  822. dstack/dashboard/statics/static/media/eye-off-outline.5b4afb7ad624a44dd307518ff93d1faa.svg +0 -3
  823. dstack/dashboard/statics/static/media/eye-outline.ca41708feaaed1edb15c5fff021fbafe.svg +0 -3
  824. dstack/dashboard/statics/static/media/file-download-outline.3634b41923ba79b297ff294ef898661c.svg +0 -3
  825. dstack/dashboard/statics/static/media/folder-outline.33378387af61821dd1207e4b2d061a07.svg +0 -3
  826. dstack/dashboard/statics/static/media/github-circle.1bb85d171c31a3c2eebad07319377171.svg +0 -3
  827. dstack/dashboard/statics/static/media/infinity.915f92939afc0a37f94adba211ceb172.svg +0 -3
  828. dstack/dashboard/statics/static/media/layers.b4b02cea267a617d7aa44c2719250c89.svg +0 -3
  829. dstack/dashboard/statics/static/media/linkedin.1c52fae553eee54397f0e63a79455a5e.svg +0 -3
  830. dstack/dashboard/statics/static/media/loading.e466be7b2c1f0ac9e7e51ca929d0e37d.svg +0 -3
  831. dstack/dashboard/statics/static/media/lock.4a4c7768d0fa60c716609ddc483470ef.svg +0 -3
  832. dstack/dashboard/statics/static/media/magnify.0c803314d039d21f3cb1504ccd1437a4.svg +0 -3
  833. dstack/dashboard/statics/static/media/mark.3f68ffc787a15c0476793a6d18ecb71a.svg +0 -3
  834. dstack/dashboard/statics/static/media/menu-close.3ee84714181017c6ff837830297c8437.svg +0 -3
  835. dstack/dashboard/statics/static/media/menu.922f81e0972fbcbb5adcd8def20c86a3.svg +0 -3
  836. dstack/dashboard/statics/static/media/pencil.f706a3b9dcbff4959a91bf72e1e6324f.svg +0 -3
  837. dstack/dashboard/statics/static/media/refresh.a80edb948e98b322cd73b67814a57a48.svg +0 -3
  838. dstack/dashboard/statics/static/media/shape-plus.63b093c7f4b44c3def774f30fcfbceca.svg +0 -3
  839. dstack/dashboard/statics/static/media/slack.ec2fca99c6b944950ac65404ddd26880.svg +0 -4
  840. dstack/dashboard/statics/static/media/small-logo.b9cc8d09f646a553e65fa336dafd8b10.svg +0 -116
  841. dstack/dashboard/statics/static/media/source-branch.b8d22cfc42a7bed81f0fc08130818e85.svg +0 -3
  842. dstack/dashboard/statics/static/media/source-commit.be2bb53c081b9b6836adffccc0b8d3e6.svg +0 -3
  843. dstack/dashboard/statics/static/media/stop.11488ff1437ad929476be8924a3b7075.svg +0 -3
  844. dstack/dashboard/statics/static/media/tag-minus.15680a815b0b8d027e973c84832c05e6.svg +0 -3
  845. dstack/dashboard/statics/static/media/tag-outline.19b0bf86a8afd7d6d9c716e9a91d94ca.svg +0 -3
  846. dstack/dashboard/statics/static/media/twitter.4af18861c84a2f3044c7546b55d5739c.svg +0 -3
  847. dstack/dashboard/tags.py +0 -119
  848. dstack/jobs.py +0 -255
  849. dstack/providers/__init__.py +0 -316
  850. dstack/providers/_python/main.py +0 -88
  851. dstack/providers/_tensorboard/main.py +0 -93
  852. dstack/providers/_torchrun/main.py +0 -121
  853. dstack/providers/bash/main.py +0 -90
  854. dstack/providers/code/main.py +0 -95
  855. dstack/providers/docker/main.py +0 -79
  856. dstack/providers/lab/main.py +0 -95
  857. dstack/providers/notebook/main.py +0 -90
  858. dstack/random_name.py +0 -29
  859. dstack/repo.py +0 -135
  860. dstack/runners.py +0 -35
  861. dstack/util.py +0 -15
  862. dstack-0.0.9.dist-info/METADATA +0 -176
  863. dstack-0.0.9.dist-info/RECORD +0 -179
  864. dstack-0.0.9.dist-info/entry_points.txt +0 -3
  865. dstack-0.0.9.dist-info/top_level.txt +0 -2
  866. tests/test_config.py +0 -70
  867. /dstack/{cli → _internal}/__init__.py +0 -0
  868. /dstack/{dashboard → _internal/cli}/__init__.py +0 -0
  869. /dstack/{providers/_python → _internal/cli/models}/__init__.py +0 -0
  870. /dstack/{providers/_tensorboard → _internal/cli/services}/__init__.py +0 -0
  871. /dstack/{providers/_torchrun → _internal/cli/utils}/__init__.py +0 -0
  872. /dstack/{providers/bash → _internal/core}/__init__.py +0 -0
  873. /dstack/{providers/code → _internal/core/backends}/__init__.py +0 -0
  874. /dstack/{providers/docker → _internal/core/backends/aws}/__init__.py +0 -0
  875. /dstack/{providers/lab → _internal/core/backends/azure}/__init__.py +0 -0
  876. /dstack/{providers/notebook → _internal/core/backends/base}/__init__.py +0 -0
  877. {tests → dstack/_internal/core/backends/cloudrift}/__init__.py +0 -0
  878. /dstack/{dashboard → _internal/server}/statics/assets/yandex-browser-manifest.json +0 -0
  879. /dstack/{dashboard → _internal/server}/statics/robots.txt +0 -0
@@ -0,0 +1,1123 @@
1
+ import re
2
+ import string
3
+ from collections import Counter
4
+ from enum import Enum
5
+ from pathlib import PurePosixPath
6
+ from typing import Annotated, Any, Dict, List, Literal, Optional, Union
7
+
8
+ import orjson
9
+ from pydantic import Field, ValidationError, conint, constr, root_validator, validator
10
+ from typing_extensions import Self
11
+
12
+ from dstack._internal.core.errors import ConfigurationError
13
+ from dstack._internal.core.models.common import (
14
+ CoreConfig,
15
+ CoreModel,
16
+ Duration,
17
+ RegistryAuth,
18
+ generate_dual_core_model,
19
+ )
20
+ from dstack._internal.core.models.envs import Env
21
+ from dstack._internal.core.models.files import FilePathMapping
22
+ from dstack._internal.core.models.fleets import FleetConfiguration
23
+ from dstack._internal.core.models.gateways import GatewayConfiguration
24
+ from dstack._internal.core.models.profiles import (
25
+ ProfileParams,
26
+ ProfileParamsConfig,
27
+ parse_duration,
28
+ parse_off_duration,
29
+ )
30
+ from dstack._internal.core.models.resources import Range, ResourcesSpec
31
+ from dstack._internal.core.models.services import AnyModel, OpenAIChatModel
32
+ from dstack._internal.core.models.unix import UnixUser
33
+ from dstack._internal.core.models.volumes import MountPoint, VolumeConfiguration, parse_mount_point
34
+ from dstack._internal.core.services import is_valid_replica_group_name
35
+ from dstack._internal.utils.common import has_duplicates, list_enum_values_for_annotation
36
+ from dstack._internal.utils.json_schema import add_extra_schema_types
37
+ from dstack._internal.utils.json_utils import (
38
+ pydantic_orjson_dumps_with_indent,
39
+ )
40
+
41
+ CommandsList = List[str]
42
+ ValidPort = conint(gt=0, le=65536)
43
+ MAX_INT64 = 2**63 - 1
44
+ SERVICE_HTTPS_DEFAULT = True
45
+ STRIP_PREFIX_DEFAULT = True
46
+ RUN_PRIOTIRY_MIN = 0
47
+ RUN_PRIOTIRY_MAX = 100
48
+ RUN_PRIORITY_DEFAULT = 0
49
+ LEGACY_REPO_DIR = "/workflow"
50
+ MIN_PROBE_TIMEOUT = 1
51
+ MIN_PROBE_INTERVAL = 1
52
+ DEFAULT_PROBE_URL = "/"
53
+ DEFAULT_PROBE_TIMEOUT = 10
54
+ DEFAULT_PROBE_INTERVAL = 15
55
+ DEFAULT_PROBE_READY_AFTER = 1
56
+ DEFAULT_PROBE_METHOD = "get"
57
+ MAX_PROBE_URL_LEN = 2048
58
+ DEFAULT_REPLICA_GROUP_NAME = "0"
59
+
60
+
61
+ class RunConfigurationType(str, Enum):
62
+ DEV_ENVIRONMENT = "dev-environment"
63
+ TASK = "task"
64
+ SERVICE = "service"
65
+
66
+
67
+ class PythonVersion(str, Enum):
68
+ PY39 = "3.9"
69
+ PY310 = "3.10"
70
+ PY311 = "3.11"
71
+ PY312 = "3.12"
72
+ PY313 = "3.13"
73
+
74
+
75
+ class PortMapping(CoreModel):
76
+ local_port: Optional[ValidPort] = None
77
+ container_port: ValidPort
78
+
79
+ @classmethod
80
+ def parse(cls, v: str) -> "PortMapping":
81
+ """
82
+ Possible values:
83
+ - 8080
84
+ - 80:8080
85
+ - *:8080
86
+ """
87
+ r = re.search(r"^(?:(\d+|\*):)?(\d+)?$", v)
88
+ if not r:
89
+ raise ValueError(v)
90
+ local_port, container_port = r.groups()
91
+ if local_port is None: # identity mapping by default
92
+ local_port = int(container_port)
93
+ elif local_port == "*":
94
+ local_port = None
95
+ else:
96
+ local_port = int(local_port)
97
+ return PortMapping(local_port=local_port, container_port=int(container_port))
98
+
99
+
100
+ class RepoExistsAction(str, Enum):
101
+ # Don't try to check out, terminate the run with an error (the default action since 0.20.0)
102
+ ERROR = "error"
103
+ # Don't try to check out, skip the repo (the logic hardcoded in the pre-0.20.0 runner)
104
+ SKIP = "skip"
105
+
106
+
107
+ class RepoSpec(CoreModel):
108
+ local_path: Annotated[
109
+ Optional[str],
110
+ Field(
111
+ description=(
112
+ "The path to the Git repo on the user's machine. Relative paths are resolved"
113
+ " relative to the parent directory of the the configuration file."
114
+ " Mutually exclusive with `url`"
115
+ )
116
+ ),
117
+ ] = None
118
+ url: Annotated[
119
+ Optional[str],
120
+ Field(description="The Git repo URL. Mutually exclusive with `local_path`"),
121
+ ] = None
122
+ branch: Annotated[
123
+ Optional[str],
124
+ Field(
125
+ description=(
126
+ "The repo branch. Defaults to the active branch for local paths"
127
+ " and the default branch for URLs"
128
+ )
129
+ ),
130
+ ] = None
131
+ hash: Annotated[
132
+ Optional[str],
133
+ Field(description="The commit hash"),
134
+ ] = None
135
+ path: Annotated[
136
+ str,
137
+ Field(
138
+ description=(
139
+ "The repo path inside the run container. Relative paths are resolved"
140
+ " relative to the working directory"
141
+ )
142
+ ),
143
+ ] = "."
144
+ if_exists: Annotated[
145
+ RepoExistsAction,
146
+ Field(
147
+ description=(
148
+ "The action to be taken if `path` exists and is not empty."
149
+ f" One of: {list_enum_values_for_annotation(RepoExistsAction)}"
150
+ ),
151
+ ),
152
+ ] = RepoExistsAction.ERROR
153
+
154
+ @classmethod
155
+ def parse(cls, v: str) -> Self:
156
+ is_url = False
157
+ parts = v.split(":")
158
+ if len(parts) > 1:
159
+ # Git repo, git@github.com:dstackai/dstack.git or https://github.com/dstackai/dstack
160
+ if "@" in parts[0] or parts[1].startswith("//"):
161
+ parts = [f"{parts[0]}:{parts[1]}", *parts[2:]]
162
+ is_url = True
163
+ # Windows path, e.g., `C:\path\to`, 'c:/path/to'
164
+ elif (
165
+ len(parts[0]) == 1
166
+ and parts[0] in string.ascii_letters
167
+ and parts[1][:1] in ["\\", "/"]
168
+ ):
169
+ parts = [f"{parts[0]}:{parts[1]}", *parts[2:]]
170
+ if len(parts) == 1:
171
+ if is_url:
172
+ return cls(url=parts[0])
173
+ return cls(local_path=parts[0])
174
+ if len(parts) == 2:
175
+ if is_url:
176
+ return cls(url=parts[0], path=parts[1])
177
+ return cls(local_path=parts[0], path=parts[1])
178
+ raise ValueError(f"Invalid repo: {v}")
179
+
180
+ @root_validator
181
+ def validate_local_path_or_url(cls, values):
182
+ if values["local_path"] and values["url"]:
183
+ raise ValueError("`local_path` and `url` are mutually exclusive")
184
+ if not values["local_path"] and not values["url"]:
185
+ raise ValueError("Either `local_path` or `url` must be specified")
186
+ return values
187
+
188
+ @validator("path")
189
+ def validate_path(cls, v: Optional[str]) -> Optional[str]:
190
+ if v is None:
191
+ return v
192
+ if v.startswith("~") and PurePosixPath(v).parts[0] != "~":
193
+ raise ValueError("`~username` syntax is not supported")
194
+ return v
195
+
196
+
197
+ class ScalingSpec(CoreModel):
198
+ metric: Annotated[
199
+ Literal["rps"],
200
+ Field(
201
+ description="The target metric to track. Currently, the only supported value is `rps` "
202
+ "(meaning requests per second)"
203
+ ),
204
+ ]
205
+ target: Annotated[
206
+ float,
207
+ Field(
208
+ description="The target value of the metric. "
209
+ "The number of replicas is calculated based on this number and automatically adjusts "
210
+ "(scales up or down) as this metric changes",
211
+ gt=0,
212
+ ),
213
+ ]
214
+ scale_up_delay: Annotated[
215
+ Duration, Field(description="The delay in seconds before scaling up")
216
+ ] = Duration.parse("5m")
217
+ scale_down_delay: Annotated[
218
+ Duration, Field(description="The delay in seconds before scaling down")
219
+ ] = Duration.parse("10m")
220
+
221
+
222
+ class IPAddressPartitioningKey(CoreModel):
223
+ type: Annotated[Literal["ip_address"], Field(description="Partitioning type")] = "ip_address"
224
+
225
+
226
+ class HeaderPartitioningKey(CoreModel):
227
+ type: Annotated[Literal["header"], Field(description="Partitioning type")] = "header"
228
+ header: Annotated[
229
+ str,
230
+ Field(
231
+ description="Name of the header to use for partitioning",
232
+ regex=r"^[a-zA-Z0-9-_]+$", # prevent Nginx config injection
233
+ max_length=500, # chosen randomly, Nginx limit is higher
234
+ ),
235
+ ]
236
+
237
+
238
+ class RateLimit(CoreModel):
239
+ prefix: Annotated[
240
+ str,
241
+ Field(
242
+ description=(
243
+ "URL path prefix to which this limit is applied."
244
+ " If an incoming request matches several prefixes, the longest prefix is applied"
245
+ ),
246
+ max_length=4094, # Nginx limit
247
+ regex=r"^/[^\s\\{}]*$", # prevent Nginx config injection
248
+ ),
249
+ ] = "/"
250
+ key: Annotated[
251
+ Union[IPAddressPartitioningKey, HeaderPartitioningKey],
252
+ Field(
253
+ discriminator="type",
254
+ description=(
255
+ "The partitioning key. Each incoming request belongs to a partition"
256
+ " and rate limits are applied per partition."
257
+ " Defaults to partitioning by client IP address"
258
+ ),
259
+ ),
260
+ ] = IPAddressPartitioningKey()
261
+ rps: Annotated[
262
+ float,
263
+ Field(
264
+ description=(
265
+ "Max allowed number of requests per second."
266
+ " Requests are tracked at millisecond granularity."
267
+ " For example, `rps: 10` means at most 1 request per 100ms"
268
+ ),
269
+ # should fit into Nginx limits after being converted to requests per minute
270
+ ge=1 / 60,
271
+ le=MAX_INT64 // 60,
272
+ ),
273
+ ]
274
+ burst: Annotated[
275
+ int,
276
+ Field(
277
+ ge=0,
278
+ le=MAX_INT64, # Nginx limit
279
+ description=(
280
+ "Max number of requests that can be passed to the service ahead of the rate limit"
281
+ ),
282
+ ),
283
+ ] = 0
284
+
285
+
286
+ HTTPMethod = Literal["get", "post", "put", "delete", "patch", "head"]
287
+
288
+
289
+ class HTTPHeaderSpec(CoreModel):
290
+ name: Annotated[
291
+ str,
292
+ Field(
293
+ description="The name of the HTTP header",
294
+ min_length=1,
295
+ max_length=256,
296
+ ),
297
+ ]
298
+ value: Annotated[
299
+ str,
300
+ Field(
301
+ description="The value of the HTTP header",
302
+ min_length=1,
303
+ max_length=2048,
304
+ ),
305
+ ]
306
+
307
+
308
+ class ProbeConfigConfig(CoreConfig):
309
+ @staticmethod
310
+ def schema_extra(schema: Dict[str, Any]):
311
+ add_extra_schema_types(
312
+ schema["properties"]["timeout"],
313
+ extra_types=[{"type": "string"}],
314
+ )
315
+ add_extra_schema_types(
316
+ schema["properties"]["interval"],
317
+ extra_types=[{"type": "string"}],
318
+ )
319
+
320
+
321
+ class ProbeConfig(generate_dual_core_model(ProbeConfigConfig)):
322
+ type: Literal["http"] # expect other probe types in the future, namely `exec`
323
+ url: Annotated[
324
+ Optional[str], Field(description=f"The URL to request. Defaults to `{DEFAULT_PROBE_URL}`")
325
+ ] = None
326
+ method: Annotated[
327
+ Optional[HTTPMethod],
328
+ Field(
329
+ description=(
330
+ "The HTTP method to use for the probe (e.g., `get`, `post`, etc.)."
331
+ f" Defaults to `{DEFAULT_PROBE_METHOD}`"
332
+ )
333
+ ),
334
+ ] = None
335
+ headers: Annotated[
336
+ list[HTTPHeaderSpec],
337
+ Field(description="A list of HTTP headers to include in the request", max_items=16),
338
+ ] = []
339
+ body: Annotated[
340
+ Optional[str],
341
+ Field(
342
+ description="The HTTP request body to send with the probe",
343
+ min_length=1,
344
+ max_length=2048,
345
+ ),
346
+ ] = None
347
+ timeout: Annotated[
348
+ Optional[int],
349
+ Field(
350
+ description=(
351
+ f"Maximum amount of time the HTTP request is allowed to take. Defaults to `{DEFAULT_PROBE_TIMEOUT}s`"
352
+ )
353
+ ),
354
+ ] = None
355
+ interval: Annotated[
356
+ Optional[int],
357
+ Field(
358
+ description=(
359
+ "Minimum amount of time between the end of one probe execution"
360
+ f" and the start of the next. Defaults to `{DEFAULT_PROBE_INTERVAL}s`"
361
+ )
362
+ ),
363
+ ] = None
364
+ ready_after: Annotated[
365
+ Optional[int],
366
+ Field(
367
+ ge=1,
368
+ description=(
369
+ "The number of consecutive successful probe executions required for the replica"
370
+ " to be considered ready. Used during rolling deployments."
371
+ f" Defaults to `{DEFAULT_PROBE_READY_AFTER}`"
372
+ ),
373
+ ),
374
+ ] = None
375
+
376
+ @validator("timeout", pre=True)
377
+ def parse_timeout(cls, v: Optional[Union[int, str]]) -> Optional[int]:
378
+ if v is None:
379
+ return v
380
+ parsed = parse_duration(v)
381
+ if parsed < MIN_PROBE_TIMEOUT:
382
+ raise ValueError(f"Probe timeout cannot be shorter than {MIN_PROBE_TIMEOUT}s")
383
+ return parsed
384
+
385
+ @validator("interval", pre=True)
386
+ def parse_interval(cls, v: Optional[Union[int, str]]) -> Optional[int]:
387
+ if v is None:
388
+ return v
389
+ parsed = parse_duration(v)
390
+ if parsed < MIN_PROBE_INTERVAL:
391
+ raise ValueError(f"Probe interval cannot be shorter than {MIN_PROBE_INTERVAL}s")
392
+ return parsed
393
+
394
+ @validator("url")
395
+ def validate_url(cls, v: Optional[str]) -> Optional[str]:
396
+ if v is None:
397
+ return v
398
+ if not v.startswith("/"):
399
+ raise ValueError("Must start with `/`")
400
+ if len(v) > MAX_PROBE_URL_LEN:
401
+ raise ValueError(f"Cannot be longer than {MAX_PROBE_URL_LEN} characters")
402
+ if not v.isprintable():
403
+ raise ValueError("Cannot contain non-printable characters")
404
+ return v
405
+
406
+ @root_validator
407
+ def validate_body_matches_method(cls, values):
408
+ method: HTTPMethod = values["method"]
409
+ if values["body"] is not None and method in ["get", "head"]:
410
+ raise ValueError(f"Cannot set request body for the `{method}` method")
411
+ return values
412
+
413
+
414
+ class BaseRunConfigurationConfig(CoreConfig):
415
+ @staticmethod
416
+ def schema_extra(schema: Dict[str, Any]):
417
+ add_extra_schema_types(
418
+ schema["properties"]["volumes"]["items"],
419
+ extra_types=[{"type": "string"}],
420
+ )
421
+ add_extra_schema_types(
422
+ schema["properties"]["files"]["items"],
423
+ extra_types=[{"type": "string"}],
424
+ )
425
+
426
+
427
+ class BaseRunConfiguration(CoreModel):
428
+ type: Literal["none"]
429
+ name: Annotated[
430
+ Optional[str],
431
+ Field(description="The run name. If not specified, a random name is generated"),
432
+ ] = None
433
+ image: Annotated[Optional[str], Field(description="The name of the Docker image to run")] = (
434
+ None
435
+ )
436
+ user: Annotated[
437
+ Optional[str],
438
+ Field(
439
+ description=(
440
+ "The user inside the container, `user_name_or_id[:group_name_or_id]`"
441
+ " (e.g., `ubuntu`, `1000:1000`). Defaults to the default user from the `image`"
442
+ )
443
+ ),
444
+ ] = None
445
+ privileged: Annotated[bool, Field(description="Run the container in privileged mode")] = False
446
+ entrypoint: Annotated[Optional[str], Field(description="The Docker entrypoint")] = None
447
+ working_dir: Annotated[
448
+ Optional[str],
449
+ Field(
450
+ description=(
451
+ "The absolute path to the working directory inside the container."
452
+ " Defaults to the `image`'s default working directory"
453
+ ),
454
+ ),
455
+ ] = None
456
+ # deprecated since 0.18.31; has no effect
457
+ home_dir: str = "/root"
458
+ registry_auth: Annotated[
459
+ Optional[RegistryAuth], Field(description="Credentials for pulling a private Docker image")
460
+ ] = None
461
+ python: Annotated[
462
+ Optional[PythonVersion],
463
+ Field(
464
+ description="The major version of Python. Mutually exclusive with `image` and `docker`"
465
+ ),
466
+ ] = None
467
+ nvcc: Annotated[
468
+ Optional[bool],
469
+ Field(
470
+ description="Use image with NVIDIA CUDA Compiler (NVCC) included. Mutually exclusive with `image` and `docker`"
471
+ ),
472
+ ] = None
473
+ single_branch: Annotated[
474
+ Optional[bool],
475
+ Field(
476
+ description=(
477
+ "Whether to clone and track only the current branch or all remote branches."
478
+ " Relevant only when using remote Git repos."
479
+ " Defaults to `false` for dev environments and to `true` for tasks and services"
480
+ )
481
+ ),
482
+ ] = None
483
+ env: Annotated[
484
+ Env,
485
+ Field(description="The mapping or the list of environment variables"),
486
+ ] = Env()
487
+ shell: Annotated[
488
+ Optional[str],
489
+ Field(
490
+ description=(
491
+ "The shell used to run commands."
492
+ " Allowed values are `sh`, `bash`, or an absolute path, e.g., `/usr/bin/zsh`."
493
+ " Defaults to `/bin/sh` if the `image` is specified, `/bin/bash` otherwise"
494
+ )
495
+ ),
496
+ ] = None
497
+ resources: Annotated[
498
+ ResourcesSpec, Field(description="The resources requirements to run the configuration")
499
+ ] = ResourcesSpec()
500
+ priority: Annotated[
501
+ Optional[int],
502
+ Field(
503
+ ge=RUN_PRIOTIRY_MIN,
504
+ le=RUN_PRIOTIRY_MAX,
505
+ description=(
506
+ f"The priority of the run, an integer between `{RUN_PRIOTIRY_MIN}` and `{RUN_PRIOTIRY_MAX}`."
507
+ " `dstack` tries to provision runs with higher priority first."
508
+ f" Defaults to `{RUN_PRIORITY_DEFAULT}`"
509
+ ),
510
+ ),
511
+ ] = None
512
+ volumes: Annotated[List[MountPoint], Field(description="The volumes mount points")] = []
513
+ docker: Annotated[
514
+ Optional[bool],
515
+ Field(
516
+ description="Use Docker inside the container. Mutually exclusive with `image`, `python`, and `nvcc`. Overrides `privileged`"
517
+ ),
518
+ ] = None
519
+ repos: Annotated[
520
+ list[RepoSpec],
521
+ Field(description="The list of Git repos"),
522
+ ] = []
523
+ files: Annotated[
524
+ list[FilePathMapping],
525
+ Field(description="The local to container file path mappings"),
526
+ ] = []
527
+ # deprecated since 0.18.31; task, service -- no effect; dev-environment -- executed right before `init`
528
+ setup: CommandsList = []
529
+
530
+ @validator("python", pre=True, always=True)
531
+ def convert_python(cls, v, values) -> Optional[PythonVersion]:
532
+ if v is not None and values.get("image"):
533
+ raise KeyError("`image` and `python` are mutually exclusive fields")
534
+ if isinstance(v, float):
535
+ v = str(v)
536
+ if v == "3.1":
537
+ v = "3.10"
538
+ if isinstance(v, str):
539
+ return PythonVersion(v)
540
+ return v
541
+
542
+ @validator("docker", pre=True, always=True)
543
+ def _docker(cls, v, values) -> Optional[bool]:
544
+ if v is True and values.get("image"):
545
+ raise KeyError("`image` and `docker` are mutually exclusive fields")
546
+ if v is True and values.get("python"):
547
+ raise KeyError("`python` and `docker` are mutually exclusive fields")
548
+ if v is True and values.get("nvcc"):
549
+ raise KeyError("`nvcc` and `docker` are mutually exclusive fields")
550
+ # Ideally, we'd like to also prohibit privileged=False when docker=True,
551
+ # but it's not possible to do so without breaking backwards compatibility.
552
+ return v
553
+
554
+ @validator("volumes", each_item=True, pre=True)
555
+ def convert_volumes(cls, v: Union[MountPoint, str]) -> MountPoint:
556
+ if isinstance(v, str):
557
+ return parse_mount_point(v)
558
+ return v
559
+
560
+ @validator("files", each_item=True, pre=True)
561
+ def convert_files(cls, v: Union[FilePathMapping, str]) -> FilePathMapping:
562
+ if isinstance(v, str):
563
+ return FilePathMapping.parse(v)
564
+ return v
565
+
566
+ @validator("repos", pre=True, each_item=True)
567
+ def convert_repos(cls, v: Union[RepoSpec, str]) -> RepoSpec:
568
+ if isinstance(v, str):
569
+ return RepoSpec.parse(v)
570
+ return v
571
+
572
+ @validator("repos")
573
+ def validate_repos(cls, v) -> RepoSpec:
574
+ if len(v) > 1:
575
+ raise ValueError("A maximum of one repo is currently supported")
576
+ return v
577
+
578
+ @validator("user")
579
+ def validate_user(cls, v) -> Optional[str]:
580
+ if v is None:
581
+ return None
582
+ UnixUser.parse(v)
583
+ return v
584
+
585
+ @validator("shell")
586
+ def validate_shell(cls, v) -> Optional[str]:
587
+ if v is None:
588
+ return None
589
+ if v in ["sh", "bash"]:
590
+ return v
591
+ path = PurePosixPath(v)
592
+ if path.is_absolute():
593
+ return v
594
+ raise ValueError("The value must be `sh`, `bash`, or an absolute path")
595
+
596
+
597
+ class ConfigurationWithPortsParams(CoreModel):
598
+ ports: Annotated[
599
+ List[Union[ValidPort, constr(regex=r"^(?:[0-9]+|\*):[0-9]+$"), PortMapping]],
600
+ Field(description="Port numbers/mapping to expose"),
601
+ ] = []
602
+
603
+ @validator("ports", each_item=True)
604
+ def convert_ports(cls, v) -> PortMapping:
605
+ if isinstance(v, int):
606
+ return PortMapping(local_port=v, container_port=v)
607
+ elif isinstance(v, str):
608
+ return PortMapping.parse(v)
609
+ return v
610
+
611
+
612
+ class ConfigurationWithCommandsParams(CoreModel):
613
+ commands: Annotated[CommandsList, Field(description="The shell commands to run")] = []
614
+
615
+ @root_validator
616
+ def check_image_or_commands_present(cls, values):
617
+ # If replicas is list, skip validation - commands come from replica groups
618
+ replicas = values.get("replicas")
619
+ if isinstance(replicas, list):
620
+ return values
621
+
622
+ if not values.get("commands") and not values.get("image"):
623
+ raise ValueError("Either `commands` or `image` must be set")
624
+ return values
625
+
626
+
627
+ class DevEnvironmentConfigurationParams(CoreModel):
628
+ ide: Annotated[
629
+ Union[Literal["vscode"], Literal["cursor"], Literal["windsurf"]],
630
+ Field(
631
+ description="The IDE to run. Supported values include `vscode`, `cursor`, and `windsurf`"
632
+ ),
633
+ ]
634
+ version: Annotated[
635
+ Optional[str],
636
+ Field(
637
+ description="The version of the IDE. For `windsurf`, the version is in the format `version@commit`"
638
+ ),
639
+ ] = None
640
+ init: Annotated[CommandsList, Field(description="The shell commands to run on startup")] = []
641
+ inactivity_duration: Annotated[
642
+ Optional[Union[Literal["off"], int, bool, str]],
643
+ Field(
644
+ description=(
645
+ "The maximum amount of time the dev environment can be inactive"
646
+ " (e.g., `2h`, `1d`, etc)."
647
+ " After it elapses, the dev environment is automatically stopped."
648
+ " Inactivity is defined as the absence of SSH connections to the"
649
+ " dev environment, including VS Code connections, `ssh <run name>`"
650
+ " shells, and attached `dstack apply` or `dstack attach` commands."
651
+ " Use `off` for unlimited duration. Can be updated in-place."
652
+ " Defaults to `off`"
653
+ )
654
+ ),
655
+ ] = None
656
+
657
+ @validator("inactivity_duration", pre=True, allow_reuse=True)
658
+ def parse_inactivity_duration(
659
+ cls, v: Optional[Union[Literal["off"], int, bool, str]]
660
+ ) -> Optional[int]:
661
+ v = parse_off_duration(v)
662
+ if isinstance(v, int):
663
+ return v
664
+ return None
665
+
666
+ @root_validator
667
+ def validate_windsurf_version_format(cls, values):
668
+ ide = values.get("ide")
669
+ version = values.get("version")
670
+ if ide == "windsurf" and version:
671
+ # Validate format: version@commit
672
+ if not re.match(r"^.+@[a-f0-9]+$", version):
673
+ raise ValueError(
674
+ f"Invalid Windsurf version format: `{version}`. "
675
+ "Expected format: `version@commit` (e.g., `1.106.0@8951cd3ad688e789573d7f51750d67ae4a0bea7d`)"
676
+ )
677
+ return values
678
+
679
+
680
+ class DevEnvironmentConfigurationConfig(
681
+ ProfileParamsConfig,
682
+ BaseRunConfigurationConfig,
683
+ ):
684
+ @staticmethod
685
+ def schema_extra(schema: Dict[str, Any]):
686
+ ProfileParamsConfig.schema_extra(schema)
687
+ BaseRunConfigurationConfig.schema_extra(schema)
688
+
689
+
690
+ class DevEnvironmentConfiguration(
691
+ ProfileParams,
692
+ BaseRunConfiguration,
693
+ ConfigurationWithPortsParams,
694
+ DevEnvironmentConfigurationParams,
695
+ generate_dual_core_model(DevEnvironmentConfigurationConfig),
696
+ ):
697
+ type: Literal["dev-environment"] = "dev-environment"
698
+
699
+ @validator("entrypoint")
700
+ def validate_entrypoint(cls, v: Optional[str]) -> Optional[str]:
701
+ if v is not None:
702
+ raise ValueError("entrypoint is not supported for dev-environment")
703
+ return v
704
+
705
+
706
+ class TaskConfigurationParams(CoreModel):
707
+ nodes: Annotated[int, Field(description="Number of nodes", ge=1)] = 1
708
+
709
+
710
+ class TaskConfigurationConfig(
711
+ ProfileParamsConfig,
712
+ BaseRunConfigurationConfig,
713
+ ):
714
+ @staticmethod
715
+ def schema_extra(schema: Dict[str, Any]):
716
+ ProfileParamsConfig.schema_extra(schema)
717
+ BaseRunConfigurationConfig.schema_extra(schema)
718
+
719
+
720
+ class TaskConfiguration(
721
+ ProfileParams,
722
+ BaseRunConfiguration,
723
+ ConfigurationWithCommandsParams,
724
+ ConfigurationWithPortsParams,
725
+ TaskConfigurationParams,
726
+ generate_dual_core_model(TaskConfigurationConfig),
727
+ ):
728
+ type: Literal["task"] = "task"
729
+
730
+
731
+ class ServiceConfigurationParamsConfig(CoreConfig):
732
+ @staticmethod
733
+ def schema_extra(schema: Dict[str, Any]):
734
+ add_extra_schema_types(
735
+ schema["properties"]["replicas"],
736
+ extra_types=[{"type": "integer"}, {"type": "string"}],
737
+ )
738
+ add_extra_schema_types(
739
+ schema["properties"]["model"],
740
+ extra_types=[{"type": "string"}],
741
+ )
742
+
743
+
744
+ def _validate_replica_range(v: Range[int]) -> Range[int]:
745
+ """Validate a Range[int] used for replica counts."""
746
+ if v.max is None:
747
+ raise ValueError("The maximum number of replicas is required")
748
+ if v.min is None:
749
+ v.min = 0
750
+ if v.min < 0:
751
+ raise ValueError("The minimum number of replicas must be greater than or equal to 0")
752
+ return v
753
+
754
+
755
+ class ReplicaGroup(CoreModel):
756
+ name: Annotated[
757
+ Optional[str],
758
+ Field(
759
+ description="The name of the replica group. If not provided, defaults to '0', '1', etc. based on position."
760
+ ),
761
+ ]
762
+ count: Annotated[
763
+ Range[int],
764
+ Field(
765
+ description="The number of replicas. Can be a number (e.g. `2`) or a range (`0..4` or `1..8`). "
766
+ "If it's a range, the `scaling` property is required"
767
+ ),
768
+ ]
769
+ scaling: Annotated[
770
+ Optional[ScalingSpec],
771
+ Field(description="The auto-scaling rules. Required if `count` is set to a range"),
772
+ ] = None
773
+
774
+ resources: Annotated[
775
+ ResourcesSpec,
776
+ Field(description="The resources requirements for replicas in this group"),
777
+ ] = ResourcesSpec()
778
+
779
+ commands: Annotated[
780
+ CommandsList,
781
+ Field(description="The shell commands to run for replicas in this group"),
782
+ ] = []
783
+
784
+ @validator("name")
785
+ def validate_name(cls, v: Optional[str]) -> Optional[str]:
786
+ if v is not None:
787
+ if not is_valid_replica_group_name(v):
788
+ raise ValueError("Resource name should match regex '^[a-z0-9][a-z0-9-]{0,39}$'")
789
+ return v
790
+
791
+ @validator("count")
792
+ def convert_count(cls, v: Range[int]) -> Range[int]:
793
+ return _validate_replica_range(v)
794
+
795
+ @root_validator()
796
+ def validate_scaling(cls, values):
797
+ scaling = values.get("scaling")
798
+ count = values.get("count")
799
+ if count and count.min != count.max and not scaling:
800
+ raise ValueError("When you set `count` to a range, ensure to specify `scaling`.")
801
+ if count and count.min == count.max and scaling:
802
+ raise ValueError("To use `scaling`, `count` must be set to a range.")
803
+ return values
804
+
805
+
806
+ class ServiceConfigurationParams(CoreModel):
807
+ port: Annotated[
808
+ # NOTE: it's a PortMapping for historical reasons. Only `port.container_port` is used.
809
+ Union[ValidPort, constr(regex=r"^[0-9]+:[0-9]+$"), PortMapping],
810
+ Field(description="The port the application listens on"),
811
+ ]
812
+ gateway: Annotated[
813
+ Optional[Union[bool, str]],
814
+ Field(
815
+ description=(
816
+ "The name of the gateway. Specify boolean `false` to run without a gateway."
817
+ " Specify boolean `true` to run with the default gateway."
818
+ " Omit to run with the default gateway if there is one, or without a gateway otherwise"
819
+ ),
820
+ ),
821
+ ] = None
822
+ strip_prefix: Annotated[
823
+ bool,
824
+ Field(
825
+ description=(
826
+ "Strip the `/proxy/services/<project name>/<run name>/` path prefix"
827
+ " when forwarding requests to the service. Only takes effect"
828
+ " when running the service without a gateway"
829
+ )
830
+ ),
831
+ ] = STRIP_PREFIX_DEFAULT
832
+ model: Annotated[
833
+ Optional[AnyModel],
834
+ Field(
835
+ description=(
836
+ "Mapping of the model for the OpenAI-compatible endpoint provided by `dstack`."
837
+ " Can be a full model format definition or just a model name."
838
+ " If it's a name, the service is expected to expose an OpenAI-compatible"
839
+ " API at the `/v1` path"
840
+ )
841
+ ),
842
+ ] = None
843
+ https: Annotated[bool, Field(description="Enable HTTPS if running with a gateway")] = (
844
+ SERVICE_HTTPS_DEFAULT
845
+ )
846
+ auth: Annotated[bool, Field(description="Enable the authorization")] = True
847
+
848
+ scaling: Annotated[
849
+ Optional[ScalingSpec],
850
+ Field(description="The auto-scaling rules. Required if `replicas` is set to a range"),
851
+ ] = None
852
+ rate_limits: Annotated[list[RateLimit], Field(description="Rate limiting rules")] = []
853
+ probes: Annotated[
854
+ list[ProbeConfig],
855
+ Field(description="List of probes used to determine job health"),
856
+ ] = []
857
+
858
+ replicas: Annotated[
859
+ Optional[Union[List[ReplicaGroup], Range[int]]],
860
+ Field(
861
+ description=(
862
+ "The number of replicas or a list of replica groups. "
863
+ "Can be an integer (e.g., `2`), a range (e.g., `0..4`), or a list of replica groups. "
864
+ "Each replica group defines replicas with shared configuration "
865
+ "(commands, resources, scaling). "
866
+ "When `replicas` is a list of replica groups, top-level `scaling`, `commands`, "
867
+ "and `resources` are not allowed and must be specified in each replica group instead. "
868
+ )
869
+ ),
870
+ ] = None
871
+
872
+ @validator("port")
873
+ def convert_port(cls, v) -> PortMapping:
874
+ if isinstance(v, int):
875
+ return PortMapping(local_port=80, container_port=v)
876
+ elif isinstance(v, str):
877
+ return PortMapping.parse(v)
878
+ return v
879
+
880
+ @validator("model", pre=True)
881
+ def convert_model(cls, v: Optional[Union[AnyModel, str]]) -> Optional[AnyModel]:
882
+ if isinstance(v, str):
883
+ return OpenAIChatModel(type="chat", name=v, format="openai")
884
+ return v
885
+
886
+ @validator("rate_limits")
887
+ def validate_rate_limits(cls, v: list[RateLimit]) -> list[RateLimit]:
888
+ counts = Counter(limit.prefix for limit in v)
889
+ duplicates = [prefix for prefix, count in counts.items() if count > 1]
890
+ if duplicates:
891
+ raise ValueError(
892
+ f"Prefixes {duplicates} are used more than once."
893
+ " Each rate limit should have a unique path prefix"
894
+ )
895
+ return v
896
+
897
+ @validator("probes")
898
+ def validate_probes(cls, v: list[ProbeConfig]) -> list[ProbeConfig]:
899
+ if has_duplicates(v):
900
+ # Using a custom validator instead of Field(unique_items=True) to avoid Pydantic bug:
901
+ # https://github.com/pydantic/pydantic/issues/3765
902
+ # Because of the bug, our gen_schema_reference.py fails to determine the type of
903
+ # ServiceConfiguration.probes and insert the correct hyperlink.
904
+ raise ValueError("Probes must be unique")
905
+ return v
906
+
907
+ @validator("replicas")
908
+ def validate_replicas(
909
+ cls, v: Optional[Union[Range[int], List[ReplicaGroup]]]
910
+ ) -> Optional[Union[Range[int], List[ReplicaGroup]]]:
911
+ if v is None:
912
+ return v
913
+ if isinstance(v, Range):
914
+ return _validate_replica_range(v)
915
+
916
+ if isinstance(v, list):
917
+ if not v:
918
+ raise ValueError("`replicas` cannot be an empty list")
919
+
920
+ # Assign default names to groups without names
921
+ for index, group in enumerate(v):
922
+ if group.name is None:
923
+ group.name = str(index)
924
+
925
+ # Check for duplicate names
926
+ names = [group.name for group in v]
927
+ if len(names) != len(set(names)):
928
+ duplicates = [name for name in set(names) if names.count(name) > 1]
929
+ raise ValueError(
930
+ f"Duplicate replica group names found: {duplicates}. "
931
+ "Each replica group must have a unique name."
932
+ )
933
+ return v
934
+
935
+ @root_validator()
936
+ def validate_scaling(cls, values):
937
+ scaling = values.get("scaling")
938
+ replicas = values.get("replicas")
939
+
940
+ if isinstance(replicas, Range):
941
+ if replicas and replicas.min != replicas.max and not scaling:
942
+ raise ValueError(
943
+ "When you set `replicas` to a range, ensure to specify `scaling`."
944
+ )
945
+ if replicas and replicas.min == replicas.max and scaling:
946
+ raise ValueError("To use `scaling`, `replicas` must be set to a range.")
947
+ return values
948
+
949
+ @root_validator()
950
+ def validate_top_level_properties_with_replica_groups(cls, values):
951
+ """
952
+ When replicas is a list of ReplicaGroup, forbid top-level scaling, commands, and resources
953
+ """
954
+ replicas = values.get("replicas")
955
+
956
+ if not isinstance(replicas, list):
957
+ return values
958
+
959
+ scaling = values.get("scaling")
960
+ if scaling is not None:
961
+ raise ValueError(
962
+ "Top-level `scaling` is not allowed when `replicas` is a list. "
963
+ "Specify `scaling` in each replica group instead."
964
+ )
965
+
966
+ commands = values.get("commands", [])
967
+ if commands:
968
+ raise ValueError(
969
+ "Top-level `commands` is not allowed when `replicas` is a list. "
970
+ "Specify `commands` in each replica group instead."
971
+ )
972
+
973
+ resources = values.get("resources")
974
+
975
+ default_resources = ResourcesSpec()
976
+ if resources and resources.dict() != default_resources.dict():
977
+ raise ValueError(
978
+ "Top-level `resources` is not allowed when `replicas` is a list. "
979
+ "Specify `resources` in each replica group instead."
980
+ )
981
+
982
+ return values
983
+
984
+ @root_validator()
985
+ def validate_replica_groups_have_commands_or_image(cls, values):
986
+ """
987
+ When replicas is a list, ensure each ReplicaGroup has commands OR service has image.
988
+ """
989
+ replicas = values.get("replicas")
990
+ image = values.get("image")
991
+
992
+ if not isinstance(replicas, list):
993
+ return values
994
+
995
+ for group in replicas:
996
+ if not group.commands and not image:
997
+ raise ValueError(
998
+ f"Replica group '{group.name}' has no commands. "
999
+ "Either set `commands` in the replica group or set `image` at the service level."
1000
+ )
1001
+
1002
+ return values
1003
+
1004
+
1005
+ class ServiceConfigurationConfig(
1006
+ ProfileParamsConfig,
1007
+ BaseRunConfigurationConfig,
1008
+ ServiceConfigurationParamsConfig,
1009
+ ):
1010
+ @staticmethod
1011
+ def schema_extra(schema: Dict[str, Any]):
1012
+ ProfileParamsConfig.schema_extra(schema)
1013
+ BaseRunConfigurationConfig.schema_extra(schema)
1014
+ ServiceConfigurationParamsConfig.schema_extra(schema)
1015
+
1016
+
1017
+ class ServiceConfiguration(
1018
+ ProfileParams,
1019
+ BaseRunConfiguration,
1020
+ ConfigurationWithCommandsParams,
1021
+ ServiceConfigurationParams,
1022
+ generate_dual_core_model(ServiceConfigurationConfig),
1023
+ ):
1024
+ type: Literal["service"] = "service"
1025
+
1026
+ @property
1027
+ def replica_groups(self) -> List[ReplicaGroup]:
1028
+ if self.replicas is None:
1029
+ return [
1030
+ ReplicaGroup(
1031
+ name=DEFAULT_REPLICA_GROUP_NAME,
1032
+ count=Range[int](min=1, max=1),
1033
+ commands=self.commands,
1034
+ resources=self.resources,
1035
+ scaling=self.scaling,
1036
+ )
1037
+ ]
1038
+ if isinstance(self.replicas, list):
1039
+ return self.replicas
1040
+ if isinstance(self.replicas, Range):
1041
+ return [
1042
+ ReplicaGroup(
1043
+ name=DEFAULT_REPLICA_GROUP_NAME,
1044
+ count=self.replicas,
1045
+ commands=self.commands,
1046
+ resources=self.resources,
1047
+ scaling=self.scaling,
1048
+ )
1049
+ ]
1050
+ raise ValueError(
1051
+ f"Invalid replicas type: {type(self.replicas)}. Expected None, Range[int], or List[ReplicaGroup]"
1052
+ )
1053
+
1054
+
1055
+ AnyRunConfiguration = Union[DevEnvironmentConfiguration, TaskConfiguration, ServiceConfiguration]
1056
+
1057
+
1058
+ class RunConfiguration(CoreModel):
1059
+ __root__: Annotated[
1060
+ AnyRunConfiguration,
1061
+ Field(discriminator="type"),
1062
+ ]
1063
+
1064
+
1065
+ def parse_run_configuration(data: dict) -> AnyRunConfiguration:
1066
+ try:
1067
+ conf = RunConfiguration.parse_obj(data).__root__
1068
+ except ValidationError as e:
1069
+ raise ConfigurationError(e)
1070
+ return conf
1071
+
1072
+
1073
+ class ApplyConfigurationType(str, Enum):
1074
+ DEV_ENVIRONMENT = "dev-environment"
1075
+ TASK = "task"
1076
+ SERVICE = "service"
1077
+ FLEET = "fleet"
1078
+ GATEWAY = "gateway"
1079
+ VOLUME = "volume"
1080
+
1081
+
1082
+ AnyApplyConfiguration = Union[
1083
+ AnyRunConfiguration,
1084
+ FleetConfiguration,
1085
+ GatewayConfiguration,
1086
+ VolumeConfiguration,
1087
+ ]
1088
+
1089
+
1090
+ class ApplyConfiguration(CoreModel):
1091
+ __root__: Annotated[
1092
+ AnyApplyConfiguration,
1093
+ Field(discriminator="type"),
1094
+ ]
1095
+
1096
+
1097
+ def parse_apply_configuration(data: dict) -> AnyApplyConfiguration:
1098
+ try:
1099
+ conf = ApplyConfiguration.parse_obj(data).__root__
1100
+ except ValidationError as e:
1101
+ raise ConfigurationError(e)
1102
+ return conf
1103
+
1104
+
1105
+ AnyDstackConfiguration = AnyApplyConfiguration
1106
+
1107
+
1108
+ class DstackConfiguration(CoreModel):
1109
+ __root__: Annotated[
1110
+ AnyDstackConfiguration,
1111
+ Field(discriminator="type"),
1112
+ ]
1113
+
1114
+ class Config(CoreConfig):
1115
+ json_loads = orjson.loads
1116
+ json_dumps = pydantic_orjson_dumps_with_indent
1117
+
1118
+ @staticmethod
1119
+ def schema_extra(schema: Dict[str, Any]):
1120
+ schema["$schema"] = "http://json-schema.org/draft-07/schema#"
1121
+ # Allow additionalProperties so that vscode and others not supporting
1122
+ # top-level oneOf do not warn about properties being invalid.
1123
+ schema["additionalProperties"] = True