gradio-workflowcanvas 0.0.1__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 (27) hide show
  1. gradio_workflowcanvas-0.0.1/.gitignore +12 -0
  2. gradio_workflowcanvas-0.0.1/PKG-INFO +39 -0
  3. gradio_workflowcanvas-0.0.1/README.md +11 -0
  4. gradio_workflowcanvas-0.0.1/WORKFLOW_SPEC.md +450 -0
  5. gradio_workflowcanvas-0.0.1/app.py +133 -0
  6. gradio_workflowcanvas-0.0.1/backend/gradio_workflowcanvas/__init__.py +4 -0
  7. gradio_workflowcanvas-0.0.1/backend/gradio_workflowcanvas/workflowcanvas.py +380 -0
  8. gradio_workflowcanvas-0.0.1/demo/__init__.py +0 -0
  9. gradio_workflowcanvas-0.0.1/demo/app.py +120 -0
  10. gradio_workflowcanvas-0.0.1/demo/css.css +157 -0
  11. gradio_workflowcanvas-0.0.1/frontend/Index.svelte +22 -0
  12. gradio_workflowcanvas-0.0.1/frontend/gradio.config.js +9 -0
  13. gradio_workflowcanvas-0.0.1/frontend/package-lock.json +2753 -0
  14. gradio_workflowcanvas-0.0.1/frontend/package.json +34 -0
  15. gradio_workflowcanvas-0.0.1/frontend/tsconfig.json +14 -0
  16. gradio_workflowcanvas-0.0.1/frontend/workflow/WorkflowCanvas.svelte +1690 -0
  17. gradio_workflowcanvas-0.0.1/frontend/workflow/WorkflowEdges.svelte +212 -0
  18. gradio_workflowcanvas-0.0.1/frontend/workflow/WorkflowNode.svelte +1209 -0
  19. gradio_workflowcanvas-0.0.1/frontend/workflow/WorkflowSidebar.svelte +858 -0
  20. gradio_workflowcanvas-0.0.1/frontend/workflow/node-library.ts +256 -0
  21. gradio_workflowcanvas-0.0.1/frontend/workflow/space-api.ts +180 -0
  22. gradio_workflowcanvas-0.0.1/frontend/workflow/workflow-executor.ts +230 -0
  23. gradio_workflowcanvas-0.0.1/frontend/workflow/workflow-store.ts +153 -0
  24. gradio_workflowcanvas-0.0.1/frontend/workflow/workflow-to-space.ts +236 -0
  25. gradio_workflowcanvas-0.0.1/frontend/workflow/workflow-types.ts +99 -0
  26. gradio_workflowcanvas-0.0.1/pyproject.toml +50 -0
  27. gradio_workflowcanvas-0.0.1/requirements.txt +0 -0
@@ -0,0 +1,12 @@
1
+ .eggs/
2
+ dist/
3
+ *.pyc
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+ __tmp/*
8
+ *.pyi
9
+ .mypycache
10
+ .ruff_cache
11
+ node_modules
12
+ backend/**/templates/
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.4
2
+ Name: gradio-workflowcanvas
3
+ Version: 0.0.1
4
+ Summary: Visual workflow builder for connecting Hugging Face Spaces into AI pipelines
5
+ Project-URL: Repository, https://github.com/gradio-app/workflow-builder
6
+ Project-URL: Space, https://huggingface.co/spaces/hmb/workflow
7
+ Project-URL: Documentation, https://github.com/gradio-app/workflow-builder/blob/main/WORKFLOW_SPEC.md
8
+ Author-email: Gradio Team <hello@gradio.app>
9
+ License-Expression: Apache-2.0
10
+ Keywords: gradio-custom-component,gradio-template-HTML
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Topic :: Scientific/Engineering
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Scientific/Engineering :: Visualization
22
+ Requires-Python: >=3.8
23
+ Requires-Dist: gradio<7.0,>=6.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: build; extra == 'dev'
26
+ Requires-Dist: twine; extra == 'dev'
27
+ Description-Content-Type: text/markdown
28
+
29
+ ---
30
+ title: Workflow Builder
31
+ app_file: app.py
32
+ sdk: gradio
33
+ sdk_version: 6.12.0
34
+ hf_oauth: true
35
+ ---
36
+
37
+ # Workflow Builder
38
+
39
+ Build and run AI pipelines visually by connecting Hugging Face Spaces together.
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: Workflow Builder
3
+ app_file: app.py
4
+ sdk: gradio
5
+ sdk_version: 6.12.0
6
+ hf_oauth: true
7
+ ---
8
+
9
+ # Workflow Builder
10
+
11
+ Build and run AI pipelines visually by connecting Hugging Face Spaces together.
@@ -0,0 +1,450 @@
1
+ # Workflow Builder Specification
2
+
3
+ ## Overview
4
+
5
+ **Workflow Builder** is a visual, no-code platform for building AI pipelines by connecting Hugging Face Spaces together. Users design workflows by:
6
+ 1. Creating input/output nodes
7
+ 2. Dragging Spaces onto the canvas
8
+ 3. Wiring ports between nodes based on type compatibility
9
+ 4. Running the workflow end-to-end
10
+ 5. Exporting workflows as JSON for sharing
11
+
12
+ **Key Principle**: Workflows are first-class artifacts (JSON files). They're separate from any UI/deployment. A single workflow can power multiple vibenodes or integrations.
13
+
14
+ ---
15
+
16
+ ## Architecture
17
+
18
+ ### Frontend (TypeScript/Svelte)
19
+ - **WorkflowCanvas.svelte** (1690 lines) — main UI, toolbar, modals, pan/zoom, keyboard shortcuts
20
+ - **WorkflowNode.svelte** (1209 lines) — node rendering, inline widgets, ports, drag-and-drop
21
+ - **WorkflowEdges.svelte** (212 lines) — edge rendering with type badges, DOM-based port positioning
22
+ - **WorkflowSidebar.svelte** (858 lines) — component library, Space search, favorites, resizable
23
+ - **workflow-store.ts** (153 lines) — Svelte writable store, node/edge CRUD, undo/redo foundation
24
+ - **workflow-executor.ts** (230 lines) — parallel layer-based execution, client caching, retry logic
25
+ - **space-api.ts** (180 lines) — API introspection via `client.view_api()`, endpoint heuristics
26
+ - **node-library.ts** (256 lines) — preset Spaces (RMBG, FLUX, Whisper, etc.), component templates
27
+ - **workflow-types.ts** (99 lines) — TypeScript interfaces and port color scheme
28
+ - **workflow-to-space.ts** (236 lines) — generates `app.py` using `gradio_client.Client.predict()`
29
+
30
+ ### Backend (Python)
31
+ - **app.py** (133 lines) — Gradio block hosting the custom WorkflowCanvas component
32
+ - **workflowcanvas.py** — Python wrapper for the custom component
33
+ - `server_functions` exposed to frontend:
34
+ - `get_token()` — returns HF token from OAuth
35
+ - `call_space(data, token)` — calls remote Space with file handling
36
+
37
+ ### Custom Component
38
+ - Built with Gradio's custom component SDK
39
+ - Compiled to wheel file for easy distribution
40
+ - Exposes `server_functions` for frontend→backend RPC
41
+
42
+ ---
43
+
44
+ ## Data Model
45
+
46
+ ### Workflow (JSON)
47
+ ```typescript
48
+ interface Workflow {
49
+ version: "1"; // schema version
50
+ name: string; // workflow name (used in export filename)
51
+ nodes: WFNode[];
52
+ edges: WFEdge[];
53
+ }
54
+ ```
55
+
56
+ ### Node
57
+ ```typescript
58
+ interface WFNode {
59
+ id: string; // unique ID (e.g., "n1", "n2")
60
+ kind: "input" | "transform" | "output";
61
+ label: string; // display name
62
+ source: "local" | "space"; // local = input/output, space = HF Space
63
+ space_id?: string; // "username/space-name" if source=space
64
+ endpoint?: string; // API endpoint (e.g., "/predict", "/image")
65
+ inputs: Port[];
66
+ outputs: Port[];
67
+ x: number; // canvas position (px)
68
+ y: number;
69
+ width: number; // node card size
70
+ height: number;
71
+ data: Record<string, unknown>; // state for input nodes or inline widgets
72
+ }
73
+ ```
74
+
75
+ ### Port
76
+ ```typescript
77
+ interface Port {
78
+ id: string; // "in_0", "out_1", etc.
79
+ label: string; // display name
80
+ type: PortType; // e.g., "image", "text", "file"
81
+ required?: boolean; // from API introspection
82
+ default_value?: unknown;
83
+ }
84
+ ```
85
+
86
+ ### Port Types
87
+ ```
88
+ "image" | "text" | "audio" | "video" | "number" | "boolean" |
89
+ "file" | "json" | "gallery" | "model3d" | "any"
90
+ ```
91
+
92
+ ### Edge
93
+ ```typescript
94
+ interface WFEdge {
95
+ id: string;
96
+ from_node_id: string;
97
+ from_port_id: string;
98
+ to_node_id: string;
99
+ to_port_id: string;
100
+ type: PortType; // enforced at connection time
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Core Features
107
+
108
+ ### 1. Canvas & Interaction
109
+ - **Pan/Zoom**: scroll to pan, pinch/scroll to zoom, `Cmd+0` to reset
110
+ - **Grid**: dot grid, snap-to-grid on node drop
111
+ - **Node Selection**: click to select, `Delete` to remove
112
+ - **Duplication**: `Cmd+D` to duplicate selected node
113
+ - **Rename**: double-click node label to edit
114
+ - **Auto-layout**: `Layout` button distributes nodes vertically
115
+
116
+ ### 2. Wiring & Type Safety
117
+ - Ports colored by type (image=cyan, text=purple, audio=orange, etc.)
118
+ - Type checking: can only wire compatible types
119
+ - Auto-create input/output nodes: `+` button on ports
120
+ - Connection badges show port type on hover
121
+ - Edge delete on hover (X icon)
122
+
123
+ ### 3. Node Library
124
+ - **Presets**: BRIA RMBG, FLUX.1-schnell, Whisper, Finegrain, UNESCO/nllb, etc.
125
+ - **Space Search**: search HuggingFace Hub, filter to running Spaces
126
+ - **API Introspection**: auto-detect inputs, outputs, required/optional params
127
+ - **Endpoint Selection**: heuristic picks best endpoint (filters event handlers, prioritizes rich outputs)
128
+ - **Custom Spaces**: add by space ID, saved to localStorage
129
+
130
+ ### 4. Execution Engine
131
+ - **Topological Sort**: executes nodes in dependency order
132
+ - **Parallel Layers**: nodes at same depth run in parallel
133
+ - **Client Caching**: one `Client` instance per Space to share auth/connection
134
+ - **Retry Logic**: retries stale clients (connection reset)
135
+ - **File Handling**:
136
+ - Blob URLs → uploaded to server → passed to Space
137
+ - Remote file paths → converted to `/file=` URLs
138
+ - Results stored in node output for downstream use
139
+ - **Status Tracking**: idle→running→done/error per node
140
+ - **Abort Signal**: can stop running workflow with `Cmd+.`
141
+
142
+ ### 5. API Introspection
143
+ - Uses `client.view_api(return_format="dict")` to inspect Space
144
+ - Extracts `named_endpoints`, parameter names, types, defaults, labels
145
+ - Maps Gradio component types to port types (e.g., `Image` → `image`)
146
+ - Falls back to type field parsing (e.g., "filepath" → `file`)
147
+ - Detects optional params via `parameter_has_default`
148
+
149
+ ### 6. Export & Import
150
+ - **Export**: downloads workflow as `workflow_name.workflow.json`
151
+ - **Import**: loads JSON back into canvas
152
+ - **No Server Dependency**: JSON is self-contained, portable
153
+
154
+ ---
155
+
156
+ ## Execution Flow
157
+
158
+ 1. **User clicks Run**
159
+ - Collect all node inputs from canvas state
160
+ - Build dependency graph from edges
161
+ - Topologically sort nodes
162
+
163
+ 2. **Execute Layer by Layer**
164
+ - For each layer (depth level):
165
+ - For each node in parallel:
166
+ - Resolve inputs (from upstream outputs or node.data)
167
+ - If transform node: call Space endpoint
168
+ - Update node output data
169
+ - Update UI status
170
+
171
+ 3. **File Handling During Execution**
172
+ - Blob URLs (from file upload) → `POST /upload` → get server path
173
+ - Remote file paths → passed to Space as-is
174
+ - Space returns local paths → convert to `/file=/path` URLs
175
+ - Downstream nodes receive readable URLs
176
+
177
+ 4. **Error Handling**
178
+ - Classify errors (GPU unavailable, quota exceeded, timeout, etc.)
179
+ - Show contextual toast messages
180
+ - Mark node as error, stop execution
181
+ - User can fix inputs and re-run
182
+
183
+ ---
184
+
185
+ ## Backend API
186
+
187
+ ### Python Backend (app.py)
188
+
189
+ **Server Functions** exposed to frontend:
190
+
191
+ #### `get_token(token: Optional[OAuthToken]) -> str`
192
+ Returns the user's HF token if logged in, empty string otherwise.
193
+
194
+ #### `call_space(data: list, token: Optional[OAuthToken]) -> str`
195
+ Calls a remote Gradio Space.
196
+
197
+ **Parameters:**
198
+ ```python
199
+ data = [
200
+ space_id, # "username/space-name"
201
+ endpoint, # "/predict" or "/image" etc.
202
+ args_json, # JSON array of arguments
203
+ manual_token # Optional token from localStorage
204
+ ]
205
+ ```
206
+
207
+ **Returns:** JSON-encoded output array
208
+ ```json
209
+ [
210
+ { "path": "/tmp/file", "url": "/file=/tmp/file", "is_file": true },
211
+ { "data": "text result" },
212
+ ...
213
+ ]
214
+ ```
215
+
216
+ **Error Response:**
217
+ ```json
218
+ {
219
+ "error": "error message",
220
+ "error_type": "gpu|quota|timeout|not_found|...",
221
+ "suggestion": "user-facing hint",
222
+ "title": "optional error title"
223
+ }
224
+ ```
225
+
226
+ **Error Classification:**
227
+ - `gpu`: ZeroGPU unavailable
228
+ - `quota`: Rate limit / GPU quota exceeded
229
+ - `sleeping`: Space paused or sleeping
230
+ - `not_found`: Space deleted or renamed
231
+ - `build_error`: Space has build errors
232
+ - `connection`: Cannot connect to Space (timeout, down, etc.)
233
+ - `unknown`: Other errors
234
+
235
+ ---
236
+
237
+ ## Workflow JSON Example
238
+
239
+ ```json
240
+ {
241
+ "version": "1",
242
+ "name": "Image Background Removal",
243
+ "nodes": [
244
+ {
245
+ "id": "input_1",
246
+ "kind": "input",
247
+ "label": "Image",
248
+ "source": "local",
249
+ "inputs": [],
250
+ "outputs": [
251
+ { "id": "out_0", "label": "Image", "type": "image" }
252
+ ],
253
+ "x": 100,
254
+ "y": 100,
255
+ "width": 220,
256
+ "height": 160,
257
+ "data": {}
258
+ },
259
+ {
260
+ "id": "transform_1",
261
+ "kind": "transform",
262
+ "label": "Remove Background",
263
+ "source": "space",
264
+ "space_id": "not-lain/background-removal",
265
+ "endpoint": "/image",
266
+ "inputs": [
267
+ { "id": "in_0", "label": "Image", "type": "image", "required": true }
268
+ ],
269
+ "outputs": [
270
+ { "id": "out_0", "label": "Image", "type": "image" }
271
+ ],
272
+ "x": 450,
273
+ "y": 100,
274
+ "width": 200,
275
+ "height": 90,
276
+ "data": {}
277
+ },
278
+ {
279
+ "id": "output_1",
280
+ "kind": "output",
281
+ "label": "Result",
282
+ "source": "local",
283
+ "inputs": [
284
+ { "id": "in_0", "label": "Image", "type": "image" }
285
+ ],
286
+ "outputs": [],
287
+ "x": 750,
288
+ "y": 100,
289
+ "width": 220,
290
+ "height": 160,
291
+ "data": {}
292
+ }
293
+ ],
294
+ "edges": [
295
+ {
296
+ "id": "edge_1",
297
+ "from_node_id": "input_1",
298
+ "from_port_id": "out_0",
299
+ "to_node_id": "transform_1",
300
+ "to_port_id": "in_0",
301
+ "type": "image"
302
+ },
303
+ {
304
+ "id": "edge_2",
305
+ "from_node_id": "transform_1",
306
+ "from_port_id": "out_0",
307
+ "to_node_id": "output_1",
308
+ "to_port_id": "in_0",
309
+ "type": "image"
310
+ }
311
+ ]
312
+ }
313
+ ```
314
+
315
+ ---
316
+
317
+ ## UI/UX
318
+
319
+ ### Toolbar
320
+ - **Token Input**: paste HF token for GPU access (saved to localStorage)
321
+ - **Templates**: quick-start workflows (background removal, image generation, etc.)
322
+ - **Export/Import**: download/upload workflow.json
323
+ - **Layout**: auto-arrange nodes
324
+ - **Clear**: delete all nodes and edges
325
+ - **Run**: execute workflow end-to-end
326
+ - **Stop**: abort running workflow
327
+
328
+ ### Keyboard Shortcuts
329
+ - `Cmd+R` or Click Run → execute
330
+ - `Cmd+.` → stop running
331
+ - `Cmd+S` → export workflow
332
+ - `Delete` → remove selected node
333
+ - `Cmd+D` → duplicate selected node
334
+ - `Cmd+0` → reset pan/zoom
335
+ - `?` → show shortcuts help
336
+
337
+ ### Sidebar
338
+ - **Component Library**: input/output/transform node templates
339
+ - **Spaces**: preset and custom Spaces with categories (IMAGE, AUDIO, TEXT, etc.)
340
+ - **Search**: find Spaces on HF Hub, filter by name
341
+ - **Resizable**: drag divider to expand/collapse
342
+ - **Collapsible**: click chevron to collapse header
343
+
344
+ ### Modals
345
+ - **Deploy** (removed): was generating Gradio Space apps, no longer needed
346
+ - **Templates**: quick-start workflows
347
+ - **Shortcuts**: keyboard reference
348
+
349
+ ---
350
+
351
+ ## Constraints & Design Decisions
352
+
353
+ 1. **Workflows ≠ Gradio Apps**: Workflows are JSON artifacts, not UI deployments. Separate concerns.
354
+
355
+ 2. **Type System**: Simple 10-type system covers most cases. `any` for unknowns.
356
+
357
+ 3. **API Introspection**: Uses Gradio client's `view_api()` for zero-configuration Space discovery.
358
+
359
+ 4. **File Handling**:
360
+ - Blob URLs must be uploaded to server before passing to remote Spaces
361
+ - Remote file paths use `/file=` URL scheme for browser access
362
+ - Gradio handles auth/CORS transparently via client
363
+
364
+ 5. **Execution**: Topological sort + layer-based parallel execution maximizes throughput without complexity.
365
+
366
+ 6. **Storage**:
367
+ - Workflow JSON is portable (no server state)
368
+ - HF token saved to localStorage (client-side only)
369
+ - Node state persisted only in running workflow
370
+
371
+ 7. **Error Recovery**:
372
+ - Client caching + retry handles transient connection issues
373
+ - Classified errors provide user-facing guidance
374
+ - No auto-recovery for invalid Space IDs or missing endpoints
375
+
376
+ ---
377
+
378
+ ## Limitations & Future Work
379
+
380
+ ### Current Limitations
381
+ - No loops or conditional branching
382
+ - No multi-Space chains (edges can only wire N→1, 1→N, never M→N split)
383
+ - No node comments or versioning
384
+ - No collaborative editing
385
+ - No workflow versioning or rollback
386
+ - Endpoint selection heuristic can fail on complex Spaces (needs manual override UI)
387
+
388
+ ### Future Enhancements
389
+ 1. **Workflow Sharing Registry**: platform to publish/discover workflows
390
+ 2. **Vibenode Integration**: export workflow → auto-generate vibenode boilerplate
391
+ 3. **Branches & Loops**: conditional execution, map operations
392
+ 4. **Caching Layer**: skip re-running unchanged nodes
393
+ 5. **Scheduling**: trigger workflows on cron schedule
394
+ 6. **Monitoring**: runtime logs, performance metrics
395
+ 7. **Version Control**: git-like workflow snapshots
396
+ 8. **Collaborative**: real-time multi-user editing
397
+
398
+ ---
399
+
400
+ ## Tech Stack
401
+
402
+ - **Frontend**: Svelte 4, TypeScript, Vite
403
+ - **Canvas**: DOM-based (no canvas library), custom pan/zoom
404
+ - **Backend**: Python, Gradio 6.12+, gradio_client
405
+ - **Component Distribution**: Gradio custom component wheel
406
+ - **Hosting**: HuggingFace Spaces
407
+ - **External API**: HuggingFace Hub (`gradio_client.Client`)
408
+
409
+ ---
410
+
411
+ ## File Structure
412
+
413
+ ```
414
+ workflow-app/
415
+ ├── app.py # Main Gradio app
416
+ ├── pyproject.toml # Python package config
417
+ ├── backend/
418
+ │ └── gradio_workflowcanvas/ # Custom component backend
419
+ │ └── workflowcanvas.py
420
+ ├── frontend/
421
+ │ ├── Index.svelte # Component root
422
+ │ ├── gradio.config.js
423
+ │ ├── package.json
424
+ │ ├── tsconfig.json
425
+ │ └── workflow/ # Main app logic
426
+ │ ├── WorkflowCanvas.svelte # Main UI
427
+ │ ├── WorkflowNode.svelte # Node renderer
428
+ │ ├── WorkflowEdges.svelte # Edge renderer
429
+ │ ├── WorkflowSidebar.svelte # Component library
430
+ │ ├── workflow-types.ts # Type definitions
431
+ │ ├── workflow-store.ts # State management
432
+ │ ├── workflow-executor.ts # Execution engine
433
+ │ ├── space-api.ts # API introspection
434
+ │ ├── node-library.ts # Preset Spaces
435
+ │ └── workflow-to-space.ts # Code generation (removed)
436
+ └── dist/ # Built wheel file
437
+ ```
438
+
439
+ ---
440
+
441
+ ## Glossary
442
+
443
+ - **Node**: A UI card on canvas representing either an input, transformation (Space call), or output
444
+ - **Port**: An input or output pin on a node (e.g., "image in", "text out")
445
+ - **Edge**: A connection between two ports
446
+ - **Workflow**: A complete DAG of nodes and edges, exportable as JSON
447
+ - **Space**: A Gradio app on HuggingFace Hub
448
+ - **Endpoint**: An API function in a Space (e.g., `/predict`, `/image`)
449
+ - **Transform**: A node that calls a Space endpoint (fn node)
450
+ - **Vibenode**: A separate UI layer built on top of workflows (not part of this spec)
@@ -0,0 +1,133 @@
1
+ # WorkflowCanvas component is installed via pip (published to PyPI)
2
+ # For local development: pip install -e .
3
+
4
+ import gradio as gr
5
+ from gradio.oauth import OAuthToken
6
+ from gradio_workflowcanvas import WorkflowCanvas
7
+ from gradio_client import Client, handle_file
8
+ from typing import Optional
9
+ import json
10
+ import os
11
+
12
+
13
+ def get_token(token: Optional[OAuthToken] = None) -> str:
14
+ if token is None:
15
+ return ""
16
+ return token.token
17
+
18
+
19
+ def classify_error(e: Exception) -> dict:
20
+ """Classify an error into a type and suggestion for the frontend."""
21
+ title = getattr(e, 'title', None) or ""
22
+ message = getattr(e, 'message', None) or str(e)
23
+ full = f"{title} {message}".lower()
24
+
25
+ if "zerogpu" in full or "gpu" in full and "worker" in full:
26
+ return {"error_type": "gpu", "suggestion": "GPU unavailable — try again or log in with your HF account"}
27
+ if "quota" in full or "rate limit" in full:
28
+ return {"error_type": "quota", "suggestion": "GPU quota exceeded — log in with your HF account for more compute"}
29
+ if "sleeping" in full or "paused" in full:
30
+ return {"error_type": "sleeping", "suggestion": "Space is sleeping or paused — try again in a minute"}
31
+ if "not found" in full or "404" in full or "repository not found" in full:
32
+ return {"error_type": "not_found", "suggestion": "Space not found — it may have been deleted or renamed"}
33
+ if "build_error" in full or "build error" in full:
34
+ return {"error_type": "build_error", "suggestion": "Space has a build error — contact the Space owner"}
35
+ if "timed out" in full or "timeout" in full or "connection" in full:
36
+ return {"error_type": "connection", "suggestion": "Could not connect to the Space — it may be down"}
37
+
38
+ return {"error_type": "unknown", "suggestion": ""}
39
+
40
+
41
+ def call_space(data, token: Optional[OAuthToken] = None) -> str:
42
+ try:
43
+ space_id = data[0]
44
+ endpoint = data[1] if len(data) > 1 else None
45
+ args_json = data[2] if len(data) > 2 else "[]"
46
+ manual_token = data[3] if len(data) > 3 else None
47
+
48
+ # Use manual token if provided, otherwise fall back to OAuth token
49
+ hf_token = manual_token or (token.token if token else None)
50
+ client = Client(space_id, token=hf_token)
51
+ args = json.loads(args_json)
52
+
53
+ if not endpoint or endpoint == "/predict":
54
+ api_info = client.view_api(return_format="dict")
55
+ named = list(api_info.get("named_endpoints", {}).keys())
56
+ if endpoint and endpoint in named:
57
+ pass
58
+ elif named:
59
+ endpoint = named[0]
60
+ else:
61
+ endpoint = "/predict"
62
+
63
+ processed = []
64
+ for arg in args:
65
+ if arg is None:
66
+ processed.append(None)
67
+ elif isinstance(arg, dict) and ("url" in arg or "path" in arg):
68
+ url = arg.get("url") or arg.get("path", "")
69
+ if url:
70
+ processed.append(handle_file(url))
71
+ else:
72
+ processed.append(None)
73
+ else:
74
+ processed.append(arg)
75
+
76
+ # Strip trailing None args so the Space uses its own defaults.
77
+ # view_api(return_format="dict") returns default=None even when defaults
78
+ # exist, so we'd otherwise pass null for optional args and get
79
+ # "No value provided for required argument".
80
+ while processed and processed[-1] is None:
81
+ processed.pop()
82
+
83
+ result = client.predict(*processed, api_name=endpoint)
84
+
85
+ if not isinstance(result, (list, tuple)):
86
+ result = [result]
87
+ else:
88
+ result = list(result)
89
+
90
+ output = []
91
+ for item in result:
92
+ if isinstance(item, dict) and "url" in item:
93
+ # Already processed by gradio_client, use as-is
94
+ output.append(item)
95
+ elif isinstance(item, str) and os.path.exists(item):
96
+ # Local file path - convert to /file= URL for browser access
97
+ output.append({"path": item, "url": f"/file={item}", "is_file": True})
98
+ elif isinstance(item, dict):
99
+ output.append(item)
100
+ elif isinstance(item, (list, tuple)):
101
+ sub = []
102
+ for s in item:
103
+ if isinstance(s, dict) and "url" in s:
104
+ sub.append(s)
105
+ elif isinstance(s, str) and os.path.exists(s):
106
+ sub.append({"path": s, "url": f"/file={s}", "is_file": True})
107
+ elif isinstance(s, dict):
108
+ sub.append(s)
109
+ else:
110
+ sub.append(s)
111
+ output.append(sub)
112
+ else:
113
+ output.append(item)
114
+
115
+ return json.dumps(output)
116
+ except Exception as e:
117
+ title = getattr(e, 'title', None)
118
+ message = getattr(e, 'message', None) or str(e)
119
+ classified = classify_error(e)
120
+ error_info = {
121
+ "error": message,
122
+ **classified,
123
+ }
124
+ if title:
125
+ error_info["title"] = title
126
+ return json.dumps(error_info)
127
+
128
+
129
+ with gr.Blocks() as demo:
130
+ canvas = WorkflowCanvas(server_functions=[get_token, call_space])
131
+
132
+ if __name__ == "__main__":
133
+ demo.launch(css=".toast-wrap { display: none !important; }")
@@ -0,0 +1,4 @@
1
+
2
+ from .workflowcanvas import WorkflowCanvas
3
+
4
+ __all__ = ['WorkflowCanvas']