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
dw_mcp/workspaces.py ADDED
@@ -0,0 +1,212 @@
1
+ """Which of the server's workspaces this session works in.
2
+
3
+ A server holds several - each with its own workflows, assets and outputs,
4
+ all sharing one prompt library - and several agents can work against one
5
+ server without sharing a namespace. The selection is a session default
6
+ rather than a parameter on every tool: switching is then one visible call in
7
+ the transcript instead of an argument that can be forgotten on the one call
8
+ where it mattered.
9
+ """
10
+
11
+ import logging
12
+
13
+ from dw_mcp.client import DEFAULT_WORKSPACE, DwApiError, api_path
14
+ from dw_mcp import catalog
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ # What a compact workspace entry keeps: enough to choose one or judge its
20
+ # size, nothing that only naming its folders needs
21
+ WORKSPACE_SUMMARY_FIELDS = ("name", "default", "usage")
22
+
23
+ # dw.serve --mcp builds one DwClient for every connected agent (#298) - the
24
+ # pin is server-global there, not per-session, and that is a deliberate scope
25
+ # decision (this server is single-user) rather than a bug to fix with session
26
+ # tracking. The one thing owed to a caller is honesty: when the mounted
27
+ # client's pin is about to move away from a workspace that was not the
28
+ # default, warn - it may be this same caller switching again, or it may be
29
+ # another connected agent's work about to lose its isolation
30
+ _CONCURRENT_PIN_WARNING = (
31
+ "This server's MCP mount shares workspace state across every connected "
32
+ "client (dw.serve --mcp has no per-session pin). The pin was "
33
+ "'{previous}' and is now '{name}' - if another agent is also connected, "
34
+ "its calls saw the workspace change too, and its next switch will "
35
+ "change what this session sees. Pass workspace='{name}' on calls that "
36
+ "accept it if you need isolation from other clients."
37
+ )
38
+
39
+
40
+ def _switch(client, name):
41
+ """Set client.workspace to `name`, returning a warning string when the
42
+ mounted (shared) client is about to overwrite a pin already in use for
43
+ something other than the default - see `_CONCURRENT_PIN_WARNING`."""
44
+ previous = client.workspace
45
+ warning = None
46
+ if client.mounted and previous != DEFAULT_WORKSPACE and previous != name:
47
+ warning = _CONCURRENT_PIN_WARNING.format(previous=previous, name=name)
48
+ logger.warning(
49
+ "mcp workspace pin changed from %r to %r on the shared mounted "
50
+ "client - another connected agent may be affected",
51
+ previous,
52
+ name,
53
+ )
54
+ client.workspace = name
55
+ return warning
56
+
57
+
58
+ def list_workspaces(client, detail=False):
59
+ """The workspaces on the server, and which one this session is using.
60
+
61
+ Compact by default - each entry cut to `name`, `default` and `usage`
62
+ (files/bytes) - because a server that has accumulated dozens of
63
+ workspaces across episodes and regression runs answers in the tens of
64
+ KB otherwise for a question that is usually "which workspaces exist and
65
+ how big are they" (#249). Pass `detail=True` for each entry's full
66
+ folder paths (`root`, `workflows`, `assets`, `outputs`, `prompts`,
67
+ `common_assets`), needed before naming one to `use_workspace` or a
68
+ remote read.
69
+ """
70
+ result = client.get_json("/api/workspaces")
71
+ if not detail:
72
+ entries = result.get("workspaces")
73
+ if isinstance(entries, list):
74
+ result = {
75
+ **result,
76
+ "workspaces": [
77
+ {
78
+ key: entry.get(key)
79
+ for key in WORKSPACE_SUMMARY_FIELDS
80
+ if key in entry
81
+ }
82
+ for entry in entries
83
+ if isinstance(entry, dict)
84
+ ],
85
+ "note": (
86
+ "Compact: each entry is name/default/usage only. Call "
87
+ "list_workspaces(detail=True) for full folder paths."
88
+ ),
89
+ }
90
+ return {**result, "current": client.workspace}
91
+
92
+
93
+ def use_workspace(client, name):
94
+ """Work in a different workspace for the rest of this session.
95
+
96
+ Checked against the server before it takes effect: a typo that silently
97
+ scoped every later call to a workspace that does not exist would fail
98
+ one call at a time, far from its cause.
99
+ """
100
+ name = name or DEFAULT_WORKSPACE
101
+ listing = client.get_json("/api/workspaces")
102
+ entries = listing.get("workspaces")
103
+ # Membership is checked when the server said what it has. A listing that
104
+ # does not say is not grounds to refuse: the request reached the server,
105
+ # and a name it does not know will be refused by the next call that uses
106
+ # it - with the name in the message, which is what matters
107
+ known = (
108
+ [entry["name"] for entry in entries if isinstance(entry, dict)]
109
+ if isinstance(entries, list)
110
+ else None
111
+ )
112
+ if known and name not in known:
113
+ raise DwApiError(
114
+ f"No workspace named '{name}' on this server. It has: "
115
+ f"{', '.join(known)}. Create one with create_workspace."
116
+ )
117
+ warning = _switch(client, name)
118
+ result = {"current": name, "workspaces": known or [name]}
119
+ if warning:
120
+ result["warning"] = warning
121
+ return result
122
+
123
+
124
+ def create_workspace(client, name, use=False):
125
+ """Make a new workspace on the server. It gets its own workflows, assets
126
+ and outputs, and shares the server's one prompt library. Creating it
127
+ does not switch to it unless `use` is true - the natural
128
+ create-then-run sequence otherwise runs in the workspace the session
129
+ was already in, and the result says which that is."""
130
+ body = client.post_json("/api/workspaces", {"name": name})
131
+ warning = _switch(client, name) if use else None
132
+ result = {
133
+ **body,
134
+ "current": client.workspace,
135
+ "next": (
136
+ f"This session now works in '{client.workspace}'."
137
+ if use
138
+ else f"This session still works in '{client.workspace}' - call "
139
+ f"use_workspace('{name}') or pass workspace='{name}' to "
140
+ f"run_workflow before running anything meant for it."
141
+ ),
142
+ }
143
+ if warning:
144
+ result["warning"] = warning
145
+ return result
146
+
147
+
148
+ def delete_workspace(client, name, acknowledged_cost=False):
149
+ """Delete a workspace and every workflow, asset and generated file in it.
150
+
151
+ Refuses without `acknowledged_cost=True`, and reports what it would
152
+ remove instead - a count of files is what makes this an informed choice
153
+ rather than a surprise.
154
+ """
155
+ if not acknowledged_cost:
156
+ # Unacknowledged, the server answers 409 with what it would remove -
157
+ # which the client turns into the error text below, so the count
158
+ # reaches the caller rather than a bare refusal. A name that fails
159
+ # for another reason (404 not found, 400 for the default workspace)
160
+ # never reaches that 409, and acknowledging cannot fix it - the
161
+ # acknowledgement sentence belongs only on the 409
162
+ try:
163
+ client.delete_json(api_path("api", "workspaces", name))
164
+ except DwApiError as e:
165
+ if e.status_code == 409:
166
+ raise DwApiError(
167
+ f"{e} Call delete_workspace again with "
168
+ f"acknowledged_cost=True to proceed."
169
+ )
170
+ raise
171
+ raise DwApiError(
172
+ f"Deleting workspace '{name}' removes everything in it. Call "
173
+ f"again with acknowledged_cost=True to proceed."
174
+ )
175
+ result = client.delete_json(
176
+ api_path("api", "workspaces", name), params={"acknowledged": "true"}
177
+ )
178
+ if client.workspace == name:
179
+ client.workspace = DEFAULT_WORKSPACE
180
+ return {**result, "current": client.workspace}
181
+
182
+
183
+ def server_info(client):
184
+ """What this installation can do and where it keeps things: the
185
+ accelerator, version, directories, and which workspace this session works
186
+ in. When the session is in a named workspace, directories are scoped to
187
+ that workspace rather than the server's default.
188
+ """
189
+ info = catalog.get_server_info(client)
190
+
191
+ # /api/server describes the server's default workspace. A session in a
192
+ # named one is told where *its* folders are, so a path it is handed
193
+ # back is relative to the right place. The root's other keys (the
194
+ # workspace root itself) stay
195
+ if client.workspace != DEFAULT_WORKSPACE:
196
+ listing = client.get_json("/api/workspaces")
197
+ for workspace in listing.get("workspaces") or []:
198
+ if (
199
+ isinstance(workspace, dict)
200
+ and workspace.get("name") == client.workspace
201
+ ):
202
+ info["directories"] = {
203
+ **(info.get("directories") or {}),
204
+ **{
205
+ key: workspace.get(key)
206
+ for key in ("workflows", "assets", "outputs", "prompts")
207
+ },
208
+ }
209
+ break
210
+
211
+ info["workspace"] = client.workspace
212
+ return info