aignostics 0.2.201__tar.gz → 0.2.203__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 (203) hide show
  1. {aignostics-0.2.201 → aignostics-0.2.203}/.gitignore +2 -0
  2. {aignostics-0.2.201 → aignostics-0.2.203}/PKG-INFO +7 -7
  3. {aignostics-0.2.201 → aignostics-0.2.203}/examples/notebook.py +1 -1
  4. {aignostics-0.2.201 → aignostics-0.2.203}/pyproject.toml +11 -11
  5. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_cli.py +53 -1
  6. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/_frame.py +3 -2
  7. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/_page_application_describe.py +98 -3
  8. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/_utils.py +20 -14
  9. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_service.py +5 -1
  10. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_utils.py +38 -0
  11. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/gui/_theme.py +2 -0
  12. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/__init__.py +2 -0
  13. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_client.py +0 -1
  14. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_sdk_metadata.py +62 -1
  15. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/resources/runs.py +44 -16
  16. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_constants.py +6 -1
  17. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/boot.py +1 -3
  18. {aignostics-0.2.201 → aignostics-0.2.203}/LICENSE +0 -0
  19. {aignostics-0.2.201 → aignostics-0.2.203}/README.md +0 -0
  20. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/.openapi-generator/FILES +0 -0
  21. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/.openapi-generator/VERSION +0 -0
  22. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/api/public_api.py +0 -0
  23. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/api_client.py +0 -0
  24. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/api_response.py +0 -0
  25. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/configuration.py +0 -0
  26. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/exceptions.py +0 -0
  27. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/__init__.py +0 -0
  28. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/application_read_response.py +0 -0
  29. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/application_read_short_response.py +0 -0
  30. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/application_version.py +0 -0
  31. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/artifact_output.py +0 -0
  32. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/artifact_state.py +0 -0
  33. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/artifact_termination_reason.py +0 -0
  34. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/auth0_organization.py +0 -0
  35. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/auth0_user.py +0 -0
  36. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/custom_metadata_update_request.py +0 -0
  37. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/http_validation_error.py +0 -0
  38. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/input_artifact.py +0 -0
  39. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/input_artifact_creation_request.py +0 -0
  40. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/item_creation_request.py +0 -0
  41. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/item_output.py +0 -0
  42. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/item_result_read_response.py +0 -0
  43. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/item_state.py +0 -0
  44. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/item_termination_reason.py +0 -0
  45. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/me_read_response.py +0 -0
  46. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/organization_read_response.py +0 -0
  47. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/output_artifact.py +0 -0
  48. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/output_artifact_result_read_response.py +0 -0
  49. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/output_artifact_scope.py +0 -0
  50. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/output_artifact_visibility.py +0 -0
  51. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/run_creation_request.py +0 -0
  52. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/run_creation_response.py +0 -0
  53. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/run_item_statistics.py +0 -0
  54. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/run_output.py +0 -0
  55. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/run_read_response.py +0 -0
  56. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/run_state.py +0 -0
  57. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/run_termination_reason.py +0 -0
  58. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/user_read_response.py +0 -0
  59. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/validation_error.py +0 -0
  60. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/validation_error_loc_inner.py +0 -0
  61. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/models/version_read_response.py +0 -0
  62. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/aignx/codegen/rest.py +0 -0
  63. {aignostics-0.2.201 → aignostics-0.2.203}/codegen/out/docs/PublicApi.md +0 -0
  64. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-macos-13/extracted/aignostics.app/Contents/Frameworks/boto3/examples/cloudfront.rst +0 -0
  65. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-macos-13/extracted/aignostics.app/Contents/Frameworks/boto3/examples/s3.rst +0 -0
  66. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-macos-latest/extracted/aignostics.app/Contents/Frameworks/boto3/examples/cloudfront.rst +0 -0
  67. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-macos-latest/extracted/aignostics.app/Contents/Frameworks/boto3/examples/s3.rst +0 -0
  68. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-ubuntu-24.04-arm/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  69. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-ubuntu-24.04-arm/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  70. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-ubuntu-latest/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  71. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-ubuntu-latest/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  72. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-windows-11-arm/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  73. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-windows-11-arm/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  74. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-windows-latest/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  75. {aignostics-0.2.201 → aignostics-0.2.203}/dist_native_all/dist_native-windows-latest/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  76. {aignostics-0.2.201 → aignostics-0.2.203}/examples/__init__.py +0 -0
  77. {aignostics-0.2.201 → aignostics-0.2.203}/examples/notebook.ipynb +0 -0
  78. {aignostics-0.2.201 → aignostics-0.2.203}/examples/script.py +0 -0
  79. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/CLAUDE.md +0 -0
  80. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/__init__.py +0 -0
  81. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/CLAUDE.md +0 -0
  82. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/__init__.py +0 -0
  83. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_download.py +0 -0
  84. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/_page_application_run_describe.py +0 -0
  85. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/_page_builder.py +0 -0
  86. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/_page_index.py +0 -0
  87. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/.gitignore +0 -0
  88. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/canceled.lottie +0 -0
  89. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/done.lottie +0 -0
  90. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/empty.lottie +0 -0
  91. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/error.lottie +0 -0
  92. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/error_alt.lottie +0 -0
  93. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/home-card-1.png +0 -0
  94. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/home-card-2.png +0 -0
  95. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/home-card-3.png +0 -0
  96. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/home-card-4.png +0 -0
  97. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/home-card-5.png +0 -0
  98. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/image-not-found.png +0 -0
  99. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/pending.lottie +0 -0
  100. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/processing.lottie +0 -0
  101. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/ruo.png +0 -0
  102. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_gui/assets/ruo.svg +0 -0
  103. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_models.py +0 -0
  104. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/application/_settings.py +0 -0
  105. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/bucket/CLAUDE.md +0 -0
  106. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/bucket/__init__.py +0 -0
  107. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/bucket/_cli.py +0 -0
  108. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/bucket/_gui.py +0 -0
  109. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/bucket/_service.py +0 -0
  110. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/bucket/_settings.py +0 -0
  111. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/bucket/assets/Google-Cloud-logo.png +0 -0
  112. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/bucket/assets/Google-Cloud-logo.svg +0 -0
  113. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/cli.py +0 -0
  114. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/constants.py +0 -0
  115. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/dataset/CLAUDE.md +0 -0
  116. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/dataset/__init__.py +0 -0
  117. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/dataset/_cli.py +0 -0
  118. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/dataset/_gui.py +0 -0
  119. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/dataset/_service.py +0 -0
  120. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/dataset/assets/NIH-IDC-logo.svg +0 -0
  121. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/gui/CLAUDE.md +0 -0
  122. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/gui/__init__.py +0 -0
  123. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/gui/_error.py +0 -0
  124. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/gui/_frame.py +0 -0
  125. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/gui/assets/cabin-v27-latin-regular.woff2 +0 -0
  126. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/gui/assets/cat.lottie +0 -0
  127. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/gui/assets/logo.png +0 -0
  128. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/notebook/CLAUDE.md +0 -0
  129. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/notebook/__init__.py +0 -0
  130. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/notebook/_gui.py +0 -0
  131. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/notebook/_notebook.py +0 -0
  132. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/notebook/_service.py +0 -0
  133. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/notebook/assets/python.lottie +0 -0
  134. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/CLAUDE.md +0 -0
  135. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_authentication.py +0 -0
  136. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_cli.py +0 -0
  137. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_constants.py +0 -0
  138. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_messages.py +0 -0
  139. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_operation_cache.py +0 -0
  140. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_service.py +0 -0
  141. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_settings.py +0 -0
  142. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/_utils.py +0 -0
  143. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/resources/__init__.py +0 -0
  144. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/resources/applications.py +0 -0
  145. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/platform/resources/utils.py +0 -0
  146. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/CLAUDE.md +0 -0
  147. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/__init__.py +0 -0
  148. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/_cli.py +0 -0
  149. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/_gui.py +0 -0
  150. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/_service.py +0 -0
  151. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/_settings.py +0 -0
  152. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/assets/download.lottie +0 -0
  153. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/assets/microscope.lottie +0 -0
  154. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/assets/update.lottie +0 -0
  155. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/scripts/add.groovy +0 -0
  156. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/scripts/annotate.groovy +0 -0
  157. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/scripts/inspect.groovy +0 -0
  158. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/qupath/scripts/test.groovy +0 -0
  159. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/system/CLAUDE.md +0 -0
  160. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/system/__init__.py +0 -0
  161. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/system/_cli.py +0 -0
  162. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/system/_exceptions.py +0 -0
  163. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/system/_gui.py +0 -0
  164. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/system/_service.py +0 -0
  165. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/system/_settings.py +0 -0
  166. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/system/assets/system.lottie +0 -0
  167. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/bottle.py +0 -0
  168. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/idc_index.py +0 -0
  169. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/__init__.py +0 -0
  170. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/argumentsparse.py +0 -0
  171. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/constants.py +0 -0
  172. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/py.typed +0 -0
  173. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/showinfm.py +0 -0
  174. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/system/__init__.py +0 -0
  175. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/system/linux.py +0 -0
  176. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/system/tools.py +0 -0
  177. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/system/urivalidate.py +0 -0
  178. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/third_party/showinfm/system/windows.py +0 -0
  179. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/CLAUDE.md +0 -0
  180. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/__init__.py +0 -0
  181. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_cli.py +0 -0
  182. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_console.py +0 -0
  183. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_di.py +0 -0
  184. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_fs.py +0 -0
  185. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_gui.py +0 -0
  186. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_health.py +0 -0
  187. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_log.py +0 -0
  188. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_notebook.py +0 -0
  189. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_process.py +0 -0
  190. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_sentry.py +0 -0
  191. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_service.py +0 -0
  192. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_settings.py +0 -0
  193. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/utils/_user_agent.py +0 -0
  194. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/wsi/CLAUDE.md +0 -0
  195. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/wsi/__init__.py +0 -0
  196. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/wsi/_cli.py +0 -0
  197. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/wsi/_gui.py +0 -0
  198. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/wsi/_openslide_handler.py +0 -0
  199. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/wsi/_pydicom_handler.py +0 -0
  200. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/wsi/_service.py +0 -0
  201. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/wsi/_utils.py +0 -0
  202. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics/wsi/assets/fallback.png +0 -0
  203. {aignostics-0.2.201 → aignostics-0.2.203}/src/aignostics.py +0 -0
@@ -88,6 +88,8 @@ profile.html
88
88
  # Nicegui
89
89
  .nicegui
90
90
 
91
+ # MCP
92
+ .mcp.json
91
93
 
92
94
  # Application specific
93
95
  data/**
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aignostics
3
- Version: 0.2.201
3
+ Version: 0.2.203
4
4
  Summary: 🔬 Python SDK providing access to the Aignostics Platform. Includes Aignostics Launchpad (Desktop Application), Aignostics CLI (Command-Line Interface), example notebooks, and Aignostics Client Library.
5
5
  Project-URL: Homepage, https://aignostics.readthedocs.io/en/latest/
6
6
  Project-URL: Documentation, https://aignostics.readthedocs.io/en/latest/
@@ -49,21 +49,21 @@ Classifier: Programming Language :: Python :: 3.13
49
49
  Classifier: Typing :: Typed
50
50
  Requires-Python: <3.14,>=3.11
51
51
  Requires-Dist: aiopath<1,>=0.6.11
52
- Requires-Dist: boto3<2,>=1.40.61
52
+ Requires-Dist: boto3<2,>=1.41.0
53
53
  Requires-Dist: certifi<2026,>=2025.11.12
54
54
  Requires-Dist: defusedxml>=0.7.1
55
55
  Requires-Dist: dicom-validator<1,>=0.7.3
56
56
  Requires-Dist: dicomweb-client[gcp]<1,>=0.59.3
57
57
  Requires-Dist: duckdb<=1.4.1,>=0.10.0
58
- Requires-Dist: fastapi[all,standard]<1,>=0.121.1
58
+ Requires-Dist: fastapi[all,standard]<1,>=0.121.3
59
59
  Requires-Dist: fastparquet<2025,>=2024.11.0
60
- Requires-Dist: google-cloud-storage<4,>=3.5.0
60
+ Requires-Dist: google-cloud-storage<4,>=3.6.0
61
61
  Requires-Dist: google-crc32c<2,>=1.7.1
62
62
  Requires-Dist: highdicom<1,>=0.26.1
63
63
  Requires-Dist: html-sanitizer<3,>=2.6.0
64
64
  Requires-Dist: httpx<1,>=0.28.1
65
65
  Requires-Dist: humanize<5,>=4.14.0
66
- Requires-Dist: idc-index-data==22.1.2
66
+ Requires-Dist: idc-index-data==22.1.5
67
67
  Requires-Dist: ijson<4,>=3.4.0.post0
68
68
  Requires-Dist: jsf<1,>=0.11.2
69
69
  Requires-Dist: jsonschema[format-nongpl]<5,>=4.25.1
@@ -86,7 +86,7 @@ Requires-Dist: requests-oauthlib<3,>=2.0.0
86
86
  Requires-Dist: requests<3,>=2.32.5
87
87
  Requires-Dist: s5cmd<1,>=0.3.3
88
88
  Requires-Dist: semver<4,>=3.0.4
89
- Requires-Dist: sentry-sdk<3,>=2.44.0
89
+ Requires-Dist: sentry-sdk<3,>=2.45.0
90
90
  Requires-Dist: shapely<3,>=2.1.2
91
91
  Requires-Dist: tenacity<10,>=9.1.2
92
92
  Requires-Dist: tqdm<5,>=4.67.1
@@ -100,7 +100,7 @@ Requires-Dist: jupyter<2,>=1.1.1; extra == 'jupyter'
100
100
  Provides-Extra: marimo
101
101
  Requires-Dist: cloudpathlib<1,>=0.23.0; extra == 'marimo'
102
102
  Requires-Dist: ipython<10,>=9.7.0; extra == 'marimo'
103
- Requires-Dist: marimo<1,>=0.17.7; extra == 'marimo'
103
+ Requires-Dist: marimo<1,>=0.17.8; extra == 'marimo'
104
104
  Requires-Dist: matplotlib<4,>=3.10.7; extra == 'marimo'
105
105
  Requires-Dist: shapely<3,>=2.1.0; extra == 'marimo'
106
106
  Provides-Extra: pyinstaller
@@ -2,7 +2,7 @@
2
2
  # requires-python = ">=3.13"
3
3
  # dependencies = [
4
4
  # "marimo",
5
- # "aignostics==0.2.201",
5
+ # "aignostics==0.2.203",
6
6
  # ]
7
7
  # ///
8
8
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aignostics"
3
- version = "0.2.201"
3
+ version = "0.2.203"
4
4
  description = "🔬 Python SDK providing access to the Aignostics Platform. Includes Aignostics Launchpad (Desktop Application), Aignostics CLI (Command-Line Interface), example notebooks, and Aignostics Client Library."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -74,7 +74,7 @@ requires-python = ">=3.11, <3.14"
74
74
 
75
75
  dependencies = [
76
76
  # From Template
77
- "fastapi[standard,all]>=0.121.1,<1",
77
+ "fastapi[standard,all]>=0.121.3,<1",
78
78
  "humanize>=4.14.0,<5",
79
79
  "nicegui[native]>=3.1.0,<3.2.0", # Regression in 3.2.0
80
80
  "packaging>=25.0,<26",
@@ -83,24 +83,24 @@ dependencies = [
83
83
  "pydantic-settings>=2.12.0,<3",
84
84
  "pywin32>=310,<311 ; sys_platform == 'win32'",
85
85
  "pyyaml>=6.0.3,<7",
86
- "sentry-sdk>=2.44.0,<3",
86
+ "sentry-sdk>=2.45.0,<3",
87
87
  "typer>=0.20.0,<1",
88
88
  "uptime>=3.0.1,<4",
89
89
  # Custom
90
90
  "aiopath>=0.6.11,<1",
91
- "boto3>=1.40.61,<2",
91
+ "boto3>=1.41.0,<2",
92
92
  "certifi>=2025.11.12,<2026",
93
93
  "defusedxml>=0.7.1",
94
94
  "dicom-validator>=0.7.3,<1",
95
95
  "dicomweb-client[gcp]>=0.59.3,<1",
96
96
  "duckdb>=0.10.0,<=1.4.1",
97
97
  "fastparquet>=2024.11.0,<2025",
98
- "google-cloud-storage>=3.5.0,<4",
98
+ "google-cloud-storage>=3.6.0,<4",
99
99
  "google-crc32c>=1.7.1,<2",
100
100
  "highdicom>=0.26.1,<1",
101
101
  "html-sanitizer>=2.6.0,<3",
102
102
  "httpx>=0.28.1,<1",
103
- "idc-index-data==22.1.2",
103
+ "idc-index-data==22.1.5",
104
104
  "ijson>=3.4.0.post0,<4",
105
105
  "jsf>=0.11.2,<1",
106
106
  "jsonschema[format-nongpl]>=4.25.1,<5",
@@ -131,7 +131,7 @@ jupyter = ["jupyter>=1.1.1,<2"]
131
131
  marimo = [
132
132
  "cloudpathlib>=0.23.0,<1",
133
133
  "ipython>=9.7.0,<10",
134
- "marimo>=0.17.7,<1",
134
+ "marimo>=0.17.8,<1",
135
135
  "matplotlib>=3.10.7,<4",
136
136
  "shapely>=2.1.0,<3",
137
137
  ]
@@ -165,7 +165,7 @@ dev = [
165
165
  "pytest-timeout>=2.4.0,<3",
166
166
  "pytest-watcher>=0.4.3,<1",
167
167
  "pytest-xdist[psutil]>=3.8.0,<4",
168
- "ruff>=0.14.4,<1",
168
+ "ruff>=0.14.5,<1",
169
169
  "scalene>=1.5.55,<2",
170
170
  "sphinx>=8.2.3,<9",
171
171
  "sphinx-autobuild>=2025.8.25,<2026",
@@ -175,9 +175,9 @@ dev = [
175
175
  "sphinx-mdinclude>=0.6.2,<1",
176
176
  "sphinx-rtd-theme>=3.0.2,<4",
177
177
  "sphinx_selective_exclude>=1.0.3,<2",
178
- "sphinx-toolbox>=3.9.0,<4",
178
+ "sphinx-toolbox>=4.0.0,<5",
179
179
  "sphinxext.opengraph>=0.9.1,<1",
180
- "swagger-plugin-for-sphinx>=5.2.0,<6",
180
+ "swagger-plugin-for-sphinx>=6.0.0,<7",
181
181
  "tomli>=2.3.0,<3",
182
182
  "types-pyyaml>=6.0.12.20250915,<7",
183
183
  "types-requests>=2.32.4.20250913,<3",
@@ -372,7 +372,7 @@ source = ["src/"]
372
372
 
373
373
 
374
374
  [tool.bumpversion]
375
- current_version = "0.2.201"
375
+ current_version = "0.2.203"
376
376
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
377
377
  serialize = ["{major}.{minor}.{patch}"]
378
378
  search = "{current_version}"
@@ -366,6 +366,22 @@ def run_execute( # noqa: PLR0913, PLR0917
366
366
  validate_only: Annotated[
367
367
  bool, typer.Option(help="If True, cancel the run post validation, before analysis.")
368
368
  ] = False,
369
+ gpu_type: Annotated[
370
+ str,
371
+ typer.Option(help="GPU type to use for processing (L4 or A100)."),
372
+ ] = "A100",
373
+ gpu_provisioning_mode: Annotated[
374
+ str,
375
+ typer.Option(help="GPU provisioning mode (SPOT or ON_DEMAND)."),
376
+ ] = "ON_DEMAND",
377
+ max_gpus_per_slide: Annotated[
378
+ int,
379
+ typer.Option(help="Maximum number of GPUs to allocate per slide (1-8).", min=1, max=8),
380
+ ] = 1,
381
+ cpu_provisioning_mode: Annotated[
382
+ str,
383
+ typer.Option(help="CPU provisioning mode (SPOT or ON_DEMAND)."),
384
+ ] = "ON_DEMAND",
369
385
  ) -> None:
370
386
  """Prepare metadata, upload data to platform, and submit an application run, then incrementally download results.
371
387
 
@@ -401,10 +417,15 @@ def run_execute( # noqa: PLR0913, PLR0917
401
417
  metadata_csv_file=metadata_csv_file,
402
418
  application_version=application_version,
403
419
  note=note,
420
+ tags=None,
404
421
  due_date=due_date,
405
422
  deadline=deadline,
406
423
  onboard_to_aignostics_portal=onboard_to_aignostics_portal,
407
424
  validate_only=validate_only,
425
+ gpu_type=gpu_type,
426
+ gpu_provisioning_mode=gpu_provisioning_mode,
427
+ max_gpus_per_slide=max_gpus_per_slide,
428
+ cpu_provisioning_mode=cpu_provisioning_mode,
408
429
  )
409
430
  result_download(
410
431
  run_id=run_id,
@@ -652,6 +673,22 @@ def run_submit( # noqa: PLR0913, PLR0917
652
673
  validate_only: Annotated[
653
674
  bool, typer.Option(help="If True, cancel the run post validation, before analysis.")
654
675
  ] = False,
676
+ gpu_type: Annotated[
677
+ str,
678
+ typer.Option(help="GPU type to use for processing (L4 or A100)."),
679
+ ] = "A100",
680
+ gpu_provisioning_mode: Annotated[
681
+ str,
682
+ typer.Option(help="GPU provisioning mode (SPOT or ON_DEMAND)."),
683
+ ] = "ON_DEMAND",
684
+ max_gpus_per_slide: Annotated[
685
+ int,
686
+ typer.Option(help="Maximum number of GPUs to allocate per slide (1-8).", min=1, max=8),
687
+ ] = 1,
688
+ cpu_provisioning_mode: Annotated[
689
+ str,
690
+ typer.Option(help="CPU provisioning mode (SPOT or ON_DEMAND)."),
691
+ ] = "ON_DEMAND",
655
692
  ) -> str:
656
693
  """Submit run by referencing the metadata CSV file.
657
694
 
@@ -701,11 +738,26 @@ def run_submit( # noqa: PLR0913, PLR0917
701
738
  app_version.version_number,
702
739
  metadata_dict,
703
740
  )
741
+
742
+ # Build custom metadata with pipeline configuration
743
+ custom_metadata = {
744
+ "pipeline": {
745
+ "gpu": {
746
+ "gpu_type": gpu_type,
747
+ "provisioning_mode": gpu_provisioning_mode,
748
+ "max_gpus_per_slide": max_gpus_per_slide,
749
+ },
750
+ "cpu": {
751
+ "provisioning_mode": cpu_provisioning_mode,
752
+ },
753
+ },
754
+ }
755
+
704
756
  application_run = Service().application_run_submit_from_metadata(
705
757
  application_id=application_id,
706
758
  metadata=metadata_dict,
707
759
  application_version=application_version,
708
- custom_metadata=None, # TODO(Helmut): Add support for custom metadata
760
+ custom_metadata=custom_metadata,
709
761
  note=note,
710
762
  tags={tag.strip() for tag in tags.split(",") if tag.strip()} if tags else None,
711
763
  due_date=due_date,
@@ -11,7 +11,7 @@ from .._service import Service # noqa: TID252
11
11
  from ._utils import application_id_to_icon, run_status_to_icon_and_color
12
12
 
13
13
  BORDERED_SEPARATOR = "bordered separator"
14
- RUNS_LIMIT = 100
14
+ RUNS_LIMIT = 500
15
15
  RUNS_REFRESH_INTERVAL = 60 * 15 # 15 minutes
16
16
  STORAGE_TAB_RUNS_HAS_OUTPUT = "runs_has_output"
17
17
 
@@ -59,7 +59,7 @@ async def _frame( # noqa: C901, PLR0913, PLR0915, PLR0917
59
59
  if applications is None:
60
60
  message = ( # type: ignore[unreachable]
61
61
  "nicegui_run.io_bound(Service.applications_static) returned None, "
62
- "likely canceled by appliction shutdown."
62
+ "likely canceled by application shutdown."
63
63
  )
64
64
  logger.error(message)
65
65
  raise RuntimeError(message) # noqa: TRY301
@@ -157,6 +157,7 @@ async def _frame( # noqa: C901, PLR0913, PLR0915, PLR0917
157
157
  run_data["termination_reason"],
158
158
  run_data["item_count"],
159
159
  run_data["item_succeeded_count"],
160
+ run_data["is_not_terminated_with_deadline_exceeded"],
160
161
  )
161
162
  with (
162
163
  ui.circular_progress(
@@ -28,6 +28,9 @@ from ._utils import (
28
28
  WIDTH_1200px = "width: 1200px; max-width: none"
29
29
  MESSAGE_METADATA_GRID_IS_NOT_INITIALIZED = "Metadata grid is not initialized."
30
30
 
31
+ CLASS_SUBSECTION_HEADER = "text-h6 mb-0 pb-0"
32
+ CLASS_WIDTH_ONE_THIRD = "w-1/3"
33
+
31
34
 
32
35
  @binding.bindable_dataclass
33
36
  class SubmitForm:
@@ -50,6 +53,10 @@ class SubmitForm:
50
53
  deadline: str = (datetime.now().astimezone() + timedelta(hours=24)).strftime("%Y-%m-%d %H:%M")
51
54
  validate_only: bool = False
52
55
  onboard_to_aignostics_portal: bool = False
56
+ gpu_type: str = "A100"
57
+ gpu_provisioning_mode: str = "ON_DEMAND"
58
+ max_gpus_per_slide: int = 1
59
+ cpu_provisioning_mode: str = "ON_DEMAND"
53
60
 
54
61
 
55
62
  submit_form = SubmitForm()
@@ -619,7 +626,7 @@ async def _page_application_describe(application_id: str) -> None: # noqa: C901
619
626
  today = now.strftime("%Y/%m/%d")
620
627
  min_hour = (now + timedelta(hours=1)).hour
621
628
  min_minute = (now + timedelta(hours=1)).minute
622
- ui.label("Soft Due Date").classes("text-h6 mb-0 pb-0")
629
+ ui.label("Soft Due Date").classes("class_subsection_header")
623
630
  ui.label(
624
631
  "The platform will try to complete the run before this time, "
625
632
  "given your subscription tier and available GPU resources."
@@ -672,7 +679,7 @@ async def _page_application_describe(application_id: str) -> None: # noqa: C901
672
679
  }}
673
680
  """
674
681
  )
675
- ui.label("Hard Deadline").classes("text-h6 mb-0 pb-0")
682
+ ui.label("Hard Deadline").classes("class_subsection_header")
676
683
  ui.label("The platform might cancel the run if not completed by this time.").classes(
677
684
  "text-sm mt-0 pt-0"
678
685
  )
@@ -702,11 +709,25 @@ async def _page_application_describe(application_id: str) -> None: # noqa: C901
702
709
  """Submit the application run."""
703
710
  ui.notify("Submitting application run ...", type="info")
704
711
  try:
712
+ # Build custom metadata with pipeline configuration
713
+ custom_metadata = {
714
+ "pipeline": {
715
+ "gpu": {
716
+ "gpu_type": submit_form.gpu_type,
717
+ "provisioning_mode": submit_form.gpu_provisioning_mode,
718
+ "max_gpus_per_slide": submit_form.max_gpus_per_slide,
719
+ },
720
+ "cpu": {
721
+ "provisioning_mode": submit_form.cpu_provisioning_mode,
722
+ },
723
+ },
724
+ }
725
+
705
726
  run = service.application_run_submit_from_metadata(
706
727
  application_id=str(submit_form.application_id),
707
728
  metadata=submit_form.metadata or [],
708
729
  application_version=str(submit_form.application_version),
709
- custom_metadata=None, # TODO(Helmut): Allow user to edit custom metadata
730
+ custom_metadata=custom_metadata,
710
731
  note=submit_form.note,
711
732
  tags=set(submit_form.tags) if submit_form.tags else None,
712
733
  due_date=datetime.strptime(submit_form.due_date, "%Y-%m-%d %H:%M")
@@ -816,6 +837,80 @@ async def _page_application_describe(application_id: str) -> None: # noqa: C901
816
837
  break
817
838
  _upload_ui.refresh(submit_form.metadata)
818
839
 
840
+ with ui.step("Pipeline"):
841
+ user_info: UserInfo | None = app.storage.tab.get("user_info", None)
842
+ can_configure_pipeline = (
843
+ user_info
844
+ and user_info.organization
845
+ and user_info.organization.name
846
+ and user_info.organization.name.lower() in {"aignostics", "pre-alpha-org", "lmu", "charite"}
847
+ )
848
+
849
+ if can_configure_pipeline:
850
+ with ui.column(align_items="start").classes("w-full"):
851
+ ui.label("GPU Configuration").classes("class_subsection_header")
852
+ ui.label(
853
+ "Configure GPU resources for processing your whole slide images. "
854
+ "These settings control the type and provisioning mode of GPUs used during AI analysis."
855
+ ).classes("text-sm mt-0 pt-0 mb-4")
856
+
857
+ with ui.row().classes("w-full gap-4"):
858
+ ui.select(
859
+ label="GPU Type",
860
+ options={"L4": "L4", "A100": "A100"},
861
+ value=submit_form.gpu_type,
862
+ ).bind_value(submit_form, "gpu_type").mark("SELECT_GPU_TYPE").classes(CLASS_WIDTH_ONE_THIRD)
863
+
864
+ ui.number(
865
+ label="Max GPUs per Slide",
866
+ value=submit_form.max_gpus_per_slide,
867
+ min=1,
868
+ max=8,
869
+ step=1,
870
+ ).bind_value(submit_form, "max_gpus_per_slide").mark("NUMBER_MAX_GPUS_PER_SLIDE").classes(
871
+ CLASS_WIDTH_ONE_THIRD
872
+ )
873
+
874
+ ui.select(
875
+ label="GPU Provisioning Mode",
876
+ options={
877
+ "SPOT": "Spot nodes (lower cost, better availability, might be preempted and retried)",
878
+ "ON_DEMAND": (
879
+ "On demand nodes (higher cost, limited availability, processing might be delayed)"
880
+ ),
881
+ },
882
+ value=submit_form.gpu_provisioning_mode,
883
+ ).bind_value(submit_form, "gpu_provisioning_mode").mark("SELECT_GPU_PROVISIONING_MODE").classes(
884
+ CLASS_WIDTH_ONE_THIRD
885
+ )
886
+
887
+ ui.separator().classes("my-4")
888
+
889
+ ui.label("CPU Configuration").classes("class_subsection_header")
890
+ ui.label("Configure CPU resources for algorithms that do not require GPU acceleration.").classes(
891
+ "text-sm mt-0 pt-0 mb-4"
892
+ )
893
+
894
+ with ui.row().classes("w-full gap-4"):
895
+ ui.select(
896
+ label="CPU Provisioning Mode",
897
+ options={
898
+ "SPOT": "Spot nodes (lower cost, better availability, might be preempted and retried)",
899
+ "ON_DEMAND": "On demand nodes (higher cost, limited availability, might be delayed)",
900
+ },
901
+ value=submit_form.cpu_provisioning_mode,
902
+ ).bind_value(submit_form, "cpu_provisioning_mode").mark("SELECT_CPU_PROVISIONING_MODE").classes(
903
+ "w-1/2"
904
+ )
905
+ else:
906
+ ui.label(
907
+ "Pipeline configuration is not available for your organization. Default settings will be used."
908
+ ).classes("text-body1")
909
+
910
+ with ui.stepper_navigation():
911
+ ui.button("Next", on_click=stepper.next).mark("BUTTON_PIPELINE_NEXT")
912
+ ui.button("Back", on_click=stepper.previous).props("flat")
913
+
819
914
  with ui.step("Submit"):
820
915
  _upload_ui([])
821
916
  ui.timer(0.1, callback=_update_upload_progress)
@@ -1,5 +1,7 @@
1
1
  """Utility functions for the application GUI."""
2
2
 
3
+ from tkinter import NONE
4
+
3
5
  from aignostics.platform import ItemState, ItemTerminationReason, RunState, RunTerminationReason
4
6
 
5
7
 
@@ -21,7 +23,11 @@ def application_id_to_icon(application_id: str) -> str:
21
23
 
22
24
 
23
25
  def run_status_to_icon_and_color(
24
- run_status: str, termination_reason: str | None, item_count: int, item_succeeded_count: int
26
+ run_status: str,
27
+ termination_reason: str | None,
28
+ item_count: int,
29
+ item_succeeded_count: int,
30
+ is_not_terminated_with_deadline_exceeded: bool = False,
25
31
  ) -> tuple[str, str]:
26
32
  """Convert run status and termination reason to icon and color.
27
33
 
@@ -30,30 +36,30 @@ def run_status_to_icon_and_color(
30
36
  termination_reason (str): The termination reason.
31
37
  item_count (int): The total number of items in the run.
32
38
  item_succeeded_count (int): The number of items that succeeded in the run.
39
+ is_not_terminated_with_deadline_exceeded (bool): Whether the run is not terminated with deadline exceeded.
33
40
 
34
41
  Returns:
35
42
  tuple[str, str]: The icon name and color.
36
43
  """
44
+ if is_not_terminated_with_deadline_exceeded: # This should never happen
45
+ return "alarm_off", "orange"
37
46
  match run_status:
38
47
  case RunState.PENDING:
39
- return "schedule", "info"
48
+ return "schedule", "secondary"
40
49
  case RunState.PROCESSING:
41
50
  return "directions_run", "info"
42
51
  case RunState.TERMINATED:
43
52
  icon = "bug_report"
53
+ color = NONE
44
54
  if termination_reason == RunTerminationReason.CANCELED_BY_USER:
45
55
  icon = "cancel"
56
+ color = "warning"
46
57
  if termination_reason == RunTerminationReason.CANCELED_BY_SYSTEM:
47
58
  icon = "error"
59
+ color = "error"
48
60
  if termination_reason == RunTerminationReason.ALL_ITEMS_PROCESSED:
49
61
  icon = "sports_score"
50
- color = "negative"
51
- if item_succeeded_count <= 0:
52
- color = "negative"
53
- elif item_succeeded_count < item_count:
54
- color = "warning"
55
- elif item_succeeded_count == item_count:
56
- color = "positive"
62
+ color = "success" if item_succeeded_count == item_count else "error"
57
63
  return (icon, color)
58
64
  return "bug_report", "negative"
59
65
 
@@ -72,19 +78,19 @@ def run_item_status_and_termination_reason_to_icon_and_color( # noqa: PLR0911
72
78
  """
73
79
  match item_status:
74
80
  case ItemState.PENDING:
75
- return "schedule", "info"
81
+ return "schedule", "secondary"
76
82
  case ItemState.PROCESSING:
77
83
  return "directions_run", "info"
78
84
  case ItemState.TERMINATED:
79
85
  if termination_reason == ItemTerminationReason.SKIPPED:
80
86
  return "next_plan", "warning"
81
87
  if termination_reason == ItemTerminationReason.SUCCEEDED:
82
- return "check_circle", "positive"
88
+ return "check_circle", "success"
83
89
  if termination_reason == ItemTerminationReason.SYSTEM_ERROR:
84
- return "error", "negative"
90
+ return "error", "error"
85
91
  if termination_reason == ItemTerminationReason.USER_ERROR:
86
- return "warning", "negative"
87
- return "bug_report", "negative"
92
+ return "warning", "warning"
93
+ return "bug_report", "error"
88
94
 
89
95
 
90
96
  def mime_type_to_icon(mime_type: str) -> str:
@@ -49,6 +49,7 @@ from ._settings import Settings
49
49
  from ._utils import (
50
50
  get_mime_type_for_artifact,
51
51
  get_supported_extensions_for_application,
52
+ is_not_terminated_with_deadline_exceeded,
52
53
  validate_due_date,
53
54
  )
54
55
 
@@ -571,6 +572,9 @@ class Service(BaseService): # noqa: PLR0904
571
572
  "item_count": run.statistics.item_count,
572
573
  "item_succeeded_count": run.statistics.item_succeeded_count,
573
574
  "tags": run.custom_metadata.get("sdk", {}).get("tags", []) if run.custom_metadata else [],
575
+ "is_not_terminated_with_deadline_exceeded": is_not_terminated_with_deadline_exceeded(
576
+ run.state, run.custom_metadata
577
+ ),
574
578
  }
575
579
  for run in Service().application_runs(
576
580
  application_id=application_id,
@@ -916,7 +920,7 @@ class Service(BaseService): # noqa: PLR0904
916
920
  validate_only=validate_only,
917
921
  )
918
922
  logger.debug(
919
- "Submitted application run with items: %s, application run id %s, custom metadata: %s",
923
+ "Submitted application run with items: {}, application run id {}, custom metadata: {}",
920
924
  items,
921
925
  run.run_id,
922
926
  custom_metadata,
@@ -83,6 +83,44 @@ def validate_due_date(due_date: str | None) -> None:
83
83
  raise ValueError(message)
84
84
 
85
85
 
86
+ def is_not_terminated_with_deadline_exceeded(
87
+ run_state: RunState,
88
+ custom_metadata: dict[str, Any] | None,
89
+ ) -> bool | None:
90
+ """Check if the run is not terminated and the deadline has been exceeded.
91
+
92
+ Only returns True if the run is still in PENDING or PROCESSING state and the deadline has passed.
93
+ This is useful for identifying runs that are overdue and still active.
94
+
95
+ Args:
96
+ run_state (RunState): The current state of the run.
97
+ custom_metadata (dict[str, Any] | None): The custom metadata containing optional deadline information.
98
+
99
+ Returns:
100
+ bool | None: True if run is not terminated and deadline exceeded,
101
+ False if run is not terminated but deadline not exceeded,
102
+ None if run is terminated, no deadline set, or invalid deadline format.
103
+ """
104
+ # If run is already terminated, return None (deadline is no longer relevant)
105
+ if run_state == RunState.TERMINATED:
106
+ return None
107
+
108
+ if not custom_metadata:
109
+ return None
110
+
111
+ deadline_str = custom_metadata.get("sdk", {}).get("scheduling", {}).get("deadline")
112
+ if not deadline_str:
113
+ return None
114
+
115
+ try:
116
+ now = datetime.now(tz=UTC)
117
+ deadline_dt = datetime.fromisoformat(deadline_str)
118
+ return now > deadline_dt
119
+ except (ValueError, TypeError, AttributeError):
120
+ # Invalid deadline format, return None
121
+ return None
122
+
123
+
86
124
  class OutputFormat(StrEnum):
87
125
  """
88
126
  Enum representing the supported output formats.
@@ -24,9 +24,11 @@ def theme() -> None:
24
24
  dark="#1d1d1d1d",
25
25
  dark_page="#12121212",
26
26
  positive="#0CA57B",
27
+ success="#0CA57B",
27
28
  negative="#D4313C",
28
29
  info="#261C8D",
29
30
  warning="#FFCC00",
31
+ error="#D4313C",
30
32
  brand_white="#EFF0F1",
31
33
  brand_background_light="#E7E6E8",
32
34
  brand_logo="#AFA3DD",
@@ -61,6 +61,7 @@ from ._constants import (
61
61
  TOKEN_URL_STAGING,
62
62
  )
63
63
  from ._messages import AUTHENTICATION_FAILED, NOT_YET_IMPLEMENTED, UNKNOWN_ENDPOINT_URL
64
+ from ._sdk_metadata import RunSdkMetadata
64
65
  from ._service import Service, TokenInfo, UserInfo
65
66
  from ._settings import Settings, settings
66
67
  from ._utils import (
@@ -124,6 +125,7 @@ __all__ = [
124
125
  "RunData",
125
126
  "RunItemStatistics",
126
127
  "RunOutput",
128
+ "RunSdkMetadata",
127
129
  "RunState",
128
130
  "RunState",
129
131
  "RunTerminationReason",
@@ -301,7 +301,6 @@ class Client:
301
301
  client = ApiClient(
302
302
  config,
303
303
  )
304
- # TODO(Helmut): move to request calling
305
304
  client.user_agent = user_agent()
306
305
  api_client = PublicApi(client)
307
306