diffusers-workflow 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. diffusers_workflow-0.4.0.dist-info/METADATA +318 -0
  2. diffusers_workflow-0.4.0.dist-info/RECORD +260 -0
  3. diffusers_workflow-0.4.0.dist-info/WHEEL +5 -0
  4. diffusers_workflow-0.4.0.dist-info/entry_points.txt +7 -0
  5. diffusers_workflow-0.4.0.dist-info/licenses/LICENSE +201 -0
  6. diffusers_workflow-0.4.0.dist-info/top_level.txt +2 -0
  7. dw/__init__.py +440 -0
  8. dw/adapter_compatibility.py +226 -0
  9. dw/arguments.py +1231 -0
  10. dw/assessment_rules.py +159 -0
  11. dw/assets.py +130 -0
  12. dw/cache_blocks.json +16 -0
  13. dw/cache_blocks.py +146 -0
  14. dw/community_pipelines/pipeline_flux_rf_inversion.py +1184 -0
  15. dw/content_types.py +150 -0
  16. dw/dissolve_frame_errors.py +121 -0
  17. dw/docs/ACCELERATION.md +352 -0
  18. dw/docs/AGENT_LOOP.md +95 -0
  19. dw/docs/DEPENDENCIES.md +91 -0
  20. dw/docs/IP_ADAPTER.md +109 -0
  21. dw/docs/LORAS.md +131 -0
  22. dw/docs/MCP.md +517 -0
  23. dw/docs/PROMPT_WEIGHTING.md +78 -0
  24. dw/docs/QUANTIZATION.md +230 -0
  25. dw/docs/RECIPES_24GB.md +201 -0
  26. dw/docs/RELEASING.md +195 -0
  27. dw/docs/REMOTE.md +140 -0
  28. dw/docs/REPL_COMMANDS.md +121 -0
  29. dw/docs/REPL_WORKER_GUIDE.md +51 -0
  30. dw/docs/SECURITY.md +272 -0
  31. dw/docs/SECURITY_QUICKREF.md +112 -0
  32. dw/docs/SERVER.md +679 -0
  33. dw/docs/TASKS.md +1741 -0
  34. dw/docs/TESTING.md +71 -0
  35. dw/docs/WORKFLOW_GUIDE.md +2038 -0
  36. dw/docs/WORKSPACES.md +316 -0
  37. dw/download_watch.py +335 -0
  38. dw/elision.py +306 -0
  39. dw/events.py +275 -0
  40. dw/for_each.py +409 -0
  41. dw/host_memory.py +258 -0
  42. dw/host_memory_projection.py +230 -0
  43. dw/hub_cache.py +432 -0
  44. dw/introspection.py +1228 -0
  45. dw/kernel_availability.py +208 -0
  46. dw/locations.py +599 -0
  47. dw/log_setup.py +45 -0
  48. dw/loudness.py +82 -0
  49. dw/media_audio.py +217 -0
  50. dw/media_frames.py +367 -0
  51. dw/media_info.py +297 -0
  52. dw/pipeline_processors/chain.py +821 -0
  53. dw/pipeline_processors/config_objects.py +237 -0
  54. dw/pipeline_processors/pipeline.py +2297 -0
  55. dw/pipeline_processors/remote.py +46 -0
  56. dw/plan.py +920 -0
  57. dw/previous_results.py +411 -0
  58. dw/probe_paths.py +59 -0
  59. dw/prompt_schema.json +48 -0
  60. dw/prompt_weighting.py +378 -0
  61. dw/prompts.py +159 -0
  62. dw/realize.py +250 -0
  63. dw/reference_limits.py +215 -0
  64. dw/reference_names.py +125 -0
  65. dw/repl.py +338 -0
  66. dw/repl_commands.py +836 -0
  67. dw/repl_worker.py +159 -0
  68. dw/result.py +1720 -0
  69. dw/result_fps.py +82 -0
  70. dw/run.py +162 -0
  71. dw/runs.py +768 -0
  72. dw/scalar_result_validation.py +97 -0
  73. dw/schema.py +283 -0
  74. dw/security.py +1038 -0
  75. dw/select_validation.py +115 -0
  76. dw/serve.py +277 -0
  77. dw/server/__init__.py +2 -0
  78. dw/server/app.py +4586 -0
  79. dw/server/assess.py +132 -0
  80. dw/server/catalog_shape.py +487 -0
  81. dw/server/enhancers.py +129 -0
  82. dw/server/exports.py +480 -0
  83. dw/server/guides.py +257 -0
  84. dw/server/jobs.py +1561 -0
  85. dw/server/mcp_mount.py +95 -0
  86. dw/server/netinfo.py +124 -0
  87. dw/server/observed_cost.py +379 -0
  88. dw/server/sysinfo.py +71 -0
  89. dw/server/ui/assets/abap-08VXUWAP.js +1 -0
  90. dw/server/ui/assets/apex-BWPQTe0t.js +1 -0
  91. dw/server/ui/assets/azcli-Bc_sGQ0U.js +1 -0
  92. dw/server/ui/assets/bat-i0X4ZdIN.js +1 -0
  93. dw/server/ui/assets/bicep-B5-_aFwp.js +2 -0
  94. dw/server/ui/assets/cameligo-DMUM7wLl.js +1 -0
  95. dw/server/ui/assets/clojure-Cm7r79vr.js +1 -0
  96. dw/server/ui/assets/codicon-Brq4_Ui5.ttf +0 -0
  97. dw/server/ui/assets/coffee-Ba7i2nA0.js +1 -0
  98. dw/server/ui/assets/cpp-C7h46wYY.js +1 -0
  99. dw/server/ui/assets/csharp-BKxtCVv1.js +1 -0
  100. dw/server/ui/assets/csp-bTuwJoIa.js +1 -0
  101. dw/server/ui/assets/css-DIMkf-bt.js +3 -0
  102. dw/server/ui/assets/css.worker-B3ciXF_0.js +93 -0
  103. dw/server/ui/assets/cssMode-CPznxfY8.js +1 -0
  104. dw/server/ui/assets/cypher-CVaqCwHa.js +1 -0
  105. dw/server/ui/assets/dart-onAF5SnQ.js +1 -0
  106. dw/server/ui/assets/dockerfile-DZFCIeNp.js +1 -0
  107. dw/server/ui/assets/ecl-D05T4iGw.js +1 -0
  108. dw/server/ui/assets/editor-jjEx9u7D.css +1 -0
  109. dw/server/ui/assets/editor.api-CpWcotrd.js +847 -0
  110. dw/server/ui/assets/editor.worker-q-txB4vs.js +30 -0
  111. dw/server/ui/assets/elixir-6RTg0lbw.js +1 -0
  112. dw/server/ui/assets/flow9-C5_-GSwl.js +1 -0
  113. dw/server/ui/assets/freemarker2-CXtRM8N4.js +3 -0
  114. dw/server/ui/assets/fsharp-C8Ef5oNN.js +1 -0
  115. dw/server/ui/assets/go-C-y9NEjX.js +1 -0
  116. dw/server/ui/assets/graphql-fmXr3nnJ.js +1 -0
  117. dw/server/ui/assets/handlebars-N7x-6NMY.js +1 -0
  118. dw/server/ui/assets/hcl-CpzslTdj.js +1 -0
  119. dw/server/ui/assets/html-PhsdjHSr.js +1 -0
  120. dw/server/ui/assets/html.worker-C93Ht9o9.js +506 -0
  121. dw/server/ui/assets/htmlMode-Dgj0SEok.js +1 -0
  122. dw/server/ui/assets/index-3Vw6WAPW.css +1 -0
  123. dw/server/ui/assets/index-DgrYhQd9.js +43 -0
  124. dw/server/ui/assets/ini-sBoK_t0W.js +1 -0
  125. dw/server/ui/assets/java-BEtHBSE6.js +1 -0
  126. dw/server/ui/assets/javascript-BJqN9Qhv.js +1 -0
  127. dw/server/ui/assets/json.worker-B2V3pomh.js +62 -0
  128. dw/server/ui/assets/jsonMode-DbM4SWSv.js +7 -0
  129. dw/server/ui/assets/julia-Bri6UV-V.js +1 -0
  130. dw/server/ui/assets/kotlin-BOotOW0E.js +1 -0
  131. dw/server/ui/assets/less-B9JPFI3C.js +2 -0
  132. dw/server/ui/assets/lexon-CfSJPG6W.js +1 -0
  133. dw/server/ui/assets/liquid-BWr8lEc4.js +1 -0
  134. dw/server/ui/assets/lspLanguageFeatures-C1iGuDyZ.js +4 -0
  135. dw/server/ui/assets/lua-CsQS60Ue.js +1 -0
  136. dw/server/ui/assets/m3-D-oSqn_W.js +1 -0
  137. dw/server/ui/assets/markdown-Cimd5fb3.js +1 -0
  138. dw/server/ui/assets/mdx-DAdMi_0p.js +1 -0
  139. dw/server/ui/assets/mips-CIPQ_RoX.js +1 -0
  140. dw/server/ui/assets/monaco--ixms01u.css +1 -0
  141. dw/server/ui/assets/monaco-BGCeEqaw.js +56 -0
  142. dw/server/ui/assets/msdax-DauUninz.js +1 -0
  143. dw/server/ui/assets/mysql-SOo6toE5.js +1 -0
  144. dw/server/ui/assets/objective-c-FvmIjYaQ.js +1 -0
  145. dw/server/ui/assets/pascal-DrH0SRf2.js +1 -0
  146. dw/server/ui/assets/pascaligo-D-ptJ9y-.js +1 -0
  147. dw/server/ui/assets/perl-oz_6vUea.js +1 -0
  148. dw/server/ui/assets/pgsql-DTj74zXo.js +1 -0
  149. dw/server/ui/assets/php-nr791fC2.js +1 -0
  150. dw/server/ui/assets/pla-CopQ2nXW.js +1 -0
  151. dw/server/ui/assets/postiats-43DmfD33.js +1 -0
  152. dw/server/ui/assets/powerquery-D3hlyOfw.js +1 -0
  153. dw/server/ui/assets/powershell-DmHpPYUd.js +1 -0
  154. dw/server/ui/assets/protobuf-C531GsRP.js +2 -0
  155. dw/server/ui/assets/pug-Z5eAx3Zn.js +1 -0
  156. dw/server/ui/assets/python-Bcn70HdC.js +1 -0
  157. dw/server/ui/assets/qsharp-DkqhCAOL.js +1 -0
  158. dw/server/ui/assets/r-BwWrilGY.js +1 -0
  159. dw/server/ui/assets/razor-D1HmNnby.js +1 -0
  160. dw/server/ui/assets/redis-ClamHrr6.js +1 -0
  161. dw/server/ui/assets/redshift-DT7zqm-g.js +1 -0
  162. dw/server/ui/assets/restructuredtext-BYgofb2h.js +1 -0
  163. dw/server/ui/assets/ruby-DezsRK8O.js +1 -0
  164. dw/server/ui/assets/rust-DdL9SqIa.js +1 -0
  165. dw/server/ui/assets/sb-CcwsVR0C.js +1 -0
  166. dw/server/ui/assets/scala-DHpiXF5c.js +1 -0
  167. dw/server/ui/assets/scheme-BeGwcela.js +1 -0
  168. dw/server/ui/assets/scss-gp-XZpBa.js +3 -0
  169. dw/server/ui/assets/shell-CC2rA5mh.js +1 -0
  170. dw/server/ui/assets/solidity-BEEn4gHE.js +1 -0
  171. dw/server/ui/assets/sophia-CRfGWb83.js +1 -0
  172. dw/server/ui/assets/sparql-D_Lu-MrJ.js +1 -0
  173. dw/server/ui/assets/sql-NEE52Syq.js +1 -0
  174. dw/server/ui/assets/st-DbInun42.js +1 -0
  175. dw/server/ui/assets/swift-Bxkupp3x.js +1 -0
  176. dw/server/ui/assets/systemverilog-Bz4Y3fRF.js +1 -0
  177. dw/server/ui/assets/tcl-DISqw1ZD.js +1 -0
  178. dw/server/ui/assets/ts.worker-D7T1-Ig5.js +67738 -0
  179. dw/server/ui/assets/tsMode-D6u0XmOW.js +11 -0
  180. dw/server/ui/assets/twig-De2hgUGE.js +1 -0
  181. dw/server/ui/assets/typescript-BU6v-LMV.js +1 -0
  182. dw/server/ui/assets/typespec-B8J7ngcE.js +1 -0
  183. dw/server/ui/assets/vb-DV3o63ZY.js +1 -0
  184. dw/server/ui/assets/wgsl-DpFanUEy.js +298 -0
  185. dw/server/ui/assets/workers-Cn7cTUKr.js +1 -0
  186. dw/server/ui/assets/xml--0LP2Lwk.js +1 -0
  187. dw/server/ui/assets/yaml-mpBg9jnt.js +1 -0
  188. dw/server/ui/index.html +17 -0
  189. dw/server/updater.py +192 -0
  190. dw/settings.py +98 -0
  191. dw/shot_span_preflight.py +116 -0
  192. dw/shots.py +359 -0
  193. dw/slice_preflight.py +148 -0
  194. dw/step.py +187 -0
  195. dw/step_cache.py +442 -0
  196. dw/subfolders.py +107 -0
  197. dw/task_domains.py +307 -0
  198. dw/tasks/assess.py +826 -0
  199. dw/tasks/audio_transcription.py +88 -0
  200. dw/tasks/audio_utils.py +1862 -0
  201. dw/tasks/background_remover.py +43 -0
  202. dw/tasks/borders.py +113 -0
  203. dw/tasks/compose_text.py +74 -0
  204. dw/tasks/concat_videos.py +300 -0
  205. dw/tasks/depth_estimator.py +54 -0
  206. dw/tasks/diffusion_upscale.py +109 -0
  207. dw/tasks/dissolve_videos.py +342 -0
  208. dw/tasks/format_messages.py +24 -0
  209. dw/tasks/gather.py +173 -0
  210. dw/tasks/grade.py +97 -0
  211. dw/tasks/image_to_text.py +43 -0
  212. dw/tasks/image_utils.py +764 -0
  213. dw/tasks/interpolate_frames.py +252 -0
  214. dw/tasks/judge.py +68 -0
  215. dw/tasks/model_cache.py +55 -0
  216. dw/tasks/pair_audio.py +268 -0
  217. dw/tasks/qr_code.py +19 -0
  218. dw/tasks/restore_faces.py +175 -0
  219. dw/tasks/rife_model.py +192 -0
  220. dw/tasks/segment.py +121 -0
  221. dw/tasks/select.py +111 -0
  222. dw/tasks/speech_generation.py +228 -0
  223. dw/tasks/stabilize.py +129 -0
  224. dw/tasks/task.py +920 -0
  225. dw/tasks/tensor_image.py +57 -0
  226. dw/tasks/text_generation.py +169 -0
  227. dw/tasks/text_sections.py +80 -0
  228. dw/tasks/upscale.py +203 -0
  229. dw/tasks/video_utils.py +624 -0
  230. dw/tasks/zoe_depth.py +71 -0
  231. dw/teacache.py +381 -0
  232. dw/teacache_models.json +99 -0
  233. dw/test.py +29 -0
  234. dw/type_helpers.py +231 -0
  235. dw/validate.py +68 -0
  236. dw/variable_constraints.py +444 -0
  237. dw/variables.py +443 -0
  238. dw/video_extensions.py +141 -0
  239. dw/vram_estimate.py +116 -0
  240. dw/worker.py +764 -0
  241. dw/workflow.py +2007 -0
  242. dw/workflow_schema.json +1346 -0
  243. dw/workflow_sources.py +383 -0
  244. dw/workflows/h3_context_ir.json +57 -0
  245. dw/workflows/test.json +31 -0
  246. dw/workspace.py +730 -0
  247. dw_mcp/__init__.py +6 -0
  248. dw_mcp/__main__.py +133 -0
  249. dw_mcp/assets.py +336 -0
  250. dw_mcp/authoring.py +114 -0
  251. dw_mcp/catalog.py +360 -0
  252. dw_mcp/client.py +486 -0
  253. dw_mcp/diagnose.py +371 -0
  254. dw_mcp/exports.py +84 -0
  255. dw_mcp/guides.py +35 -0
  256. dw_mcp/media.py +638 -0
  257. dw_mcp/models.py +97 -0
  258. dw_mcp/prompts.py +104 -0
  259. dw_mcp/server.py +1343 -0
  260. dw_mcp/workspaces.py +212 -0
@@ -0,0 +1,226 @@
1
+ """The LoRA a step loads has to be trained for the path that step runs.
2
+
3
+ MiniMax-H3 ships one repository holding two checkpoint partitions -
4
+ `transformer/` for the `t2va` and `fl2va` workflows, `transformer_ref/` for
5
+ `ref2va` - and a separate turbo adapter trained against each. Handing the
6
+ FL2VA adapter to a `ref2va` step is the one H3 misconfiguration that never
7
+ shows up as a failure: a `ref2va` step holds `transformer_ref` alone, so
8
+ diffusers loads whatever it is given straight onto it, the run succeeds, and
9
+ the only symptom is identity retention that quietly is not as good as it
10
+ should be. Four templates carried exactly that mistake and nothing ever
11
+ complained (#149, #155).
12
+
13
+ Everything a pipeline itself would refuse is refused by the pipeline. This
14
+ is the other kind: a request the pipeline accepts and answers wrongly, which
15
+ costs a whole render to discover and cannot be seen in the output. So it is
16
+ checked where it is free - `validation_errors`, so `POST /api/validate` and
17
+ the pre-queue check both refuse it, at the JSON path the value sits at.
18
+
19
+ What is *not* written here: the adapter file names. `lora_weight_name` is a
20
+ free string and a future reference-trained checkpoint cannot be predicted, so
21
+ an unrecognised name is a warning naming the rule rather than a refusal - the
22
+ default inverts from "anything passes" to "the one documented mistake is
23
+ caught", and someone testing `my-new-ref-lora.safetensors` still gets
24
+ through. What *is* written here is a vendor naming convention (`ref2v` /
25
+ `fl2v` in the file name) that no diffusers symbol declares; the workflow
26
+ names and the partition each one denoises against are diffusers' own, and
27
+ `tests/test_h3_adapters.py` pins them to it.
28
+ """
29
+
30
+ import logging
31
+
32
+ from .for_each import MEMBER_SEPARATOR, render_path
33
+
34
+ logger = logging.getLogger("dw")
35
+
36
+ # The `workflow=` values MiniMaxH3Blocks._workflow_map declares, split by the
37
+ # transformer partition each one denoises against - a `ref2va` step loads
38
+ # `transformer_ref` and nothing else, every other H3 workflow loads
39
+ # `transformer`. A workflow name not listed here is not checked
40
+ REFERENCE_WORKFLOWS = frozenset({"ref2va"})
41
+ KEYFRAME_WORKFLOWS = frozenset({"t2va", "fl2va"})
42
+ H3_WORKFLOWS = REFERENCE_WORKFLOWS | KEYFRAME_WORKFLOWS
43
+
44
+ # The token MiniMax puts in an adapter's file name to say which partition it
45
+ # was trained against. `ref2v` first: it is the longer match and a name
46
+ # carrying both is ambiguous rather than a keyframe adapter
47
+ REFERENCE_TOKEN = "ref2v"
48
+ KEYFRAME_TOKEN = "fl2v"
49
+
50
+ LORAS_KEY = "loras"
51
+ WEIGHT_NAME_KEY = "weight_name"
52
+ WORKFLOW_KEY = "workflow"
53
+ FROM_PRETRAINED_KEY = "from_pretrained_arguments"
54
+ VARIABLE_PREFIX = "variable:"
55
+ # Values another pass resolves; one still spelled out here is not this
56
+ # pass's complaint
57
+ _UNRESOLVED_PREFIXES = ("variable:", "item:", "previous_result:", "gather:")
58
+
59
+
60
+ def _trained_for(weight_name):
61
+ """Which partition an adapter's file name says it was trained against -
62
+ 'reference', 'keyframe', or None when the name says neither."""
63
+ lowered = weight_name.lower()
64
+ if REFERENCE_TOKEN in lowered:
65
+ return "reference"
66
+ if KEYFRAME_TOKEN in lowered:
67
+ return "keyframe"
68
+ return None
69
+
70
+
71
+ def _problem(workflow, weight_name):
72
+ """(severity, message) for one adapter on one step, or None when the
73
+ pairing is right."""
74
+ trained = _trained_for(weight_name)
75
+ wants = "reference" if workflow in REFERENCE_WORKFLOWS else "keyframe"
76
+ if trained == wants:
77
+ return None
78
+ if trained is None:
79
+ return (
80
+ "warning",
81
+ f"'{weight_name}' names neither '{REFERENCE_TOKEN}' nor "
82
+ f"'{KEYFRAME_TOKEN}', so it cannot be checked against this "
83
+ f"step's '{workflow}' workflow. A '{workflow}' step denoises "
84
+ f"against the "
85
+ f"'{'transformer_ref' if wants == 'reference' else 'transformer'}' "
86
+ f"partition, and an adapter trained against the other one loads "
87
+ f"onto it without error and only degrades the result",
88
+ )
89
+ return (
90
+ "error",
91
+ f"'{weight_name}' is trained against the "
92
+ f"'{'transformer_ref' if trained == 'reference' else 'transformer'}' "
93
+ f"partition and this step runs the '{workflow}' workflow, which "
94
+ f"denoises against "
95
+ f"'{'transformer_ref' if wants == 'reference' else 'transformer'}'. "
96
+ f"MiniMax-H3 loads it anyway and the run succeeds - the only symptom "
97
+ f"is a worse result - so it is refused here. Use an adapter trained "
98
+ f"for this workflow (its name carries "
99
+ f"'{REFERENCE_TOKEN if wants == 'reference' else KEYFRAME_TOKEN}')",
100
+ )
101
+
102
+
103
+ def _lora_problems(steps, source_indices, written=None, supplied=()):
104
+ """Every adapter/workflow mismatch in an expanded step list, as
105
+ (severity, path, message).
106
+
107
+ `written` is the definition as the author wrote it and `supplied` the
108
+ argument names the caller passed: when the value arrived through a
109
+ variable the caller set, the path reported is `arguments.<name>`, where
110
+ they wrote it, rather than the step it landed in.
111
+ """
112
+ written_steps = (written or {}).get("steps")
113
+ found = []
114
+ for index, step in enumerate(steps):
115
+ if not isinstance(step, dict):
116
+ continue
117
+ pipeline = step.get("pipeline")
118
+ if not isinstance(pipeline, dict):
119
+ continue
120
+ source = (
121
+ source_indices[index]
122
+ if source_indices is not None and index < len(source_indices)
123
+ else index
124
+ )
125
+ from_pretrained = pipeline.get(FROM_PRETRAINED_KEY)
126
+ workflow = (
127
+ from_pretrained.get(WORKFLOW_KEY)
128
+ if isinstance(from_pretrained, dict)
129
+ else None
130
+ )
131
+ if workflow not in H3_WORKFLOWS:
132
+ continue
133
+ loras = pipeline.get(LORAS_KEY)
134
+ if not isinstance(loras, list):
135
+ continue
136
+ name = step.get("name")
137
+ where = (
138
+ f" in member '{name}'"
139
+ if isinstance(name, str) and MEMBER_SEPARATOR in name
140
+ else ""
141
+ )
142
+ for position, lora in enumerate(loras):
143
+ if not isinstance(lora, dict):
144
+ continue
145
+ weight_name = lora.get(WEIGHT_NAME_KEY)
146
+ if not isinstance(weight_name, str) or weight_name.startswith(
147
+ _UNRESOLVED_PREFIXES
148
+ ):
149
+ continue
150
+ problem = _problem(workflow, weight_name)
151
+ if problem is None:
152
+ continue
153
+ severity, message = problem
154
+ found.append(
155
+ (
156
+ severity,
157
+ _path_for(written_steps, source, position, supplied)
158
+ or render_path(
159
+ (
160
+ "steps",
161
+ source,
162
+ "pipeline",
163
+ LORAS_KEY,
164
+ position,
165
+ WEIGHT_NAME_KEY,
166
+ )
167
+ ),
168
+ message + where,
169
+ )
170
+ )
171
+ return found
172
+
173
+
174
+ def _path_for(written_steps, source, position, supplied):
175
+ """`arguments.<name>` when the adapter came from a variable the caller
176
+ set, else None - the value is reported where it was written."""
177
+ if not isinstance(written_steps, list) or source >= len(written_steps):
178
+ return None
179
+ step = written_steps[source]
180
+ pipeline = step.get("pipeline") if isinstance(step, dict) else None
181
+ loras = pipeline.get(LORAS_KEY) if isinstance(pipeline, dict) else None
182
+ if not isinstance(loras, list) or position >= len(loras):
183
+ return None
184
+ entry = loras[position]
185
+ reference = entry.get(WEIGHT_NAME_KEY) if isinstance(entry, dict) else None
186
+ if not isinstance(reference, str) or not reference.startswith(VARIABLE_PREFIX):
187
+ return None
188
+ variable = reference.removeprefix(VARIABLE_PREFIX)
189
+ return f"arguments.{variable}" if variable in (supplied or ()) else None
190
+
191
+
192
+ def adapter_errors(workflow_definition, source_indices=None, written=None, supplied=()):
193
+ """Every adapter loaded onto the wrong checkpoint partition, as
194
+ [{path, message}]. The definition is the expanded, substituted one, so a
195
+ `for_each` member's own adapter is checked as it will run."""
196
+ return [
197
+ {"path": path, "message": message}
198
+ for severity, path, message in _lora_problems(
199
+ workflow_definition.get("steps") or [], source_indices, written, supplied
200
+ )
201
+ if severity == "error"
202
+ ]
203
+
204
+
205
+ def adapter_warnings(
206
+ workflow_definition, source_indices=None, written=None, supplied=()
207
+ ):
208
+ """Every adapter whose name says nothing about what it was trained for,
209
+ as messages - valid, and said out loud because nothing at run time will."""
210
+ return [
211
+ f"{path}: {message}"
212
+ for severity, path, message in _lora_problems(
213
+ workflow_definition.get("steps") or [], source_indices, written, supplied
214
+ )
215
+ if severity == "warning"
216
+ ]
217
+
218
+
219
+ def warn_adapters(workflow_definition):
220
+ """Say the unrecognised-adapter warning where whoever asked for the run
221
+ can read it - a run started from the CLI or a rerun never passed through
222
+ the validate route."""
223
+ from .events import emit_warning
224
+
225
+ for message in adapter_warnings(workflow_definition):
226
+ emit_warning(message, kind="adapter_unrecognized")