gpustack-runtime 0.2.4.post2__tar.gz → 0.2.5__tar.gz

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 (152) hide show
  1. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/PKG-INFO +2 -2
  2. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/_version.py +2 -2
  3. gpustack_runtime-0.2.5/gpustack_runtime/_version_appendix.py +1 -0
  4. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/amd.py +53 -15
  5. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/hygon.py +12 -3
  6. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/pyproject.toml +3 -3
  7. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_amd.py +96 -1
  8. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_hygon.py +52 -3
  9. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/uv.lock +57 -43
  10. gpustack_runtime-0.2.4.post2/gpustack_runtime/_version_appendix.py +0 -1
  11. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/.codespelldict +0 -0
  12. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/.codespellrc +0 -0
  13. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/.dockerignore +0 -0
  14. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/.gitattributes +0 -0
  15. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/.gitignore +0 -0
  16. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/.pre-commit-config.yaml +0 -0
  17. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/.python-version +0 -0
  18. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/LICENSE +0 -0
  19. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/Makefile +0 -0
  20. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/README.md +0 -0
  21. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/deploy/manifests/docker-compose.yaml +0 -0
  22. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/deploy/manifests/kubernetes.yaml +0 -0
  23. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/docs/index.md +0 -0
  24. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/docs/modules/gpustack_runtime.deployer.md +0 -0
  25. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/docs/modules/gpustack_runtime.detector.md +0 -0
  26. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/docs/modules/gpustack_runtime.md +0 -0
  27. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/__init__.py +0 -0
  28. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/__main__.py +0 -0
  29. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/_version.pyi +0 -0
  30. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/cmds/__init__.py +0 -0
  31. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/cmds/__types__.py +0 -0
  32. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/cmds/deployer.py +0 -0
  33. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/cmds/detector.py +0 -0
  34. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/cmds/images.py +0 -0
  35. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/__init__.py +0 -0
  36. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/__patches__.py +0 -0
  37. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/__types__.py +0 -0
  38. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/__utils__.py +0 -0
  39. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/cdi/__init__.py +0 -0
  40. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/cdi/__types__.py +0 -0
  41. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/cdi/__utils__.py +0 -0
  42. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/cdi/amd.py +0 -0
  43. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/cdi/ascend.py +0 -0
  44. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/cdi/hygon.py +0 -0
  45. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/cdi/iluvatar.py +0 -0
  46. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/cdi/metax.py +0 -0
  47. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/cdi/thead.py +0 -0
  48. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/docker.py +0 -0
  49. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/k8s/devicemanager/__init__.py +0 -0
  50. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/kuberentes.py +0 -0
  51. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/deployer/podman.py +0 -0
  52. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/__init__.py +0 -0
  53. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/__types__.py +0 -0
  54. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/__utils__.py +0 -0
  55. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/ascend.py +0 -0
  56. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/cambricon.py +0 -0
  57. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/iluvatar.py +0 -0
  58. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/metax.py +0 -0
  59. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/mthreads.py +0 -0
  60. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/nvidia.py +0 -0
  61. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pyamdgpu/__init__.py +0 -0
  62. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pyamdsmi/__init__.py +0 -0
  63. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pycndev/__init__.py +0 -0
  64. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pydcmi/__init__.py +0 -0
  65. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pydmi/__init__.py +0 -0
  66. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pyhgml/__init__.py +0 -0
  67. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pyhgml/libhgml.so +0 -0
  68. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pyhgml/libuki.so +0 -0
  69. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pyhsa/__init__.py +0 -0
  70. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pyixml/__init__.py +0 -0
  71. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pymtml/__init__.py +0 -0
  72. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pymxsml/__init__.py +0 -0
  73. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pynvml/__init__.py +0 -0
  74. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/pyrocmsmi/__init__.py +0 -0
  75. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/detector/thead.py +0 -0
  76. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/envs.py +0 -0
  77. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/gpustack_runtime/logging.py +0 -0
  78. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/hatch.toml +0 -0
  79. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/mkdocs.yml +0 -0
  80. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/pack/Dockerfile +0 -0
  81. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/pack/Dockerfile.dummy +0 -0
  82. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/pytest.ini +0 -0
  83. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/ruff.toml +0 -0
  84. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/specs/2026-08-14-detector-alignment-and-workload-exit-status.md +0 -0
  85. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/fixtures/__init__.py +0 -0
  86. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/fixtures/test_compare_versions.json +0 -0
  87. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/fixtures/test_correct_runner_image.json +0 -0
  88. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json.json +0 -0
  89. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_jsons.json +0 -0
  90. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_yamls.yaml +0 -0
  91. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_json.json +0 -0
  92. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_yaml.yaml +0 -0
  93. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/fixtures/test_nginx_entrypoint.sh +0 -0
  94. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_docker_status.py +0 -0
  95. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_kubernetes_status.py +0 -0
  96. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_podman_status.py +0 -0
  97. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_privileged.py +0 -0
  98. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_resource_injection_policy.py +0 -0
  99. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_runtime_class.py +0 -0
  100. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_termination_grace_period.py +0 -0
  101. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_utils.py +0 -0
  102. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_visible_devices_ordering.py +0 -0
  103. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/deployer/test_workload_status.py +0 -0
  104. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/fixtures/__init__.py +0 -0
  105. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/README.md +0 -0
  106. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi300x.json +0 -0
  107. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi308x.json +0 -0
  108. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_amd_rx7800xt.json +0 -0
  109. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_ascend_310p3.json +0 -0
  110. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_ascend_910b2.json +0 -0
  111. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_hygon_k100ai.json +0 -0
  112. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_metax_c500.json +0 -0
  113. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_gb10.json +0 -0
  114. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100.json +0 -0
  115. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100_mig.json +0 -0
  116. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h200.json +0 -0
  117. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4080super.json +0 -0
  118. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090_48g.json +0 -0
  119. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090d.json +0 -0
  120. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx5090d.json +0 -0
  121. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/detect_output_thead_ppu.json +0 -0
  122. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi300x.json +0 -0
  123. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi308x.json +0 -0
  124. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_amd_rx7800xt.json +0 -0
  125. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_ascend_310p3.json +0 -0
  126. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_ascend_910b2.json +0 -0
  127. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_hygon_k100ai.json +0 -0
  128. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_metax_c500.json +0 -0
  129. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_mthreads_s5000.json +0 -0
  130. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100.json +0 -0
  131. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100_mig.json +0 -0
  132. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h200.json +0 -0
  133. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4080super.json +0 -0
  134. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090_48g.json +0 -0
  135. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090d.json +0 -0
  136. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx5090d.json +0 -0
  137. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/samples/topology_output_thead_ppu.json +0 -0
  138. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_ascend.py +0 -0
  139. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_cambricon.py +0 -0
  140. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_detector_cli.py +0 -0
  141. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_detector_types.py +0 -0
  142. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_detector_utils.py +0 -0
  143. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_iluvatar.py +0 -0
  144. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_metax.py +0 -0
  145. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_mig_devices.py +0 -0
  146. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_mthreads.py +0 -0
  147. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_nvidia.py +0 -0
  148. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_pycndev.py +0 -0
  149. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_pydmi.py +0 -0
  150. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_samples.py +0 -0
  151. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/tests/gpustack_runtime/detector/test_thead.py +0 -0
  152. {gpustack_runtime-0.2.4.post2 → gpustack_runtime-0.2.5}/uv.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gpustack-runtime
3
- Version: 0.2.4.post2
3
+ Version: 0.2.5
4
4
  Summary: GPUStack Runtime is library for detecting GPU resources and launching GPU workloads.
5
5
  Project-URL: Homepage, https://github.com/gpustack/runtime
6
6
  Project-URL: Bug Tracker, https://github.com/gpustack/gpustack/issues
@@ -16,7 +16,7 @@ Requires-Python: >=3.10
16
16
  Requires-Dist: argcomplete>=3.6.3
17
17
  Requires-Dist: cachetools>=5.5.2
18
18
  Requires-Dist: docker>=7.1.0
19
- Requires-Dist: gpustack-runner>=0.1.26.post2
19
+ Requires-Dist: gpustack-runner>=0.1.29
20
20
  Requires-Dist: kubernetes>=33.1.0
21
21
  Requires-Dist: mthreads-ml-py>=2.2.10
22
22
  Requires-Dist: nvidia-ml-py>=13.580.65
@@ -27,8 +27,8 @@ version_tuple: VERSION_TUPLE
27
27
  __commit_id__: COMMIT_ID
28
28
  commit_id: COMMIT_ID
29
29
 
30
- __version__ = version = '0.2.4.post2'
31
- __version_tuple__ = version_tuple = (0, 2, 4, 'post2')
30
+ __version__ = version = '0.2.5'
31
+ __version_tuple__ = version_tuple = (0, 2, 5)
32
32
  try:
33
33
  from ._version_appendix import git_commit
34
34
  __commit_id__ = commit_id = git_commit
@@ -0,0 +1 @@
1
+ git_commit = "81fd0aa"
@@ -4,6 +4,7 @@ import contextlib
4
4
  import logging
5
5
  from functools import lru_cache
6
6
  from pathlib import Path
7
+ from typing import Any
7
8
 
8
9
  from .. import envs
9
10
  from ..logging import debug_log_exception, debug_log_warning
@@ -137,7 +138,7 @@ class AMDDetector(Detector):
137
138
  )
138
139
 
139
140
  dev_gpu_driver_info = pyamdsmi.amdsmi_get_gpu_driver_info(dev)
140
- dev_driver_ver = dev_gpu_driver_info.get("driver_version")
141
+ dev_driver_ver = _get_reading(dev_gpu_driver_info, "driver_version")
141
142
 
142
143
  # The operator resolves the name from the local PCI ID database
143
144
  # first: pci.ids knows the board -- the subsystem vendor's name
@@ -157,7 +158,7 @@ class AMDDetector(Detector):
157
158
  ):
158
159
  dev_name = pyamdgpu.amdgpu_get_marketing_name(dev_gpudev)
159
160
  if not dev_name:
160
- dev_name = dev_gpu_asic_info.get("market_name")
161
+ dev_name = _get_reading(dev_gpu_asic_info, "market_name", "")
161
162
 
162
163
  dev_cc = dev_hsa_agent.compute_capability
163
164
  if not dev_cc:
@@ -211,12 +212,12 @@ class AMDDetector(Detector):
211
212
  # The power limit is inventory, so it stays here, while the used
212
213
  # power the same call carries belongs to the usage query.
213
214
  dev_power = None
214
- try:
215
+ with contextlib.suppress(pyamdsmi.AmdSmiException):
215
216
  dev_power_info = pyamdsmi.amdsmi_get_power_info(dev)
216
- dev_power = (
217
- dev_power_info.get("power_limit", 0) // 1000000
218
- ) # uW to W
219
- except pyamdsmi.AmdSmiException:
217
+ dev_power_limit = _get_reading(dev_power_info, "power_limit")
218
+ if dev_power_limit is not None:
219
+ dev_power = dev_power_limit // 1000000 # uW to W
220
+ if dev_power is None:
220
221
  with contextlib.suppress(pyrocmsmi.ROCMSMIError):
221
222
  dev_power = pyrocmsmi.rsmi_dev_power_cap_get(dev_idx)
222
223
 
@@ -308,8 +309,14 @@ class AMDDetector(Detector):
308
309
  dev_temp = None
309
310
  try:
310
311
  dev_gpu_metrics_info = pyamdsmi.amdsmi_get_gpu_metrics_info(dev)
311
- dev_cores_util = dev_gpu_metrics_info.get("average_gfx_activity", 0)
312
- dev_temp = dev_gpu_metrics_info.get("temperature_hotspot", 0)
312
+ dev_cores_util = _get_reading(
313
+ dev_gpu_metrics_info,
314
+ "average_gfx_activity",
315
+ )
316
+ dev_temp = _get_reading(
317
+ dev_gpu_metrics_info,
318
+ "temperature_hotspot",
319
+ )
313
320
  except pyamdsmi.AmdSmiException:
314
321
  with contextlib.suppress(pyrocmsmi.ROCMSMIError):
315
322
  dev_cores_util = pyrocmsmi.rsmi_dev_busy_percent_get(dev_idx)
@@ -353,15 +360,21 @@ class AMDDetector(Detector):
353
360
  if dev_ecc_count.uncorrectable_err > 0:
354
361
  dev_mem_status = DeviceMemoryStatusEnum.UNHEALTHY
355
362
 
363
+ # A sentinel reading means the same as a failed call -- AMD SMI
364
+ # cannot tell the used power -- so both route to ROCm SMI.
356
365
  dev_power_used = None
357
- try:
366
+ with contextlib.suppress(pyamdsmi.AmdSmiException):
358
367
  dev_power_info = pyamdsmi.amdsmi_get_power_info(dev)
359
- dev_power_used = (
360
- dev_power_info.get("current_socket_power")
361
- if dev_power_info.get("current_socket_power", "N/A") != "N/A"
362
- else dev_power_info.get("average_socket_power", 0)
368
+ dev_power_used = _get_reading(
369
+ dev_power_info,
370
+ "current_socket_power",
363
371
  )
364
- except pyamdsmi.AmdSmiException:
372
+ if dev_power_used is None:
373
+ dev_power_used = _get_reading(
374
+ dev_power_info,
375
+ "average_socket_power",
376
+ )
377
+ if dev_power_used is None:
365
378
  with contextlib.suppress(pyrocmsmi.ROCMSMIError):
366
379
  dev_power_used = pyrocmsmi.rsmi_dev_power_get(dev_idx)
367
380
 
@@ -510,6 +523,31 @@ class AMDDetector(Detector):
510
523
  return ret
511
524
 
512
525
 
526
+ def _get_reading(dev_info: dict, key: str, default: Any = None) -> Any:
527
+ """
528
+ Read one value out of an AMD SMI answer, treating its sentinel as absent.
529
+
530
+ AMD SMI reports an unavailable reading in-band: it rewrites the 0xFFFF (or
531
+ max-uint) the driver answered into the string "N/A" before returning, so a
532
+ dict otherwise holding numbers can carry a string at any key. A VF is where
533
+ this surfaces, its host-side telemetry being invisible to the guest.
534
+
535
+ Args:
536
+ dev_info:
537
+ The answer AMD SMI returned.
538
+ key:
539
+ The reading to take.
540
+ default:
541
+ What an absent or unavailable reading becomes.
542
+
543
+ Returns:
544
+ The reading, or the default.
545
+
546
+ """
547
+ dev_reading = dev_info.get(key, "N/A")
548
+ return default if dev_reading == "N/A" else dev_reading
549
+
550
+
513
551
  def _get_pci_device_name_by_bdf(dev_bdf: str) -> str:
514
552
  """
515
553
  Get the name of a device from the local PCI ID database.
@@ -349,8 +349,15 @@ class HygonDetector(Detector):
349
349
  for dev_idx in range(devs_count):
350
350
  dev_uuid = f"GPU-{pyrocmsmi.rsmi_dev_unique_id_get(dev_idx)[2:]}"
351
351
 
352
- dev_cores_util = pyrocmsmi.rsmi_dev_busy_percent_get(dev_idx)
353
- dev_temp = pyrocmsmi.rsmi_dev_temp_metric_get(dev_idx)
352
+ # Each reading is isolated on its own, mirroring the AMD path:
353
+ # ROCm SMI raises on one it cannot serve, and an unwrapped call
354
+ # would cost the whole sweep rather than the reading.
355
+ dev_cores_util = None
356
+ with contextlib.suppress(pyrocmsmi.ROCMSMIError):
357
+ dev_cores_util = pyrocmsmi.rsmi_dev_busy_percent_get(dev_idx)
358
+ dev_temp = None
359
+ with contextlib.suppress(pyrocmsmi.ROCMSMIError):
360
+ dev_temp = pyrocmsmi.rsmi_dev_temp_metric_get(dev_idx)
354
361
  if dev_cores_util is None:
355
362
  debug_log_warning(
356
363
  logger,
@@ -376,7 +383,9 @@ class HygonDetector(Detector):
376
383
  if dev_ecc_count.uncorrectable_err > 0:
377
384
  dev_mem_status = DeviceMemoryStatusEnum.UNHEALTHY
378
385
 
379
- dev_power_used = pyrocmsmi.rsmi_dev_power_get(dev_idx)
386
+ dev_power_used = None
387
+ with contextlib.suppress(pyrocmsmi.ROCMSMIError):
388
+ dev_power_used = pyrocmsmi.rsmi_dev_power_get(dev_idx)
380
389
 
381
390
  usages.append(
382
391
  Device(
@@ -23,7 +23,7 @@ dependencies = [
23
23
  # NB(thxCode): Pin to this Podman version for a custom patch. See gpustack_runtime/deployer/__patches__.py.
24
24
  "podman==5.6.0",
25
25
  "pyyaml",
26
- "gpustack-runner>=0.1.26.post2",
26
+ "gpustack-runner>=0.1.29",
27
27
  "nvidia-ml-py>=13.580.65",
28
28
  "mthreads-ml-py>=2.2.10",
29
29
  "argcomplete>=3.6.3",
@@ -43,13 +43,13 @@ gpustack-runtime = "gpustack_runtime.__main__:main"
43
43
  dev=[
44
44
  "pyclean==3.4.0",
45
45
  "pre-commit==4.5.0",
46
- "pytest==8.4.2",
46
+ "pytest>=9.0.3,<10",
47
47
  "hatchling",
48
48
  "hatch-vcs",
49
49
  "mkdocs>=1.6.1",
50
50
  "mkdocstrings-python>=1.18.0",
51
51
  "mkdocs-material>=9.6.18",
52
- "pymdown-extensions>=10.16.1",
52
+ "pymdown-extensions>=11.0.1",
53
53
  "dockerpty>=0.4.1",
54
54
  "amdsmi>=7.0.2",
55
55
  ]
@@ -152,7 +152,14 @@ class _FakeRocmSmi:
152
152
 
153
153
  def rsmi_dev_power_get(self, dev_idx: int) -> int:
154
154
  self.calls.append("rsmi_dev_power_get")
155
- return self.cards[dev_idx]["power_used"]
155
+ # A card whose fixture carries no used power stands for the one ROCm
156
+ # SMI cannot read either -- a VF, where the binding raises rather than
157
+ # answering the sentinel AMD SMI would.
158
+ power_used = self.cards[dev_idx]["power_used"]
159
+ if power_used is None:
160
+ msg = "power is not supported on this device"
161
+ raise _FakeRocmSmiError(msg)
162
+ return power_used
156
163
 
157
164
 
158
165
  class _FakeHSA:
@@ -527,6 +534,94 @@ def test_detect_composes_the_information_and_the_usage(amd_bindings):
527
534
  assert devices[0].power_used == 142
528
535
 
529
536
 
537
+ # --------------------------------------------------------------------------- #
538
+ # AMD SMI's "N/A": an unavailable reading is reported in-band, as a string #
539
+ # inside a dict otherwise holding numbers. A VF exposes no host-side power #
540
+ # telemetry, so its driver answers 0xFFFF and the library hands over "N/A". #
541
+ # --------------------------------------------------------------------------- #
542
+
543
+
544
+ def test_detect_info_falls_back_when_the_sentinel_hides_the_power_limit(amd_bindings):
545
+ # The limit is floor-divided from uW to W, so a sentinel reaching that
546
+ # arithmetic raises a TypeError -- which is not an AmdSmiException, so it
547
+ # escapes the handler and costs the host every AMD card.
548
+ card = _card("0000:05:00.0", "0x00a1b2c3d4e5f600")
549
+ card["power"]["power_limit"] = "N/A"
550
+ calls = amd_bindings([card], agents=[_agent("0000:05:00.0")])
551
+
552
+ devices = AMDDetector().detect_info()
553
+
554
+ assert "rsmi_dev_power_cap_get" in calls
555
+ assert devices[0].power == 700
556
+
557
+
558
+ def test_detect_info_reports_no_driver_version_when_the_sentinel_hides_it(
559
+ amd_bindings,
560
+ ):
561
+ card = _card("0000:05:00.0", "0x00a1b2c3d4e5f600")
562
+ card["driver_info"]["driver_version"] = "N/A"
563
+ amd_bindings([card], agents=[_agent("0000:05:00.0")])
564
+
565
+ devices = AMDDetector().detect_info()
566
+
567
+ assert devices[0].driver_version is None
568
+
569
+
570
+ def test_detect_info_never_names_a_board_after_the_sentinel(amd_bindings):
571
+ # The ASIC market name is the last link of the name chain, so a sentinel
572
+ # there would otherwise be stored as the board's name.
573
+ card = _card("0000:05:00.0", "0x00a1b2c3d4e5f600")
574
+ card["asic_info"]["market_name"] = "N/A"
575
+ amd_bindings([card], pci_ids=None)
576
+
577
+ devices = AMDDetector().detect_info()
578
+
579
+ assert devices[0].name == ""
580
+
581
+
582
+ def test_detect_usage_falls_back_when_the_sentinel_hides_the_socket_power(
583
+ amd_bindings,
584
+ ):
585
+ # The sentinel has to route to the ROCm SMI fallback the same way an AMD
586
+ # SMI failure does -- both mean "AMD SMI cannot tell us the used power".
587
+ card = _card("0000:05:00.0", "0x00a1b2c3d4e5f600")
588
+ card["power"]["current_socket_power"] = "N/A"
589
+ card["power"]["average_socket_power"] = "N/A"
590
+ calls = amd_bindings([card], agents=[_agent("0000:05:00.0")])
591
+
592
+ devices = AMDDetector().detect_usage()
593
+
594
+ assert "rsmi_dev_power_get" in calls
595
+ assert devices[0].power_used == 131
596
+
597
+
598
+ def test_detect_usage_reports_no_power_when_no_binding_can_read_it(amd_bindings):
599
+ # The reported VF: AMD SMI answers the sentinel for both socket readings
600
+ # and ROCm SMI cannot read the power either, so the field is absent rather
601
+ # than carrying a string the consumer parses as a number.
602
+ card = _card("0000:05:00.0", "0x00a1b2c3d4e5f600")
603
+ card["power"]["current_socket_power"] = "N/A"
604
+ card["power"]["average_socket_power"] = "N/A"
605
+ card["power_used"] = None
606
+ amd_bindings([card], agents=[_agent("0000:05:00.0")])
607
+
608
+ devices = AMDDetector().detect_usage()
609
+
610
+ assert devices[0].power_used is None
611
+
612
+
613
+ def test_detect_usage_reports_no_metrics_when_the_sentinel_hides_them(amd_bindings):
614
+ card = _card("0000:05:00.0", "0x00a1b2c3d4e5f600")
615
+ card["metrics"]["average_gfx_activity"] = "N/A"
616
+ card["metrics"]["temperature_hotspot"] = "N/A"
617
+ amd_bindings([card], agents=[_agent("0000:05:00.0")])
618
+
619
+ devices = AMDDetector().detect_usage()
620
+
621
+ assert devices[0].cores_utilization == 0
622
+ assert devices[0].temperature is None
623
+
624
+
530
625
  # --------------------------------------------------------------------------- #
531
626
  # The CDI generator, which numbers its device nodes from the appendix. #
532
627
  # --------------------------------------------------------------------------- #
@@ -244,11 +244,11 @@ class _FakeRocmSmi:
244
244
 
245
245
  def rsmi_dev_busy_percent_get(self, dev_idx: int) -> int:
246
246
  self.calls.append("rsmi_dev_busy_percent_get")
247
- return self.cards[dev_idx]["busy_percent"]
247
+ return self._reading(dev_idx, "busy_percent")
248
248
 
249
249
  def rsmi_dev_temp_metric_get(self, dev_idx: int) -> int:
250
250
  self.calls.append("rsmi_dev_temp_metric_get")
251
- return self.cards[dev_idx]["temperature"]
251
+ return self._reading(dev_idx, "temperature")
252
252
 
253
253
  def rsmi_dev_power_cap_get(self, dev_idx: int) -> int:
254
254
  self.calls.append("rsmi_dev_power_cap_get")
@@ -256,7 +256,17 @@ class _FakeRocmSmi:
256
256
 
257
257
  def rsmi_dev_power_get(self, dev_idx: int) -> int:
258
258
  self.calls.append("rsmi_dev_power_get")
259
- return self.cards[dev_idx]["power_used"]
259
+ return self._reading(dev_idx, "power_used")
260
+
261
+ def _reading(self, dev_idx: int, key: str) -> int:
262
+ # A card whose fixture carries None for a reading stands for the one
263
+ # ROCm SMI cannot answer: the binding checks every return code and
264
+ # raises, rather than handing back a sentinel the way AMD SMI does.
265
+ reading = self.cards[dev_idx][key]
266
+ if reading is None:
267
+ msg = f"{key} is not supported on this device"
268
+ raise _FakeRocmSmiError(msg)
269
+ return reading
260
270
 
261
271
  def rsmi_topo_get_numa_node_number(self, dev_idx: int) -> int:
262
272
  return self.cards[dev_idx]["numa"]
@@ -629,6 +639,45 @@ def test_detect_composes_the_information_and_the_usage(hygon_bindings):
629
639
  assert devices[0].power_used == 217
630
640
 
631
641
 
642
+ def test_detect_usage_bounds_an_unreadable_power_to_its_own_card(hygon_bindings):
643
+ # ROCm SMI raises on a reading it cannot serve, so an unwrapped read takes
644
+ # the whole sweep down with it and leaves every card on the host with its
645
+ # information-query values.
646
+ hygon_bindings(
647
+ [
648
+ _card("0000:0b:00.0", "0x9f8e7d6c5b4a3921", power_used=None),
649
+ _card("0000:0c:00.0", "0x9f8e7d6c5b4a3922"),
650
+ ],
651
+ agents=[_agent("0000:0b:00.0"), _agent("0000:0c:00.0")],
652
+ )
653
+
654
+ devices = HygonDetector().detect_usage()
655
+
656
+ assert [dev.power_used for dev in devices] == [None, 217]
657
+ # The rest of the faulty card's usage still arrives, and the healthy card
658
+ # is untouched.
659
+ assert [dev.cores_utilization for dev in devices] == [44, 44]
660
+ assert [dev.temperature for dev in devices] == [51, 51]
661
+ assert [dev.memory_used for dev in devices] == [1024, 1024]
662
+
663
+
664
+ def test_detect_usage_bounds_an_unreadable_utilization_to_its_own_reading(
665
+ hygon_bindings,
666
+ ):
667
+ # Each reading is isolated on its own, so an unreadable utilization does
668
+ # not carry off the temperature the next call would have served.
669
+ hygon_bindings(
670
+ [_card("0000:0b:00.0", "0x9f8e7d6c5b4a3921", busy_percent=None)],
671
+ agents=[_agent("0000:0b:00.0")],
672
+ )
673
+
674
+ devices = HygonDetector().detect_usage()
675
+
676
+ assert devices[0].cores_utilization == 0
677
+ assert devices[0].temperature == 51
678
+ assert devices[0].power_used == 217
679
+
680
+
632
681
  # --------------------------------------------------------------------------- #
633
682
  # The CDI generator, which numbers its device nodes from the appendix. #
634
683
  # --------------------------------------------------------------------------- #