diffusers-workflow 0.8.0__tar.gz → 0.9.0__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 (530) hide show
  1. {diffusers_workflow-0.8.0/diffusers_workflow.egg-info → diffusers_workflow-0.9.0}/PKG-INFO +1 -1
  2. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0/diffusers_workflow.egg-info}/PKG-INFO +1 -1
  3. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/MCP.md +1 -1
  4. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/RELEASING.md +60 -0
  5. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/REMOTE.md +5 -0
  6. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/plan.py +67 -13
  7. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/routes/jobs.py +7 -5
  8. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/joins.py +10 -0
  9. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/exports.py +15 -12
  10. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/tools_jobs.py +10 -11
  11. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/pyproject.toml +1 -1
  12. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_assemble_and_score_target_lufs.py +12 -0
  13. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_concat_videos.py +17 -0
  14. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_exports.py +11 -4
  15. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_server.py +8 -7
  16. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_plan.py +81 -4
  17. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_plugin_skills.py +35 -2
  18. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_exports.py +15 -6
  19. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/LICENSE +0 -0
  20. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/MANIFEST.in +0 -0
  21. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/README.md +0 -0
  22. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/diffusers_workflow.egg-info/SOURCES.txt +0 -0
  23. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/diffusers_workflow.egg-info/dependency_links.txt +0 -0
  24. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/diffusers_workflow.egg-info/entry_points.txt +0 -0
  25. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/diffusers_workflow.egg-info/requires.txt +0 -0
  26. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/diffusers_workflow.egg-info/top_level.txt +0 -0
  27. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/__init__.py +0 -0
  28. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/adapter_compatibility.py +0 -0
  29. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/argument_media.py +0 -0
  30. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/argument_warnings.py +0 -0
  31. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/arguments.py +0 -0
  32. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/assessment_rules.py +0 -0
  33. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/assets.py +0 -0
  34. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/audio_qc.py +0 -0
  35. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/cache_blocks.json +0 -0
  36. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/cache_blocks.py +0 -0
  37. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/community_pipelines/pipeline_flux_rf_inversion.py +0 -0
  38. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/content_types.py +0 -0
  39. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/dissolve_frame_errors.py +0 -0
  40. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/ACCELERATION.md +0 -0
  41. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/AGENT_LOOP.md +0 -0
  42. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/ARCHITECTURE.md +0 -0
  43. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/DEPENDENCIES.md +0 -0
  44. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/IP_ADAPTER.md +0 -0
  45. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/LORAS.md +0 -0
  46. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/PROMPT_WEIGHTING.md +0 -0
  47. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/QUANTIZATION.md +0 -0
  48. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/RECIPES_24GB.md +0 -0
  49. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/SECURITY.md +0 -0
  50. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/SECURITY_QUICKREF.md +0 -0
  51. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/SERVER.md +0 -0
  52. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/TASKS.md +0 -0
  53. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/TESTING.md +0 -0
  54. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/WORKER_GUIDE.md +0 -0
  55. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/WORKFLOW_GUIDE.md +0 -0
  56. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/docs/WORKSPACES.md +0 -0
  57. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/download_watch.py +0 -0
  58. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/dsp.py +0 -0
  59. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/elision.py +0 -0
  60. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/events.py +0 -0
  61. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/for_each.py +0 -0
  62. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/host_memory.py +0 -0
  63. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/host_memory_projection.py +0 -0
  64. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/hub_cache.py +0 -0
  65. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/introspection.py +0 -0
  66. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/kernel_availability.py +0 -0
  67. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/library.py +0 -0
  68. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/locations.py +0 -0
  69. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/log_setup.py +0 -0
  70. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/lora_catalog.py +0 -0
  71. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/lora_catalog_schema.json +0 -0
  72. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/lora_hub.py +0 -0
  73. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/media.py +0 -0
  74. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/media_frames.py +0 -0
  75. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/media_types.py +0 -0
  76. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/output_extraction.py +0 -0
  77. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/path_redaction.py +0 -0
  78. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/pipeline_ownership.py +0 -0
  79. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/pipeline_processors/adapters.py +0 -0
  80. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/pipeline_processors/chain.py +0 -0
  81. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/pipeline_processors/components.py +0 -0
  82. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/pipeline_processors/config_objects.py +0 -0
  83. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/pipeline_processors/pipeline.py +0 -0
  84. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/pipeline_processors/placement.py +0 -0
  85. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/pipeline_processors/progress.py +0 -0
  86. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/pipeline_processors/remote.py +0 -0
  87. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/previous_results.py +0 -0
  88. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/probe_paths.py +0 -0
  89. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/prompt_schema.json +0 -0
  90. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/prompt_weighting.py +0 -0
  91. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/prompts.py +0 -0
  92. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/realize.py +0 -0
  93. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/reference_limits.py +0 -0
  94. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/reference_names.py +0 -0
  95. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/references.py +0 -0
  96. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/result.py +0 -0
  97. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/run.py +0 -0
  98. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/runs.py +0 -0
  99. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/scalar_result_validation.py +0 -0
  100. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/schema.py +0 -0
  101. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/security.py +0 -0
  102. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/serve.py +0 -0
  103. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/__init__.py +0 -0
  104. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/admission.py +0 -0
  105. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/api_models.py +0 -0
  106. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/app.py +0 -0
  107. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/assess.py +0 -0
  108. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/catalog.py +0 -0
  109. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/catalog_shape.py +0 -0
  110. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/deps.py +0 -0
  111. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/enhancers.py +0 -0
  112. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/exports.py +0 -0
  113. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/guides.py +0 -0
  114. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/http_security.py +0 -0
  115. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/inline_media.py +0 -0
  116. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/job_history.py +0 -0
  117. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/job_record.py +0 -0
  118. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/jobs.py +0 -0
  119. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/mcp_mount.py +0 -0
  120. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/netinfo.py +0 -0
  121. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/observed_cost.py +0 -0
  122. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/outputs.py +0 -0
  123. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/routes/__init__.py +0 -0
  124. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/routes/assets.py +0 -0
  125. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/routes/files.py +0 -0
  126. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/routes/gallery.py +0 -0
  127. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/routes/library.py +0 -0
  128. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/routes/loras.py +0 -0
  129. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/routes/media.py +0 -0
  130. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/routes/system.py +0 -0
  131. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/sysinfo.py +0 -0
  132. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/abap-08VXUWAP.js +0 -0
  133. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/apex-BWPQTe0t.js +0 -0
  134. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/azcli-Bc_sGQ0U.js +0 -0
  135. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/bat-i0X4ZdIN.js +0 -0
  136. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/bicep-B5-_aFwp.js +0 -0
  137. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/cameligo-DMUM7wLl.js +0 -0
  138. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/clojure-Cm7r79vr.js +0 -0
  139. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/codicon-Brq4_Ui5.ttf +0 -0
  140. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/coffee-Ba7i2nA0.js +0 -0
  141. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/cpp-C7h46wYY.js +0 -0
  142. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/csharp-BKxtCVv1.js +0 -0
  143. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/csp-bTuwJoIa.js +0 -0
  144. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/css-DIMkf-bt.js +0 -0
  145. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/css.worker-B3ciXF_0.js +0 -0
  146. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/cssMode-C1mrKegs.js +0 -0
  147. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/cypher-CVaqCwHa.js +0 -0
  148. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/dart-onAF5SnQ.js +0 -0
  149. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/dockerfile-DZFCIeNp.js +0 -0
  150. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/ecl-D05T4iGw.js +0 -0
  151. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/editor-jjEx9u7D.css +0 -0
  152. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/editor.api-EZbUgQvM.js +0 -0
  153. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/editor.worker-q-txB4vs.js +0 -0
  154. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/elixir-6RTg0lbw.js +0 -0
  155. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/flow9-C5_-GSwl.js +0 -0
  156. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/freemarker2-Dth1K2Vy.js +0 -0
  157. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/fsharp-C8Ef5oNN.js +0 -0
  158. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/go-C-y9NEjX.js +0 -0
  159. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/graphql-fmXr3nnJ.js +0 -0
  160. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/handlebars-8Sswqnxq.js +0 -0
  161. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/hcl-CpzslTdj.js +0 -0
  162. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/html-BYtYo7f1.js +0 -0
  163. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/html.worker-C93Ht9o9.js +0 -0
  164. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/htmlMode-B_AiWoXc.js +0 -0
  165. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/index-B6Ujrvuj.js +0 -0
  166. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/index-BChhQc7I.css +0 -0
  167. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/ini-sBoK_t0W.js +0 -0
  168. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/java-BEtHBSE6.js +0 -0
  169. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/javascript-E4m083D3.js +0 -0
  170. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/json.worker-B2V3pomh.js +0 -0
  171. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/jsonMode--h7ZikWy.js +0 -0
  172. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/julia-Bri6UV-V.js +0 -0
  173. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/kotlin-BOotOW0E.js +0 -0
  174. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/less-B9JPFI3C.js +0 -0
  175. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/lexon-CfSJPG6W.js +0 -0
  176. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/liquid-BzbKdLRW.js +0 -0
  177. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/lspLanguageFeatures-CRa0bRFV.js +0 -0
  178. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/lua-CsQS60Ue.js +0 -0
  179. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/m3-D-oSqn_W.js +0 -0
  180. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/markdown-Cimd5fb3.js +0 -0
  181. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/mdx-BJ4u6XQs.js +0 -0
  182. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/mips-CIPQ_RoX.js +0 -0
  183. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/monaco--ixms01u.css +0 -0
  184. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/monaco-DwaeaVri.js +0 -0
  185. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/msdax-DauUninz.js +0 -0
  186. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/mysql-SOo6toE5.js +0 -0
  187. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/objective-c-FvmIjYaQ.js +0 -0
  188. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/pascal-DrH0SRf2.js +0 -0
  189. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/pascaligo-D-ptJ9y-.js +0 -0
  190. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/perl-oz_6vUea.js +0 -0
  191. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/pgsql-DTj74zXo.js +0 -0
  192. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/php-nr791fC2.js +0 -0
  193. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/pla-CopQ2nXW.js +0 -0
  194. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/postiats-43DmfD33.js +0 -0
  195. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/powerquery-D3hlyOfw.js +0 -0
  196. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/powershell-DmHpPYUd.js +0 -0
  197. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/protobuf-C531GsRP.js +0 -0
  198. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/pug-Z5eAx3Zn.js +0 -0
  199. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/python-CoX_gCAL.js +0 -0
  200. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/qsharp-DkqhCAOL.js +0 -0
  201. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/r-BwWrilGY.js +0 -0
  202. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/razor-BCTdG5SN.js +0 -0
  203. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/redis-ClamHrr6.js +0 -0
  204. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/redshift-DT7zqm-g.js +0 -0
  205. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/restructuredtext-BYgofb2h.js +0 -0
  206. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/ruby-DezsRK8O.js +0 -0
  207. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/rust-DdL9SqIa.js +0 -0
  208. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/sb-CcwsVR0C.js +0 -0
  209. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/scala-DHpiXF5c.js +0 -0
  210. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/scheme-BeGwcela.js +0 -0
  211. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/scss-gp-XZpBa.js +0 -0
  212. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/shell-CC2rA5mh.js +0 -0
  213. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/solidity-BEEn4gHE.js +0 -0
  214. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/sophia-CRfGWb83.js +0 -0
  215. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/sparql-D_Lu-MrJ.js +0 -0
  216. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/sql-NEE52Syq.js +0 -0
  217. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/st-DbInun42.js +0 -0
  218. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/swift-Bxkupp3x.js +0 -0
  219. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/systemverilog-Bz4Y3fRF.js +0 -0
  220. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/tcl-DISqw1ZD.js +0 -0
  221. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/ts.worker-D7T1-Ig5.js +0 -0
  222. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/tsMode-B6BV7a76.js +0 -0
  223. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/twig-De2hgUGE.js +0 -0
  224. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/typescript-DJe7-fvZ.js +0 -0
  225. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/typespec-B8J7ngcE.js +0 -0
  226. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/vb-DV3o63ZY.js +0 -0
  227. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/wgsl-DpFanUEy.js +0 -0
  228. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/workers-DCzLJTPE.js +0 -0
  229. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/xml-CNvWfUrB.js +0 -0
  230. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/assets/yaml-ByARP6WE.js +0 -0
  231. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/ui/index.html +0 -0
  232. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/server/updater.py +0 -0
  233. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/settings.py +0 -0
  234. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/shot_span_preflight.py +0 -0
  235. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/shots.py +0 -0
  236. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/slice_preflight.py +0 -0
  237. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/step.py +0 -0
  238. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/step_cache.py +0 -0
  239. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/step_value_checks.py +0 -0
  240. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/subfolders.py +0 -0
  241. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/task_domains.py +0 -0
  242. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/assess.py +0 -0
  243. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/audio_dynamics.py +0 -0
  244. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/audio_transcription.py +0 -0
  245. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/audio_utils.py +0 -0
  246. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/background_remover.py +0 -0
  247. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/borders.py +0 -0
  248. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/compose_text.py +0 -0
  249. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/concat_videos.py +0 -0
  250. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/depth_estimator.py +0 -0
  251. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/diffusion_upscale.py +0 -0
  252. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/dissolve_videos.py +0 -0
  253. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/format_messages.py +0 -0
  254. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/gather.py +0 -0
  255. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/grade.py +0 -0
  256. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/h3_latent_upscale.py +0 -0
  257. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/h3_latent_upscaler_model.py +0 -0
  258. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/image_to_text.py +0 -0
  259. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/image_utils.py +0 -0
  260. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/interpolate_frames.py +0 -0
  261. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/join_into_song.py +0 -0
  262. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/judge.py +0 -0
  263. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/loop_bed.py +0 -0
  264. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/model_cache.py +0 -0
  265. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/pair_audio.py +0 -0
  266. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/qr_code.py +0 -0
  267. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/restore_faces.py +0 -0
  268. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/rife_model.py +0 -0
  269. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/segment.py +0 -0
  270. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/select.py +0 -0
  271. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/speech_generation.py +0 -0
  272. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/stabilize.py +0 -0
  273. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/task.py +0 -0
  274. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/tensor_image.py +0 -0
  275. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/text_generation.py +0 -0
  276. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/text_sections.py +0 -0
  277. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/upscale.py +0 -0
  278. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/video_utils.py +0 -0
  279. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/voice_attribution.py +0 -0
  280. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/tasks/zoe_depth.py +0 -0
  281. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/test.py +0 -0
  282. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/trust.py +0 -0
  283. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/type_helpers.py +0 -0
  284. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/type_references.py +0 -0
  285. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/validate.py +0 -0
  286. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/validation.py +0 -0
  287. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/variable_constraints.py +0 -0
  288. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/variables.py +0 -0
  289. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/video_extensions.py +0 -0
  290. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/video_size_errors.py +0 -0
  291. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/vram_estimate.py +0 -0
  292. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/vram_inheritance.py +0 -0
  293. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/worker.py +0 -0
  294. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/worker_manager.py +0 -0
  295. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/worker_protocol.py +0 -0
  296. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/workflow.py +0 -0
  297. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/workflow_run.py +0 -0
  298. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/workflow_schema.json +0 -0
  299. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/workflows/h3_context_ir.json +0 -0
  300. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/workflows/test.json +0 -0
  301. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/workspace.py +0 -0
  302. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw/writers.py +0 -0
  303. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/__init__.py +0 -0
  304. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/__main__.py +0 -0
  305. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/assets.py +0 -0
  306. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/authoring.py +0 -0
  307. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/catalog.py +0 -0
  308. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/client.py +0 -0
  309. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/confine.py +0 -0
  310. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/diagnose.py +0 -0
  311. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/guides.py +0 -0
  312. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/loras.py +0 -0
  313. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/media.py +0 -0
  314. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/models.py +0 -0
  315. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/prompts.py +0 -0
  316. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/server.py +0 -0
  317. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/tools_authoring.py +0 -0
  318. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/tools_catalog.py +0 -0
  319. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/tools_media.py +0 -0
  320. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/dw_mcp/workspaces.py +0 -0
  321. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/setup.cfg +0 -0
  322. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_admission.py +0 -0
  323. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_api_contract.py +0 -0
  324. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_arch_metrics.py +0 -0
  325. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_arch_report.py +0 -0
  326. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_architecture_map.py +0 -0
  327. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_argument_updates.py +0 -0
  328. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_arguments.py +0 -0
  329. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_assess.py +0 -0
  330. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_assessment_rules.py +0 -0
  331. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_assets.py +0 -0
  332. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_attention_slicing.py +0 -0
  333. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_audio_utils.py +0 -0
  334. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_best_of_n_template.py +0 -0
  335. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_borders.py +0 -0
  336. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_cache_blocks.py +0 -0
  337. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_catalog_shape.py +0 -0
  338. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_catalog_structure.py +0 -0
  339. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_chain.py +0 -0
  340. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_ci_config.py +0 -0
  341. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_cli.py +0 -0
  342. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_component_name_errors.py +0 -0
  343. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_component_pruning.py +0 -0
  344. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_component_type_errors.py +0 -0
  345. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_compose_text.py +0 -0
  346. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_config_objects.py +0 -0
  347. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_configuration_schema.py +0 -0
  348. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_content_types.py +0 -0
  349. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_depth_estimator.py +0 -0
  350. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_device.py +0 -0
  351. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_device_helpers.py +0 -0
  352. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_device_portability.py +0 -0
  353. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_dialogue_short_voices.py +0 -0
  354. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_diffusion_upscale.py +0 -0
  355. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_dissolve_frame_errors.py +0 -0
  356. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_dissolve_videos.py +0 -0
  357. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_docs_links.py +0 -0
  358. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_download_watch.py +0 -0
  359. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_dsp.py +0 -0
  360. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_elision.py +0 -0
  361. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_events.py +0 -0
  362. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_examples.py +0 -0
  363. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_find_loop_bed.py +0 -0
  364. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_for_each.py +0 -0
  365. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_gather.py +0 -0
  366. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_grade.py +0 -0
  367. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_h3_adapters.py +0 -0
  368. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_h3_context_ir.py +0 -0
  369. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_h3_latent_upscale.py +0 -0
  370. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_h3_schedule.py +0 -0
  371. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_h3_upscaler_source.py +0 -0
  372. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_h3_vram_ceiling.py +0 -0
  373. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_hf_placement.py +0 -0
  374. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_host_memory.py +0 -0
  375. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_host_memory_projection.py +0 -0
  376. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_hub_cache.py +0 -0
  377. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_image_to_text.py +0 -0
  378. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_image_utils.py +0 -0
  379. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_integration.py +0 -0
  380. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_interpolate_frames.py +0 -0
  381. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_introspection.py +0 -0
  382. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_job_history_wal.py +0 -0
  383. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_job_progress.py +0 -0
  384. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_job_warnings.py +0 -0
  385. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_jobs_listing.py +0 -0
  386. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_jobs_reused.py +0 -0
  387. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_join_into_song.py +0 -0
  388. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_judge.py +0 -0
  389. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_kernel_availability.py +0 -0
  390. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_library_path.py +0 -0
  391. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_library_sources.py +0 -0
  392. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_list_images.py +0 -0
  393. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_locations.py +0 -0
  394. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_lora_alpha.py +0 -0
  395. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_lora_catalog.py +0 -0
  396. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_lora_disable.py +0 -0
  397. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_lora_hub.py +0 -0
  398. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_lora_library.py +0 -0
  399. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_ltx2_diffusion_decode.py +0 -0
  400. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_ltx2_extend_clip.py +0 -0
  401. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_ltx2_ic_loras.py +0 -0
  402. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_ltx_prompt_library.py +0 -0
  403. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_assets.py +0 -0
  404. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_authoring.py +0 -0
  405. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_catalog.py +0 -0
  406. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_client.py +0 -0
  407. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_confine.py +0 -0
  408. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_diagnose.py +0 -0
  409. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_guides.py +0 -0
  410. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_loras.py +0 -0
  411. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_main.py +0 -0
  412. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_media.py +0 -0
  413. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_models.py +0 -0
  414. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_prompts.py +0 -0
  415. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_twins.py +0 -0
  416. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mcp_workspaces.py +0 -0
  417. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_media_audio.py +0 -0
  418. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_media_frames.py +0 -0
  419. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_media_info.py +0 -0
  420. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_media_layering.py +0 -0
  421. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mix_audio.py +0 -0
  422. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_model_cache.py +0 -0
  423. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_modular_output_properties.py +0 -0
  424. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_modular_pipeline.py +0 -0
  425. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_modular_progress.py +0 -0
  426. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mps_fallback_warning.py +0 -0
  427. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_mps_rope_precision.py +0 -0
  428. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_null_media.py +0 -0
  429. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_observed_cost.py +0 -0
  430. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_offload_placement.py +0 -0
  431. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_output_references.py +0 -0
  432. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_pair_audio.py +0 -0
  433. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_pair_audio_fit.py +0 -0
  434. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_parallel_loading_default.py +0 -0
  435. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_path_redaction.py +0 -0
  436. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_phase_events.py +0 -0
  437. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_pipeline_caching.py +0 -0
  438. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_pipeline_components.py +0 -0
  439. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_prepare_pipeline.py +0 -0
  440. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_previous_result_walker_parity.py +0 -0
  441. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_previous_results.py +0 -0
  442. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_prompt_library.py +0 -0
  443. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_prompt_parser.py +0 -0
  444. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_prompt_references.py +0 -0
  445. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_prompt_weighting.py +0 -0
  446. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_prompts.py +0 -0
  447. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_real_models.py +0 -0
  448. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_realize.py +0 -0
  449. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_recenter_crop.py +0 -0
  450. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_reference_limits.py +0 -0
  451. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_reference_names.py +0 -0
  452. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_reference_skip_sets.py +0 -0
  453. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_references.py +0 -0
  454. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_rerun_new_seed.py +0 -0
  455. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_resize_bucket.py +0 -0
  456. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_result.py +0 -0
  457. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_result_fps.py +0 -0
  458. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_result_output_naming.py +0 -0
  459. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_rule_parity.py +0 -0
  460. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_runs.py +0 -0
  461. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_scalar_result_validation.py +0 -0
  462. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_scheduler_config.py +0 -0
  463. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_schema.py +0 -0
  464. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_security.py +0 -0
  465. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_security_auth.py +0 -0
  466. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_security_decoder_bombs.py +0 -0
  467. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_security_input_caps.py +0 -0
  468. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_security_ssrf.py +0 -0
  469. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_security_symlinks.py +0 -0
  470. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_security_trust_gate.py +0 -0
  471. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_security_web_headers.py +0 -0
  472. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_segment.py +0 -0
  473. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_select.py +0 -0
  474. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_select_validation.py +0 -0
  475. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_serve_main.py +0 -0
  476. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server.py +0 -0
  477. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_assess.py +0 -0
  478. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_downloads.py +0 -0
  479. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_gallery_image.py +0 -0
  480. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_guides.py +0 -0
  481. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_info.py +0 -0
  482. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_jobs.py +0 -0
  483. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_library_path.py +0 -0
  484. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_loras.py +0 -0
  485. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_mcp.py +0 -0
  486. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_server_workspaces.py +0 -0
  487. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_shaping_logs.py +0 -0
  488. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_shot_span_preflight.py +0 -0
  489. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_shots.py +0 -0
  490. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_slice_preflight.py +0 -0
  491. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_speech_generation.py +0 -0
  492. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_stabilize.py +0 -0
  493. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_step.py +0 -0
  494. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_step_cache.py +0 -0
  495. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_strip_exif_and_watermark.py +0 -0
  496. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_sub_workflow_resolver.py +0 -0
  497. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_subfolders.py +0 -0
  498. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_task.py +0 -0
  499. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_task_discovery.py +0 -0
  500. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_task_domains.py +0 -0
  501. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_task_signature_errors.py +0 -0
  502. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_template_defaults.py +0 -0
  503. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_template_subfolders.py +0 -0
  504. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_tensor_image.py +0 -0
  505. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_text_generation.py +0 -0
  506. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_text_sections.py +0 -0
  507. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_transcribe_audio.py +0 -0
  508. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_type_helpers.py +0 -0
  509. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_ui_twins.py +0 -0
  510. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_updater.py +0 -0
  511. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_validate_arguments.py +0 -0
  512. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_validation.py +0 -0
  513. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_variable_constraints.py +0 -0
  514. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_variables.py +0 -0
  515. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_video_extensions.py +0 -0
  516. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_video_size_errors.py +0 -0
  517. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_video_utils.py +0 -0
  518. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_voice_attribution.py +0 -0
  519. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_vram_estimate.py +0 -0
  520. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_vram_inheritance.py +0 -0
  521. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_worker.py +0 -0
  522. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_worker_execute.py +0 -0
  523. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_worker_job_peak_rss.py +0 -0
  524. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_worker_manager.py +0 -0
  525. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_worker_messages.py +0 -0
  526. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_workflow.py +0 -0
  527. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_workflow_snapshot.py +0 -0
  528. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_workflow_step_cache.py +0 -0
  529. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_workflow_trust.py +0 -0
  530. {diffusers_workflow-0.8.0 → diffusers_workflow-0.9.0}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffusers-workflow
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: Declarative workflow engine and web UI for Hugging Face Diffusers
5
5
  Author: dkackman
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffusers-workflow
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: Declarative workflow engine and web UI for Hugging Face Diffusers
5
5
  Author: dkackman
6
6
  License: Apache-2.0
@@ -308,7 +308,7 @@ references written in the same session.
308
308
  | `run_workflow(workflow_path=None, inline_workflow=None, arguments=None, acknowledged_cost=False, workspace=None, wait_seconds=0)` | exactly one of `workflow_path` (a catalog name from `list_workflows`, with or without `.json`, or a path to a workflow file on the server) or `inline_workflow`, optional `arguments`, `acknowledged_cost`, `workspace`, `wait_seconds` | Queue a workflow for generation. Returns as soon as the job is queued - unless `wait_seconds` is above 0, in which case the call then waits on the queued job exactly as `wait_for_job(job_id, timeout_seconds=wait_seconds)` would (same per-call cap, clamped not honoured) and the result carries the queued-job fields plus that wait's (`status`, `still_running`, `waited_seconds`, `timeout_requested_seconds`, `timeout_applied_seconds`, `timeout_capped`, the slim `job`); when the cap covers the job's runtime one call is the run and the wait, and a `still_running: true` result is followed with `wait_for_job` as before. `workspace` names the workspace for this one call without switching the session to it - use it to pin a job whose `output:` or `asset:` references live in a workspace other than the session's - `acknowledged_cost` is `true` or the bound `{fingerprint, minutes, downloads}` from the validate plan; a 409 means the plan changed and the message carries the new estimate, and nothing is waited on |
309
309
  | `get_job(job_id)` | `job_id` | Get a job's status, warnings, output manifest, error and traceback; each manifest entry's `subfolder` is the in-run subfolder the step declared - by convention `final` for the deliverable, `intermediate` for scratch, `''` for none. A running job also carries `progress` (below) |
310
310
  | `get_job_workflow(job_id)` | `job_id` | The REST equivalent is `GET /api/jobs/{id}/workflow` (see [SERVER.md](SERVER.md#jobs-api)). The workflow the job actually ran. `realized: true` means every mutable input is pinned (arguments, seed, prompts, `output:latest`); `false` means the job predates run tracking and this is the definition as submitted. Pass it to `save_workflow` to keep it under a name |
311
- | `export_job(job_id, overwrite=False)` | `job_id`, `overwrite` | Gather one finished job into `<workspace>/exports/<job id>/` on the server: the realized workflow, the run's manifest, the job row, a README, and copies of the assets, earlier-run inputs and outputs. Returns the directory, a zip URL, the file list with sizes and the total. The three JSON files are in the zip, not repeated here - get_job_workflow and get_job serve them individually. **The directory is on the machine running the server**, like `download_output`'s destination. `auth_required` says whether opening the zip needs this server's bearer token, which this agent cannot attach to someone else's fetch (#353): when false, fetch `open_url` yourself and unpack it into `exports/` under the session's working directory (a deliverable, not a temp file) - the archive already unpacks into one folder named after the job id, so do not create that folder first; when true, hand `open_url` to the person instead of fetching it |
311
+ | `export_job(job_id, overwrite=False)` | `job_id`, `overwrite` | Gather one finished job into `<workspace>/exports/<job id>/` on the server: the realized workflow, the run's manifest, the job row, a README, and copies of the assets, earlier-run inputs and outputs. Returns the directory, a zip URL, the file list with sizes and the total. The three JSON files are in the zip, not repeated here - get_job_workflow and get_job serve them individually. **The directory is on the machine running the server**, like `download_output`'s destination. The zip needs no token (`/exports/*.zip` is ungated like `/outputs`, #592), so `auth_required` is false and the agent fetches `open_url` itself (prefixing a relative one with the server address) and unpacks it into `exports/` under the session's working directory (a deliverable, not a temp file) - the archive already unpacks into one folder named after the job id, so do not create that folder first; only an agent without HTTP gives the user `open_url`. `true` is kept as a forward guard for a gated zip, when `open_url` is handed to the person instead |
312
312
  | `get_job_events(job_id, after=-1, limit=200)` | `job_id`, `after`, `limit` | Get a page of a job's progress events |
313
313
  | `wait_for_job(job_id, timeout_seconds=20)` | `job_id`, `timeout_seconds` | Block until a job reaches a terminal status, or `timeout_seconds` elapses. **One call blocks for at most the server's cap** — 55 seconds unless the deployment sets `DW_MCP_MAX_WAIT_SECONDS` higher, and the tool's description states the live value; a larger `timeout_seconds` is clamped, not honoured. Ask for the job's `plan.estimate` plus a margin: under the cap that is one call for the whole job, over it one call per cap. A deployment raises the cap only as far as its clients (and anything between them and the server) hold one silent HTTP request open - Claude Code's limit is `MCP_TOOL_TIMEOUT`. Every reply carries `waited_seconds`, `timeout_requested_seconds`, `timeout_applied_seconds` and `timeout_capped`, so a capped return is distinguishable from an elapsed one. Use instead of hand-polling `get_job`/`get_job_events` in a loop; if it returns `still_running: true`, call it again. Returns a slim job - status, warnings, error, `run_id` and `run_version` (the run's `v5`, as the gallery labels it), and the manifest once finished - without the arguments; `get_job` has those. A running job also carries `progress` (below) |
314
314
  | `cancel_job(job_id)` | `job_id` | Ask a queued or running job to stop |
@@ -7,6 +7,63 @@ notes from commits at tag time (see below). This section is a scratch pad
7
7
  for items a branch's author wants the next release note to name; clear it
8
8
  when a release ships.
9
9
 
10
+ ### 0.9.0
11
+
12
+ <!-- Drafted from v0.8.0..dd74e17b (develop). Paste into the GitHub release body once the tag has published: gh release edit v0.9.0 --notes-file ... -->
13
+
14
+ A smaller range than 0.8.0: the LoRA catalog grows to cover three more
15
+ bases, the cost planner is corrected for list-driven and `for_each` steps,
16
+ and `export_job` reports its zip's real auth gating. No upgrade-order
17
+ changes.
18
+
19
+ **LoRA catalog** (docs/LORAS.md)
20
+
21
+ - 11 LTX-2.5 entries added: IC-LoRAs for alpha generation, clean plate,
22
+ colorization, day-to-night, layout-to-render, detail refinement,
23
+ restoration, SDR-to-HDR and water simulation, plus cinemagraph and
24
+ slow-motion control.
25
+ - MiniMax-H3 goes to 13 trial and 3 rejected entries beyond the 0.8.0 set:
26
+ styles, speech, orbits, action and motion, and a diffusers-native 4-step
27
+ turbo. FastVideo FastH3 (`.diff` keys), RAVEN (unrecognised prefix, loads
28
+ nothing silently) and TaoMate (lowercase `lora_a`/`lora_b`) are rejected.
29
+ - New bases: Qwen-Image-2.1 (10 trial, including three few-step distills
30
+ that need their scheduler and sigma overrides, plus edit LoRAs; Fun-Acc
31
+ rejected) and Z-Image-Turbo (13 trial). The Z-Image entries match
32
+ `Tongyi-MAI/Z-Image-Turbo` only; whether they apply to the SDNQ
33
+ checkpoint is untested.
34
+ - Every new entry pins the repo's current sha, and its header was read
35
+ against the installed diffusers converter.
36
+
37
+ **Cost planning**
38
+
39
+ - `other_device` figures are re-priced for `for_each` counts and shifted
40
+ list drivers (#589, #590).
41
+ - A shifted per-entry field in a list driver resets to unknown, and a
42
+ summed child figure takes the children's basis (#593). A numeric string
43
+ in a list-driver entry compares as its number (#593).
44
+ - Measured MPS cost entries for `templates/ltx2/text-to-video` and Music 3
45
+ (#590).
46
+
47
+ **Fixes**
48
+
49
+ - `assemble-and-score` threads `sample_rate` into its edit join, and mixed-rate
50
+ shots resampled to a pinned `sample_rate` no longer draw a warning advising
51
+ you to pass it (#594).
52
+ - `export_job` reports the zip's real gating: `/exports/*.zip` is ungated
53
+ like `/outputs`, so `auth_required` is false whether or not the server has
54
+ a token. The MCP `next` text and the skills now say to fetch `open_url` and
55
+ unpack into `exports/` to bring a project home (#595, #592).
56
+
57
+ **Plugin**
58
+
59
+ - The `ltx-2.5`, `minimax-h3` and `minimax-music3` skills moved
60
+ request-specific detail (the LTX caption spec, H3 checkpoint and LoRA
61
+ combinations and cuts recipes, Music 3 loudness) into `references/`
62
+ beside each `SKILL.md`, which are read when the skill points there. The
63
+ 12 KiB cap applies to `SKILL.md` alone; a new test fails on an unlinked
64
+ reference or a dead link.
65
+ - The H3 and LTX skills point at `list_loras`.
66
+
10
67
  ### 0.8.0
11
68
 
12
69
  <!-- Drafted from v0.7.0..a9e1e72b (develop). Paste into the GitHub release body once the tag has published: gh release edit v0.8.0 --notes-file ... -->
@@ -38,6 +95,9 @@ upscaler and two security fixes.
38
95
  - GHSA-crqf-hw9p-r739: `create_workspace`'s MCP result is `name`,
39
96
  `default`, `current` and `next` only; `list_workspaces(detail=true)` is
40
97
  the opt-in for folder paths. `POST /api/workspaces` is unchanged.
98
+ - `GET /api/loras/recommend` and `recommend_loras`: `hub_error` names the
99
+ exception type, or the HTTP status, never the exception's text, which
100
+ could name the server's HF cache directory. The log keeps the full error.
41
101
  - UI lockfile bumps for open Dependabot alerts (devalue, dompurify,
42
102
  brace-expansion, undici).
43
103
 
@@ -48,6 +48,11 @@ responses add an `absolute_url` / `absolute_zip_url` built from it; nothing
48
48
  guesses this from request headers, so an unconfigured server omits the
49
49
  field rather than composing a wrong origin.
50
50
 
51
+ The export zip, like `/outputs` and `/inputs` files, needs no token (#592), so
52
+ an agent with HTTP fetches `export_job`'s `open_url` itself, prefixing a
53
+ relative one with the address it reaches the server at; `DW_PUBLIC_URL` is for
54
+ the URLs handed to a person.
55
+
51
56
  ## Browser
52
57
 
53
58
  Open `http://<box>:8765`. Click the key icon next to the theme toggle,
@@ -17,6 +17,7 @@ import copy
17
17
  import hashlib
18
18
  import json
19
19
  import logging
20
+ import math
20
21
  import os
21
22
 
22
23
  from huggingface_hub import model_info
@@ -340,6 +341,36 @@ def _driver_comparable(value):
340
341
  return json.dumps(value, sort_keys=True, default=str)
341
342
 
342
343
 
344
+ def _numeric_fields(entries):
345
+ """{field: {values}} over the numeric fields of a list's dict entries. A
346
+ numeric string ("243") counts as its number, as `_driver_comparable` does
347
+ for a scalar driver."""
348
+ fields = {}
349
+ if not isinstance(entries, list):
350
+ return fields
351
+ for entry in entries:
352
+ if not isinstance(entry, dict):
353
+ continue
354
+ for key, value in entry.items():
355
+ number = _driver_comparable(value)
356
+ if isinstance(number, float) and math.isfinite(number):
357
+ fields.setdefault(key, set()).add(number)
358
+ return fields
359
+
360
+
361
+ def _list_entry_field_shifted(default_entries, effective_entries):
362
+ """Whether a list driver's entries carry a numeric field (a per-shot
363
+ `num_frames`, say) with a value none of the default entries had. The
364
+ curated figure was measured over the default entries' values, so a value
365
+ outside them has no matching bucket - the #267 rule applied inside a
366
+ list driver (#593)."""
367
+ measured = _numeric_fields(default_entries)
368
+ for field, values in _numeric_fields(effective_entries).items():
369
+ if field in measured and not values <= measured[field]:
370
+ return True
371
+ return False
372
+
373
+
343
374
  def _scalar_driver_shifted(definition, expanded, list_entries):
344
375
  """Whether a declared, non-list `cost_driver` was overridden away from
345
376
  the default value the curated `cost` was measured against (#267).
@@ -352,10 +383,12 @@ def _scalar_driver_shifted(definition, expanded, list_entries):
352
383
  defaults = definition.get("variables") or {}
353
384
  effective = expanded.get("variables") or {}
354
385
  for name in _declared_drivers(definition):
355
- if name in list_entries:
356
- continue
357
386
  default_value = defaults.get(name)
358
387
  if isinstance(default_value, list):
388
+ if _list_entry_field_shifted(default_value, effective.get(name)):
389
+ return True
390
+ continue
391
+ if name in list_entries:
359
392
  continue
360
393
  if _driver_comparable(effective.get(name)) != _driver_comparable(default_value):
361
394
  return True
@@ -365,7 +398,7 @@ def _scalar_driver_shifted(definition, expanded, list_entries):
365
398
  def _own_price(definition, expanded, list_entries, device, measured_entries):
366
399
  """The workflow's own price, reset to unknown when a scalar driver moved."""
367
400
  own = _price(definition.get("cost"), device, list_entries, measured_entries or {})
368
- if own["basis"] == CATALOG and _scalar_driver_shifted(
401
+ if own["basis"] in (CATALOG, OTHER_DEVICE) and _scalar_driver_shifted(
369
402
  definition, expanded, list_entries
370
403
  ):
371
404
  # A scalar cost_driver (H3's num_frames, say) moved away from the
@@ -434,7 +467,7 @@ def _child_catalog_price(child_definition, child_cost, step_arguments, device):
434
467
  child_list_entries = _list_entries(child_definition, child_expanded)
435
468
  child = _price(child_cost, device, child_list_entries, child_measured_entries)
436
469
  if (
437
- child["basis"] == CATALOG
470
+ child["basis"] in (CATALOG, OTHER_DEVICE)
438
471
  and child_definition is not None
439
472
  and _scalar_driver_shifted(child_definition, child_expanded, child_list_entries)
440
473
  ):
@@ -458,6 +491,7 @@ class _ChildTotals:
458
491
  self.all_observed = True
459
492
  self.runs = []
460
493
  self.measured_on = set()
494
+ self.bases = []
461
495
 
462
496
  def add(self, path, child, observed):
463
497
  self.had_child = True
@@ -469,7 +503,9 @@ class _ChildTotals:
469
503
  if child["minutes"] is None:
470
504
  self.partial = True
471
505
  self.unpriced.append(path)
472
- elif self.minutes is not None:
506
+ return
507
+ self.bases.append((child["basis"], child.get("measured_on")))
508
+ if self.minutes is not None:
473
509
  self.minutes += child["minutes"]
474
510
  else:
475
511
  self.minutes = child["minutes"]
@@ -509,6 +545,20 @@ def _rolled_up_estimate(own, totals, device, cached_steps, total_steps):
509
545
  top_measured_on = (
510
546
  next(iter(totals.measured_on)) if len(totals.measured_on) == 1 else None
511
547
  )
548
+ if top_basis == UNKNOWN and minutes is not None and totals.bases:
549
+ # A number must not carry the basis a caller reads as "no number":
550
+ # the parent has no cost of its own, but its priced children do, so
551
+ # the sum takes their basis (#593)
552
+ kinds = {basis for basis, _ in totals.bases}
553
+ if OTHER_DEVICE in kinds:
554
+ top_basis = OTHER_DEVICE
555
+ elif kinds == {CATALOG}:
556
+ top_basis = CATALOG
557
+ else:
558
+ top_basis = DERIVED
559
+ devices = {on for _, on in totals.bases}
560
+ if len(devices) == 1:
561
+ top_measured_on = next(iter(devices))
512
562
  rounded = round(minutes, 1) if minutes is not None else None
513
563
  result = {
514
564
  "minutes": rounded,
@@ -696,18 +746,22 @@ def _price(cost, device, list_entries, measured_entries):
696
746
  basis = OTHER_DEVICE
697
747
  minutes = float(chosen.get("minutes", 0))
698
748
  per = chosen.get("per_entry")
699
- if (
700
- basis == CATALOG
701
- and isinstance(per, dict)
702
- and per.get("variable") in list_entries
703
- ):
749
+ # Another device's figure is re-priced for the list length too (#589): it
750
+ # stays `other_device` (still not a measurement here), but a 32-entry
751
+ # for_each must not be quoted at the one-clip figure
752
+ if isinstance(per, dict) and per.get("variable") in list_entries:
704
753
  count = list_entries[per["variable"]]
705
754
  each = float(per.get("minutes", 0))
706
755
  measured_with = int(per.get("entries", 0))
707
756
  minutes = max(0.0, (minutes - each * measured_with) + each * count)
708
- basis = PER_ENTRY
709
- elif basis == CATALOG:
710
- minutes, basis = _repriced(minutes, list_entries, measured_entries)
757
+ if basis == CATALOG:
758
+ basis = PER_ENTRY
759
+ else:
760
+ minutes, repriced_basis = _repriced(minutes, list_entries, measured_entries)
761
+ if repriced_basis == UNKNOWN:
762
+ return {"minutes": None, "basis": UNKNOWN, "measured_on": None}
763
+ if basis == CATALOG:
764
+ basis = repriced_basis
711
765
  return {"minutes": minutes, "basis": basis, "measured_on": chosen.get("name")}
712
766
 
713
767
 
@@ -455,11 +455,13 @@ def export_job_route(
455
455
  absolute_zip_url = absolute_served_url(zip_path, ws)
456
456
  if absolute_zip_url is not None:
457
457
  body["absolute_zip_url"] = absolute_zip_url
458
- # Same rule get_server_info's field states (#353): whether the zip
459
- # URL above needs a bearer token an MCP-only agent has no way to
460
- # attach itself, which is what tells the caller whether to fetch it
461
- # or hand it to the person.
462
- body["auth_required"] = bool(state.api_token)
458
+ # Whether the zip URL above needs a bearer token (#353, #592) - a
459
+ # property of the route, not of whether this server has a token. The
460
+ # auth middleware covers /api/ and /mcp only, and /exports/*.zip sits
461
+ # with /outputs and /inputs on dw/server/routes/files.py's ungated
462
+ # list, so it is false. export_job's `next` keeps its hand-it-over
463
+ # branch for the day /exports is gated.
464
+ body["auth_required"] = False
463
465
  for key, name in (
464
466
  ("workflow", "workflow.json"),
465
467
  ("manifest", "manifest.json"),
@@ -94,6 +94,7 @@ def reconcile_sample_rates(command, videos, names, waveforms, sample_rate=None):
94
94
  for video, waveform in zip(videos, waveforms)
95
95
  if waveform is not None
96
96
  ]
97
+ pinned = bool(sample_rate)
97
98
  sample_rate = sample_rate or (max(set(rates)) if rates else None)
98
99
  if rates and len(set(rates)) == 1 and rates[0] != sample_rate:
99
100
  # The inputs agree and the caller pinned another rate: converting
@@ -104,6 +105,15 @@ def reconcile_sample_rates(command, videos, names, waveforms, sample_rate=None):
104
105
  command=command,
105
106
  sample_rate=sample_rate,
106
107
  )
108
+ elif rates and pinned and any(rate != sample_rate for rate in rates):
109
+ # Mixed inputs converted to the rate the caller (or the template)
110
+ # pinned: expected, and 'pass sample_rate' advice would be wrong (#594)
111
+ emit_log(
112
+ f"{command}: resampling tracks at {sorted(set(rates))} Hz to the "
113
+ f"requested {sample_rate} Hz",
114
+ command=command,
115
+ sample_rate=sample_rate,
116
+ )
107
117
  elif rates and any(rate != sample_rate for rate in rates):
108
118
  # emit_warning rather than logger.warning: resampling every track is
109
119
  # an audio decision made on the caller's behalf, and a caller reading
@@ -3,9 +3,9 @@
3
3
  The one thing this module has to keep saying: the directory it makes is on
4
4
  the machine running dw.serve, which over a `dw.serve --mcp` endpoint is the
5
5
  GPU box and not where the agent is. The zip URL is the way to it from
6
- anywhere else - but when the server requires a bearer token (#353), that URL
7
- is for the person to open, not for this agent to fetch on their behalf; see
8
- `export_job`'s `auth_required` / `open_url`.
6
+ anywhere else, and it needs no token: /exports/*.zip is ungated like
7
+ /outputs (#592), so `auth_required` is false and the agent fetches it itself.
8
+ The hand-it-to-the-person branch (#353) stays for the day it is gated.
9
9
  """
10
10
 
11
11
  from dw_mcp.client import api_path
@@ -22,10 +22,10 @@ def export_job(client, job_id, overwrite=False):
22
22
  here. The directory is on the server machine, not this one.
23
23
 
24
24
  `auth_required` says whether the zip needs this server's bearer token
25
- to open - a token this agent has no way to attach to a browser or hand
26
- to someone else's tooling. When it is true, `open_url` is for the
27
- *person* to open, not for this agent to fetch: hand it to them (see
28
- `next`). When it is false, `open_url` may be fetched directly. It is
25
+ to open. The server reports false - the zip route is ungated, token or
26
+ not (#592) - and `next` says to fetch `open_url` and unpack it. True
27
+ is a forward guard: a gated zip is for the *person* to open, since
28
+ this agent cannot attach the token to their browser. `open_url` is
29
29
  `absolute_zip_url` when the server has one configured (`DW_PUBLIC_URL`
30
30
  / the `public_url` setting), else the relative `zip_url`."""
31
31
  body = client.post_json(
@@ -58,11 +58,14 @@ def export_job(client, job_id, overwrite=False):
58
58
  open_url = absolute_zip_url or zip_url
59
59
  next_text = (
60
60
  "The directory is on the server. To give the user the files, "
61
- "fetch open_url and unpack it into exports/ under the session's "
62
- "working directory - it is the user's deliverable, not a "
63
- "temporary file, so not a scratch or temp directory. The archive "
64
- "already unpacks into one folder named after the job id; do not "
65
- "create that folder first or the id is doubled in the path. "
61
+ "fetch open_url with whatever HTTP you have - it needs no token; "
62
+ "prefix a relative one with the address you reach this server at "
63
+ "- and unpack it into exports/ under the session's working "
64
+ "directory - it is the user's deliverable, not a temporary file, "
65
+ "so not a scratch or temp directory. The archive already unpacks "
66
+ "into one folder named after the job id; do not create that "
67
+ "folder first or the id is doubled in the path. Only if you "
68
+ "cannot make HTTP requests, give the user open_url to open. "
66
69
  "workflow.json, manifest.json and job.json are inside it - they "
67
70
  "are not repeated here; get_job_workflow and get_job serve them "
68
71
  "individually."
@@ -216,16 +216,15 @@ class JobTools:
216
216
  repeated here - get_job_workflow and get_job serve them individually.
217
217
  THE DIRECTORY IS ON THE MACHINE RUNNING THE SERVER, not on yours.
218
218
 
219
- `auth_required` says whether opening the zip needs this server's
220
- bearer token, a token you cannot attach to someone else's browser
221
- or tooling. When it is false, fetch open_url yourself and unpack
222
- it into exports/ under the session's working directory - it is
223
- the user's deliverable, not a temp file; the archive already
224
- unpacks into one folder named after the job id, so do not create that folder first.
225
- When it is true, do NOT fetch it: hand open_url to the person and let them open it
226
- (`next` says whether it is already absolute or needs the server's
227
- address told to them). Individual results stay reachable inline
228
- via get_output_image/get_output_audio/get_output_frames either
229
- way. Refuses a job that is still running; refuses an existing
219
+ open_url is the zip and needs no token: fetch it with any HTTP
220
+ you have (prefix a relative one with the server's address) and
221
+ unpack it into exports/ under the session's working directory -
222
+ the user's deliverable, not a temp file; it already unpacks into
223
+ one folder named after the job id, so do not create that folder first.
224
+ Only without HTTP, give the user open_url to open. If `auth_required`
225
+ is ever true, the zip is gated: hand it over (see `next`).
226
+ Individual results stay reachable inline via
227
+ get_output_image/get_output_audio/get_output_frames
228
+ without the zip. Refuses a job that is still running; refuses an existing
230
229
  export unless overwrite=true."""
231
230
  return exports.export_job(self.client, job_id, overwrite=overwrite)
@@ -8,7 +8,7 @@ name = "diffusers-workflow"
8
8
  # runtime (static in TOML because importing dw at build time would drag
9
9
  # torch into the build environment). Release tags must match it - see
10
10
  # docs/RELEASING.md
11
- version = "0.8.0"
11
+ version = "0.9.0"
12
12
  description = "Declarative workflow engine and web UI for Hugging Face Diffusers"
13
13
  readme = "README.md"
14
14
  requires-python = ">=3.10"
@@ -124,3 +124,15 @@ def test_the_description_places_the_limit_ceiling_on_the_mix_not_the_film():
124
124
  description = load_definition()["description"]
125
125
  assert "holds on the mix 'balanced' writes, not on the film" in description
126
126
  assert "about 1 dB above it" in description
127
+
128
+
129
+ def test_edit_join_gets_template_sample_rate():
130
+ """#594: the edit join resampled mixed-rate shots to its own 48 kHz default
131
+ and warned the caller to pass sample_rate, which the template never
132
+ threaded through."""
133
+ from dw.variables import replace_variables, resolve_variable_values
134
+
135
+ definition = load_definition()
136
+ merged = resolve_variable_values({**definition["variables"], "sample_rate": 32000})
137
+ edit = steps_by_name(replace_variables(definition, merged))["edit"]
138
+ assert edit["task"]["arguments"]["sample_rate"] == 32000
@@ -643,6 +643,23 @@ class TestWarningsReachTheCaller:
643
643
  assert "resampling them all to 200 Hz" in warnings[0]["message"]
644
644
  assert "resample_audio" in warnings[0]["message"]
645
645
 
646
+ def test_mixed_rates_resampled_to_a_pinned_rate_draw_no_warning(self):
647
+ """#594: a pinned sample_rate is the target; no advice to pass it."""
648
+ from dw.events import RunContext, activate_context, deactivate_context
649
+
650
+ events = []
651
+ token = activate_context(RunContext(on_event=events.append))
652
+ try:
653
+ result = concat_videos(
654
+ [audio_video(8, 0.5), audio_video(8, 0.5, sample_rate=200)],
655
+ sample_rate=150,
656
+ )
657
+ finally:
658
+ deactivate_context(token)
659
+
660
+ assert result.sample_rate == 150
661
+ assert [e for e in events if e.get("event") == "warning"] == []
662
+
646
663
  def test_agreeing_rates_resampled_to_a_pinned_rate_draw_no_warning(self):
647
664
  """#453: inputs that agree, converted to the rate the caller pinned,
648
665
  are not 'videos at different sample rates'."""
@@ -106,9 +106,10 @@ def test_the_next_hint_sends_the_zip_to_the_working_directory():
106
106
 
107
107
 
108
108
  def test_auth_required_tells_the_agent_to_hand_the_zip_to_a_person():
109
- """#353: when the server gates the zip with a bearer token, this agent
110
- has no way to attach one to a fetch made on the person's behalf - the
111
- hint has to say hand it over, not fetch it."""
109
+ """#353, kept as a forward guard by #592: the server reports false
110
+ today, but if the zip is ever gated this agent has no way to attach the
111
+ token to a fetch made on the person's behalf - the hint has to say hand
112
+ it over, not fetch it."""
112
113
  client, _ = exporting(body={**SUMMARY, "auth_required": True})
113
114
 
114
115
  result = exports.export_job(client, "job-1")
@@ -135,7 +136,9 @@ def test_an_absolute_open_url_is_preferred_and_said_to_be_absolute():
135
136
  assert "already absolute" in result["next"]
136
137
 
137
138
 
138
- def test_no_auth_required_still_fetches_the_zip_itself():
139
+ def test_no_auth_required_fetches_the_zip_itself():
140
+ """#592: the zip is ungated, so the server reports false and the hint
141
+ says fetch and unpack - not hand it over, which is the gated branch."""
139
142
  client, _ = exporting(body={**SUMMARY, "auth_required": False})
140
143
 
141
144
  result = exports.export_job(client, "job-1")
@@ -143,6 +146,10 @@ def test_no_auth_required_still_fetches_the_zip_itself():
143
146
  assert result["auth_required"] is False
144
147
  assert result["open_url"] == "/exports/job-1.zip"
145
148
  assert "fetch open_url" in result["next"]
149
+ assert "needs no token" in result["next"]
150
+ assert "exports/" in result["next"]
151
+ assert "hand open_url to the person" not in result["next"]
152
+ assert "do not fetch it" not in result["next"]
146
153
 
147
154
 
148
155
  def test_absolute_zip_url_is_omitted_rather_than_null_when_unconfigured():
@@ -1056,17 +1056,18 @@ async def test_export_job_sends_the_zip_to_the_working_directory():
1056
1056
 
1057
1057
 
1058
1058
  @pytest.mark.asyncio
1059
- async def test_export_job_description_is_auth_aware():
1060
- """#353: the served tool description, not just the runtime `next` hint,
1061
- has to tell the agent not to fetch an auth-gated zip on the person's
1062
- behalf - the description is what the agent plans from before it ever
1063
- calls the tool and sees `next`."""
1059
+ async def test_export_job_description_says_to_fetch_the_ungated_zip():
1060
+ """#592: the zip route is ungated, so the served description - what the
1061
+ agent plans from before it sees `next` - tells it to fetch open_url,
1062
+ no longer the #353 "do NOT fetch it", and keeps auth_required's hand-over
1063
+ as the forward guard for a gated zip."""
1064
1064
  tools = await tools_of(server_over(ok({})))
1065
1065
 
1066
1066
  description = tools["export_job"].description
1067
+ assert "needs no token" in description
1068
+ assert "fetch it with any HTTP" in description
1069
+ assert "do NOT fetch it" not in description
1067
1070
  assert "auth_required" in description
1068
- assert "do NOT fetch it" in description
1069
- assert "hand open_url to the person" in description
1070
1071
 
1071
1072
 
1072
1073
  @pytest.mark.asyncio
@@ -365,14 +365,42 @@ class TestEstimate:
365
365
  answer = plan(spec, arguments=arguments)["estimate"]
366
366
  assert (answer["minutes"], answer["basis"]) == (None, "unknown")
367
367
 
368
- def test_another_devices_figure_is_not_extrapolated(self, plan):
369
- """`other_device` already says the figure is not this machine's -
370
- re-pricing it would dress a guess as arithmetic."""
368
+ def test_another_devices_figure_is_re_priced_for_the_list(self, plan):
369
+ """#589: a 2-shot figure from another backend quoted verbatim for a
370
+ 10-shot run was the 1/100 gate. It stays `other_device` - not a
371
+ measurement here - but the entry count multiplies."""
371
372
  spec = definition()
372
373
  spec["cost"] = [cost("mps", 40, name="M2")]
373
374
  shots = [{"name": f"s{n}", "prompt": "x"} for n in range(10)]
374
375
  answer = plan(spec, arguments={"shots": shots})["estimate"]
375
- assert (answer["minutes"], answer["basis"]) == (40.0, "other_device")
376
+ assert (answer["minutes"], answer["basis"]) == (200.0, "other_device")
377
+
378
+ def test_another_devices_per_entry_rate_prices_the_list(self, plan):
379
+ spec = definition()
380
+ spec["cost"] = [
381
+ cost("mps", 10, {"variable": "shots", "minutes": 3, "entries": 2})
382
+ ]
383
+ shots = [{"name": f"s{n}", "prompt": "x"} for n in range(5)]
384
+ answer = plan(spec, arguments={"shots": shots})["estimate"]
385
+ assert (answer["minutes"], answer["basis"]) == (19.0, "other_device")
386
+
387
+ def test_another_devices_figure_with_two_changed_lists_is_unknown(self, plan):
388
+ spec = definition()
389
+ spec["cost"] = [cost("mps", 40, name="M2")]
390
+ spec["variables"]["angles"] = [{"name": "wide"}]
391
+ spec["steps"].append(
392
+ {
393
+ "name": "angle",
394
+ "for_each": "variable:angles",
395
+ "task": {"command": "x", "arguments": {"name": "item:name"}},
396
+ }
397
+ )
398
+ arguments = {
399
+ "shots": [{"name": f"s{n}", "prompt": "x"} for n in range(4)],
400
+ "angles": [{"name": "wide"}, {"name": "tight"}],
401
+ }
402
+ answer = plan(spec, arguments=arguments)["estimate"]
403
+ assert (answer["minutes"], answer["basis"]) == (None, "unknown")
376
404
 
377
405
  def test_minutes_is_rounded_to_one_decimal(self, plan):
378
406
  spec = definition()
@@ -389,6 +417,15 @@ class TestEstimate:
389
417
  answer = plan(spec, arguments={"frames": 9})["estimate"]
390
418
  assert (answer["minutes"], answer["basis"]) == (None, "unknown")
391
419
 
420
+ def test_a_shifted_scalar_driver_is_unknown_on_another_device_too(self, plan):
421
+ """#589: the other_device branch skipped the #267 check, so a 241-frame
422
+ run was quoted the 121-frame clip's figure."""
423
+ spec = definition()
424
+ spec["cost"] = [cost("mps", 40, name="M2")]
425
+ spec["cost_drivers"] = ["frames"]
426
+ answer = plan(spec, arguments={"frames": 9})["estimate"]
427
+ assert (answer["minutes"], answer["basis"]) == (None, "unknown")
428
+
392
429
  def test_an_unshifted_scalar_cost_driver_still_quotes_the_catalog_figure(
393
430
  self, plan
394
431
  ):
@@ -407,6 +444,35 @@ class TestEstimate:
407
444
  answer = plan(spec, arguments={"shots": shots})["estimate"]
408
445
  assert (answer["minutes"], answer["basis"]) == (50.0, "derived")
409
446
 
447
+ def test_a_shifted_per_entry_field_in_a_list_driver_is_unknown(self, plan):
448
+ """#593: a per-shot num_frames outside the values the default entries
449
+ were measured with has no matching bucket, same as a scalar (#267)."""
450
+ spec = definition()
451
+ spec["cost"] = [cost("mps", 40, name="M2")]
452
+ spec["cost_drivers"] = ["shots"]
453
+ spec["variables"]["shots"] = [
454
+ {"name": "a", "prompt": "p", "num_frames": 124},
455
+ {"name": "b", "prompt": "p", "num_frames": 141},
456
+ ]
457
+ same = [
458
+ {"name": "x", "prompt": "p", "num_frames": 124},
459
+ {"name": "y", "prompt": "p", "num_frames": 124},
460
+ ]
461
+ long = [
462
+ {"name": "x", "prompt": "p", "num_frames": 243},
463
+ {"name": "y", "prompt": "p", "num_frames": 243},
464
+ ]
465
+ kept = plan(spec, arguments={"shots": same})["estimate"]
466
+ assert (kept["minutes"], kept["basis"]) == (40.0, "other_device")
467
+ moved = plan(spec, arguments={"shots": long})["estimate"]
468
+ assert (moved["minutes"], moved["basis"]) == (None, "unknown")
469
+ as_text = [dict(entry, num_frames=str(entry["num_frames"])) for entry in long]
470
+ moved = plan(spec, arguments={"shots": as_text})["estimate"]
471
+ assert (moved["minutes"], moved["basis"]) == (None, "unknown")
472
+ same_text = [dict(entry, num_frames="124") for entry in same]
473
+ kept = plan(spec, arguments={"shots": same_text})["estimate"]
474
+ assert (kept["minutes"], kept["basis"]) == (40.0, "other_device")
475
+
410
476
  def test_an_undeclared_variable_shift_is_not_a_driver_shift(self, plan):
411
477
  """Only a declared cost_driver triggers the fallback - any other
412
478
  variable overridden away from its default is none of this rule's
@@ -580,6 +646,17 @@ class TestSubWorkflowEstimate:
580
646
  assert (answer["minutes"], answer["partial"]) == (7.0, False)
581
647
  assert answer["unpriced"] == []
582
648
 
649
+ def test_a_number_never_carries_the_unknown_basis(self, plan, tmp_path):
650
+ """#593: an uncosted parent that only composes priced children sums
651
+ to a figure, which takes the children's basis rather than `unknown`."""
652
+ child = {"id": "child", "cost": [cost("cuda", 5)], "steps": []}
653
+ (tmp_path / "child.json").write_text(json.dumps(child))
654
+ parent = composing("child.json")
655
+ del parent["cost"]
656
+ parent["steps"] = parent["steps"][1:]
657
+ answer = plan(parent)["estimate"]
658
+ assert (answer["minutes"], answer["basis"]) == (5.0, "catalog")
659
+
583
660
  def test_a_child_without_a_cost_makes_the_estimate_partial(self, plan, tmp_path):
584
661
  (tmp_path / "child.json").write_text(json.dumps({"id": "child", "steps": []}))
585
662
  answer = plan(composing("child.json"))["estimate"]