[project] name = "dstack" dynamic = ["version", "readme"] authors = [{ name = "Andrey Cheptsov", email = "andrey@dstack.ai" }] description = "dstack is an open-source orchestration engine for running AI workloads on any cloud or on-premises." requires-python = ">=3.10" classifiers = [ "Development Status :: 4 - Beta", "Topic :: Scientific/Engineering :: Artificial Intelligence", "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", "Programming Language :: Python :: 3", ] dependencies = [ "pyyaml", "requests", "requests-unixsocket>=0.4.1", "typing-extensions>=4.0.0", "tomli>=2.0; python_version < '3.11'", "cryptography", "packaging", "python-dateutil", "cachetools", "gitpython", "jsonschema", "paramiko>=3.2.0", "cursor", "rich", "rich-argparse", "tqdm", "questionary>=2.0.1", "pydantic>=2.12", "websocket-client", "python-multipart>=0.0.16", "filelock", "psutil", "gpuhunt==0.1.34", "argcomplete>=3.5.0", "ignore-python>=0.2.0", "apscheduler<4", ] [project.urls] Homepage = "https://dstack.ai" Source = "https://github.com/dstackai/dstack" Documentation = "https://dstack.ai/docs" Issues = "https://github.com/dstackai/dstack/issues" Changelog = "https://github.com/dstackai/dstack/releases" Discord = "https://discord.gg/u8SmfwPpMd" [build-system] requires = ["hatchling", "hatch-fancy-pypi-readme"] build-backend = "hatchling.build" [project.scripts] dstack = "dstack._internal.cli.main:main" [tool.hatch.version] path = "src/dstack/version.py" [tool.hatch.build.targets.sdist] artifacts = [ "src/dstack/_internal/server/statics/**", ] [tool.hatch.build.targets.sdist.force-include] "skills/dstack/SKILL.md" = "skills/dstack/SKILL.md" "skills/dstack-prototyping/SKILL.md" = "skills/dstack-prototyping/SKILL.md" [tool.hatch.build.targets.wheel] artifacts = [ "src/dstack/_internal/server/statics/**", ] [tool.hatch.build.targets.wheel.force-include] "skills/dstack/SKILL.md" = "dstack/_internal/cli/services/presets/resources/skills/dstack/SKILL.md" "skills/dstack-prototyping/SKILL.md" = "dstack/_internal/cli/services/presets/resources/skills/dstack-prototyping/SKILL.md" [tool.hatch.metadata.hooks.fancy-pypi-readme] content-type = "text/markdown" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "README.md" [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] pattern = '\s*|]*>\s*|\s*|]*>\s*|\s*|### Demo\s*' replacement = '' ignore-case = true [tool.uv.sources] dstack-plugin-server = { path = "examples/plugins/example_plugin_server", editable = true } [tool.ruff] target-version = "py310" line-length = 99 [tool.ruff.lint] select = ["E", "F", "I", "Q", "W", "PGH", "FLY", "S113", "PLR1722"] ignore = [ "E501", "E712", ] [tool.ruff.lint.isort] known-first-party = ["dstack"] [tool.pyright] typeCheckingMode = "standard" include = [ "src/dstack/plugins", "src/dstack/_internal/server", "src/dstack/_internal/core/services", "src/dstack/_internal/core/backends", "src/dstack/_internal/cli/services/configurators", "src/dstack/_internal/cli/commands", "src/tests/_internal/server/background/pipeline_tasks", ] exclude = [ # OCI has pyright errors. Excluded rather than ignored because checking it makes pyright analyze # the large OCI SDK, which is slow and takes a lot of memory. Editors still show its errors "src/dstack/_internal/core/backends/oci", ] ignore = [ "src/dstack/_internal/server/migrations/versions", ] [tool.pytest.ini_options] testpaths = ["src/tests"] addopts = [ "--disable-socket", "--allow-hosts=127.0.0.1,localhost", # unix socket for Docker/testcontainers "--allow-unix-socket", ] # One session-scoped event loop for fixtures and tests so that `sqlite_db` and `postgres_db` engines are reused. # Tests must not leave pending asyncio tasks behind; nothing enforces it automatically. asyncio_default_fixture_loop_scope = "session" asyncio_default_test_loop_scope = "session" markers = [ "shim_version", "dockerized", ] env = [ "DSTACK_SSHPROXY_API_TOKEN=test-token", # Pin every rich console in the process to non-terminal output so tests asserting on plain CLI # text never see ANSI escapes. "TTY_COMPATIBLE=0", ] filterwarnings = [ # Fail on any use of a pydantic v1 shim (`.dict()`, `parse_obj_as`, class-based `Config`, ...) # so they cannot creep back after the v2 migration. "error::pydantic.PydanticDeprecatedSince20", # Remove or change to 3.11 once support for Python 3.10 is dropped. "ignore:You are using a Python version \\(3.10:FutureWarning:^google\\.", ] [tool.tox] env_list = ["default"] [tool.tox.env_run_base] description = "Run tests under {base_python}" extras = ["all"] dependency_groups = ["test"] commands = [["pytest", { replace = "posargs", extend = true }]] # `tox run [-e default]` -- run tests inside an isolated environment # with Python version read from `.python-version` file [tool.tox.env.default] base_python_file = [".python-version"] # `tox run -e dummy --skip-env-install` -- run tests inside the current environment # all dependencies must be already installed; a dummy environment is still created [tool.tox.env.dummy] description = "Run tests inside the current environment" # Must be called with --skip-env-install dependency_groups = ["nonexistent-group-to-trigger-failure"] allowlist_externals = ["pytest"] [dependency-groups] dev = [ "pre-commit>=4.2.0", "ruff==0.12.7", # should match .pre-commit-config.yaml "pyinstrument>=5.0.0", "kubernetes-stubs-elephant-fork>=35.0.0.post3", {include-group = "test"}, {include-group = "docs"}, ] test = [ "pytest~=8.0", "pytest-asyncio>=0.25.2", "pytest-mock>=3.14.0", "pytest-httpbin>=2.1.0", "pytest-socket>=0.7.0", "pytest-env>=1.1.0", "pytest-unordered>=0.7.0", "pytest-xdist>=3.6.1", "requests-mock>=1.12.1", "time-machine>=3.5.1", "httpbin>=0.10.2", # indirect to make compatible with Werkzeug 3 "openai>=1.68.2", "testcontainers>=4.15.0", ] docs = [ "dstack[server]", "dstack-plugin-server", "pillow", "cairosvg", "mkdocs-material>=9.7.0", "mkdocs-material[imaging]", "mkdocs-material-extensions", "mkdocs-redirects", "mkdocs-gen-files", "mkdocstrings[python]", "mkdocs-render-swagger-plugin", ] [project.optional-dependencies] gateway = [ "fastapi", "starlette>=0.26.0", "uvicorn", # Supervise long-lived SSH children without a waiting thread for each replica. "uvloop>=0.18.0; sys_platform != 'win32' and platform_python_implementation != 'PyPy'", "aiorwlock", "aiocache", "httpx>=0.28.0", # Gateway authorization needs the pool cancellation and recovery fixes in httpx2 (#4338). "httpx2>=2.13.1", "jinja2", ] server = [ "fastapi", "starlette>=0.26.0", "uvicorn[standard]", "aiorwlock", "aiocache", "httpx>=0.28.0", "jinja2", "watchfiles", "sqlalchemy[asyncio]>=2.0.0", "sqlalchemy_utils>=0.43.0", "alembic>=1.16.0", "aiosqlite", "docker>=6.0.0", "python-dxf>=12.1.1", "sentry-sdk[fastapi]>=2.27.0", "alembic-postgresql-enum", "asyncpg", "python-json-logger>=3.1.0", "prometheus-client", "grpcio>=1.81.0", "protobuf>=6.33.5", "smg-grpc-proto==0.4.9", # Pin to avoid grpcio version mismatch when smg-grpc-proto bumps grpcio dependency https://github.com/dstackai/dstack/pull/3971 ] aws = [ "boto3>=1.38.13", "botocore", "dstack[server]", ] azure = [ "azure-identity>=1.12.0", "azure-mgmt-subscription>=3.1.1", "azure-mgmt-compute>=29.1.0", "azure-mgmt-network>=23.0.0,<28.0.0", "azure-mgmt-resource>=22.0.0", "azure-mgmt-authorization>=3.0.0", "azure-mgmt-msi>=7.0.0", "dstack[server]", ] gcp = [ "google-auth>=2.3.0", "google-cloud-storage>=2.0.0", "google-cloud-compute>=1.5.0", "google-cloud-logging>=2.0.0", "google-api-python-client>=2.80.0", "google-cloud-billing>=1.11.0", "google-cloud-tpu>=1.18.3", "dstack[server]", ] datacrunch = [ "verda>=1.23.0", "dstack[server]", ] verda = [ "verda>=1.23.0", "dstack[server]", ] kubernetes = [ "kubernetes", "dstack[server]", ] lambda = [ "boto3>=1.38.13", "botocore", "dstack[server]", ] oci = [ "oci>=2.150.0", "cryptography>=44.0.3", # pyopenssl is indirect to avoid uv falling back to the old version # due to an upper limit from oci "pyopenssl>=23.2.0", "dstack[server]", ] nebius = [ "nebius>=0.6.13,<0.7", "dstack[server]", ] fluentbit = [ "fluent-logger>=0.10.0", "elasticsearch>=8.0.0", "dstack[server]", ] otel = [ "opentelemetry-sdk>=1.30.0", "opentelemetry-exporter-otlp-proto-http>=1.30.0", "opentelemetry-exporter-prometheus>=0.51b0", "opentelemetry-instrumentation-fastapi>=0.51b0", "opentelemetry-instrumentation-sqlalchemy>=0.51b0", "opentelemetry-instrumentation-httpx>=0.51b0", "opentelemetry-instrumentation-requests>=0.51b0", "opentelemetry-instrumentation-system-metrics>=0.51b0", "opentelemetry-instrumentation-logging>=0.61b0", "dstack[server]", ] crusoe = [ "dstack[server]", ] all = [ "dstack[gateway,server,aws,azure,gcp,verda,kubernetes,lambda,nebius,oci,crusoe,fluentbit,otel]", ]