flowyml 1.1.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 (159) hide show
  1. flowyml/__init__.py +207 -0
  2. flowyml/assets/__init__.py +22 -0
  3. flowyml/assets/artifact.py +40 -0
  4. flowyml/assets/base.py +209 -0
  5. flowyml/assets/dataset.py +100 -0
  6. flowyml/assets/featureset.py +301 -0
  7. flowyml/assets/metrics.py +104 -0
  8. flowyml/assets/model.py +82 -0
  9. flowyml/assets/registry.py +157 -0
  10. flowyml/assets/report.py +315 -0
  11. flowyml/cli/__init__.py +5 -0
  12. flowyml/cli/experiment.py +232 -0
  13. flowyml/cli/init.py +256 -0
  14. flowyml/cli/main.py +327 -0
  15. flowyml/cli/run.py +75 -0
  16. flowyml/cli/stack_cli.py +532 -0
  17. flowyml/cli/ui.py +33 -0
  18. flowyml/core/__init__.py +68 -0
  19. flowyml/core/advanced_cache.py +274 -0
  20. flowyml/core/approval.py +64 -0
  21. flowyml/core/cache.py +203 -0
  22. flowyml/core/checkpoint.py +148 -0
  23. flowyml/core/conditional.py +373 -0
  24. flowyml/core/context.py +155 -0
  25. flowyml/core/error_handling.py +419 -0
  26. flowyml/core/executor.py +354 -0
  27. flowyml/core/graph.py +185 -0
  28. flowyml/core/parallel.py +452 -0
  29. flowyml/core/pipeline.py +764 -0
  30. flowyml/core/project.py +253 -0
  31. flowyml/core/resources.py +424 -0
  32. flowyml/core/scheduler.py +630 -0
  33. flowyml/core/scheduler_config.py +32 -0
  34. flowyml/core/step.py +201 -0
  35. flowyml/core/step_grouping.py +292 -0
  36. flowyml/core/templates.py +226 -0
  37. flowyml/core/versioning.py +217 -0
  38. flowyml/integrations/__init__.py +1 -0
  39. flowyml/integrations/keras.py +134 -0
  40. flowyml/monitoring/__init__.py +1 -0
  41. flowyml/monitoring/alerts.py +57 -0
  42. flowyml/monitoring/data.py +102 -0
  43. flowyml/monitoring/llm.py +160 -0
  44. flowyml/monitoring/monitor.py +57 -0
  45. flowyml/monitoring/notifications.py +246 -0
  46. flowyml/registry/__init__.py +5 -0
  47. flowyml/registry/model_registry.py +491 -0
  48. flowyml/registry/pipeline_registry.py +55 -0
  49. flowyml/stacks/__init__.py +27 -0
  50. flowyml/stacks/base.py +77 -0
  51. flowyml/stacks/bridge.py +288 -0
  52. flowyml/stacks/components.py +155 -0
  53. flowyml/stacks/gcp.py +499 -0
  54. flowyml/stacks/local.py +112 -0
  55. flowyml/stacks/migration.py +97 -0
  56. flowyml/stacks/plugin_config.py +78 -0
  57. flowyml/stacks/plugins.py +401 -0
  58. flowyml/stacks/registry.py +226 -0
  59. flowyml/storage/__init__.py +26 -0
  60. flowyml/storage/artifacts.py +246 -0
  61. flowyml/storage/materializers/__init__.py +20 -0
  62. flowyml/storage/materializers/base.py +133 -0
  63. flowyml/storage/materializers/keras.py +185 -0
  64. flowyml/storage/materializers/numpy.py +94 -0
  65. flowyml/storage/materializers/pandas.py +142 -0
  66. flowyml/storage/materializers/pytorch.py +135 -0
  67. flowyml/storage/materializers/sklearn.py +110 -0
  68. flowyml/storage/materializers/tensorflow.py +152 -0
  69. flowyml/storage/metadata.py +931 -0
  70. flowyml/tracking/__init__.py +1 -0
  71. flowyml/tracking/experiment.py +211 -0
  72. flowyml/tracking/leaderboard.py +191 -0
  73. flowyml/tracking/runs.py +145 -0
  74. flowyml/ui/__init__.py +15 -0
  75. flowyml/ui/backend/Dockerfile +31 -0
  76. flowyml/ui/backend/__init__.py +0 -0
  77. flowyml/ui/backend/auth.py +163 -0
  78. flowyml/ui/backend/main.py +187 -0
  79. flowyml/ui/backend/routers/__init__.py +0 -0
  80. flowyml/ui/backend/routers/assets.py +45 -0
  81. flowyml/ui/backend/routers/execution.py +179 -0
  82. flowyml/ui/backend/routers/experiments.py +49 -0
  83. flowyml/ui/backend/routers/leaderboard.py +118 -0
  84. flowyml/ui/backend/routers/notifications.py +72 -0
  85. flowyml/ui/backend/routers/pipelines.py +110 -0
  86. flowyml/ui/backend/routers/plugins.py +192 -0
  87. flowyml/ui/backend/routers/projects.py +85 -0
  88. flowyml/ui/backend/routers/runs.py +66 -0
  89. flowyml/ui/backend/routers/schedules.py +222 -0
  90. flowyml/ui/backend/routers/traces.py +84 -0
  91. flowyml/ui/frontend/Dockerfile +20 -0
  92. flowyml/ui/frontend/README.md +315 -0
  93. flowyml/ui/frontend/dist/assets/index-DFNQnrUj.js +448 -0
  94. flowyml/ui/frontend/dist/assets/index-pWI271rZ.css +1 -0
  95. flowyml/ui/frontend/dist/index.html +16 -0
  96. flowyml/ui/frontend/index.html +15 -0
  97. flowyml/ui/frontend/nginx.conf +26 -0
  98. flowyml/ui/frontend/package-lock.json +3545 -0
  99. flowyml/ui/frontend/package.json +33 -0
  100. flowyml/ui/frontend/postcss.config.js +6 -0
  101. flowyml/ui/frontend/src/App.jsx +21 -0
  102. flowyml/ui/frontend/src/app/assets/page.jsx +397 -0
  103. flowyml/ui/frontend/src/app/dashboard/page.jsx +295 -0
  104. flowyml/ui/frontend/src/app/experiments/[experimentId]/page.jsx +255 -0
  105. flowyml/ui/frontend/src/app/experiments/page.jsx +360 -0
  106. flowyml/ui/frontend/src/app/leaderboard/page.jsx +133 -0
  107. flowyml/ui/frontend/src/app/pipelines/page.jsx +454 -0
  108. flowyml/ui/frontend/src/app/plugins/page.jsx +48 -0
  109. flowyml/ui/frontend/src/app/projects/page.jsx +292 -0
  110. flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +682 -0
  111. flowyml/ui/frontend/src/app/runs/page.jsx +470 -0
  112. flowyml/ui/frontend/src/app/schedules/page.jsx +585 -0
  113. flowyml/ui/frontend/src/app/settings/page.jsx +314 -0
  114. flowyml/ui/frontend/src/app/tokens/page.jsx +456 -0
  115. flowyml/ui/frontend/src/app/traces/page.jsx +246 -0
  116. flowyml/ui/frontend/src/components/Layout.jsx +108 -0
  117. flowyml/ui/frontend/src/components/PipelineGraph.jsx +295 -0
  118. flowyml/ui/frontend/src/components/header/Header.jsx +72 -0
  119. flowyml/ui/frontend/src/components/plugins/AddPluginDialog.jsx +121 -0
  120. flowyml/ui/frontend/src/components/plugins/InstalledPlugins.jsx +124 -0
  121. flowyml/ui/frontend/src/components/plugins/PluginBrowser.jsx +167 -0
  122. flowyml/ui/frontend/src/components/plugins/PluginManager.jsx +60 -0
  123. flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +145 -0
  124. flowyml/ui/frontend/src/components/ui/Badge.jsx +26 -0
  125. flowyml/ui/frontend/src/components/ui/Button.jsx +34 -0
  126. flowyml/ui/frontend/src/components/ui/Card.jsx +44 -0
  127. flowyml/ui/frontend/src/components/ui/CodeSnippet.jsx +38 -0
  128. flowyml/ui/frontend/src/components/ui/CollapsibleCard.jsx +53 -0
  129. flowyml/ui/frontend/src/components/ui/DataView.jsx +175 -0
  130. flowyml/ui/frontend/src/components/ui/EmptyState.jsx +49 -0
  131. flowyml/ui/frontend/src/components/ui/ExecutionStatus.jsx +122 -0
  132. flowyml/ui/frontend/src/components/ui/KeyValue.jsx +25 -0
  133. flowyml/ui/frontend/src/components/ui/ProjectSelector.jsx +134 -0
  134. flowyml/ui/frontend/src/contexts/ProjectContext.jsx +79 -0
  135. flowyml/ui/frontend/src/contexts/ThemeContext.jsx +54 -0
  136. flowyml/ui/frontend/src/index.css +11 -0
  137. flowyml/ui/frontend/src/layouts/MainLayout.jsx +23 -0
  138. flowyml/ui/frontend/src/main.jsx +10 -0
  139. flowyml/ui/frontend/src/router/index.jsx +39 -0
  140. flowyml/ui/frontend/src/services/pluginService.js +90 -0
  141. flowyml/ui/frontend/src/utils/api.js +47 -0
  142. flowyml/ui/frontend/src/utils/cn.js +6 -0
  143. flowyml/ui/frontend/tailwind.config.js +31 -0
  144. flowyml/ui/frontend/vite.config.js +21 -0
  145. flowyml/ui/utils.py +77 -0
  146. flowyml/utils/__init__.py +67 -0
  147. flowyml/utils/config.py +308 -0
  148. flowyml/utils/debug.py +240 -0
  149. flowyml/utils/environment.py +346 -0
  150. flowyml/utils/git.py +319 -0
  151. flowyml/utils/logging.py +61 -0
  152. flowyml/utils/performance.py +314 -0
  153. flowyml/utils/stack_config.py +296 -0
  154. flowyml/utils/validation.py +270 -0
  155. flowyml-1.1.0.dist-info/METADATA +372 -0
  156. flowyml-1.1.0.dist-info/RECORD +159 -0
  157. flowyml-1.1.0.dist-info/WHEEL +4 -0
  158. flowyml-1.1.0.dist-info/entry_points.txt +3 -0
  159. flowyml-1.1.0.dist-info/licenses/LICENSE +17 -0
@@ -0,0 +1,222 @@
1
+ from fastapi import APIRouter, HTTPException
2
+ from pydantic import BaseModel
3
+ from flowyml.core.scheduler import PipelineScheduler
4
+ from flowyml.registry.pipeline_registry import pipeline_registry
5
+
6
+ router = APIRouter()
7
+ # Note: In a real app, the scheduler instance should be a singleton managed by the app state
8
+ # For now, we instantiate it here, but it might not persist state across reloads if not handled carefully.
9
+ # Ideally, the scheduler is started when the backend starts.
10
+ scheduler = PipelineScheduler()
11
+ scheduler.start() # Start the scheduler thread
12
+
13
+
14
+ class ScheduleRequest(BaseModel):
15
+ name: str
16
+ pipeline_name: str
17
+ schedule_type: str # 'daily', 'hourly', 'interval', 'cron'
18
+ hour: int | None = 0
19
+ minute: int | None = 0
20
+ interval_seconds: int | None = 0
21
+ cron_expression: str | None = None
22
+ timezone: str = "UTC"
23
+ project_name: str | None = None
24
+
25
+
26
+ @router.get("/")
27
+ async def list_schedules():
28
+ """List all active schedules."""
29
+ # Convert Schedule objects to dicts for JSON serialization
30
+ schedules = scheduler.list_schedules()
31
+ return [
32
+ {
33
+ "pipeline_name": s.pipeline_name,
34
+ "schedule_type": s.schedule_type,
35
+ "schedule_value": s.schedule_value,
36
+ "enabled": s.enabled,
37
+ "last_run": s.last_run,
38
+ "next_run": s.next_run,
39
+ "timezone": s.timezone,
40
+ }
41
+ for s in schedules
42
+ ]
43
+
44
+
45
+ @router.get("/health")
46
+ async def get_scheduler_health():
47
+ """Get scheduler health metrics."""
48
+ return scheduler.health_check()
49
+
50
+
51
+ @router.post("/")
52
+ async def create_schedule(schedule: ScheduleRequest):
53
+ """Create a new schedule."""
54
+ # 1. Look up pipeline factory
55
+ pipeline_factory = pipeline_registry.get(schedule.pipeline_name)
56
+
57
+ if not pipeline_factory:
58
+ # Try to see if it's a template
59
+ from flowyml.core.templates import TEMPLATES, create_from_template
60
+
61
+ if schedule.pipeline_name in TEMPLATES:
62
+ # For templates, we need a way to instantiate them with default args or args provided in request
63
+ # This is a simplification for the demo
64
+ def template_wrapper():
65
+ # Create and run template pipeline
66
+ # Note: This assumes default args work. In reality, we'd need more config.
67
+ p = create_from_template(schedule.pipeline_name)
68
+ return p.run()
69
+
70
+ pipeline_func = template_wrapper
71
+ else:
72
+ # Check if it's a historical pipeline (in metadata but not registry)
73
+ # This means we can't run it because we don't have the code loaded
74
+ from flowyml.storage.metadata import SQLiteMetadataStore
75
+
76
+ store = SQLiteMetadataStore()
77
+ pipelines = store.list_pipelines()
78
+
79
+ if schedule.pipeline_name in pipelines:
80
+ # Try to load pipeline definition
81
+ from flowyml.storage.metadata import SQLiteMetadataStore
82
+
83
+ store = SQLiteMetadataStore()
84
+ pipeline_def = store.get_pipeline_definition(schedule.pipeline_name)
85
+
86
+ if pipeline_def:
87
+ # Reconstruct pipeline from definition
88
+ from flowyml.core.pipeline import Pipeline
89
+ from flowyml.core.context import Context
90
+
91
+ def pipeline_wrapper():
92
+ # Reconstruct pipeline each time
93
+ p = Pipeline.from_definition(pipeline_def, Context())
94
+ return p.run()
95
+
96
+ pipeline_func = pipeline_wrapper
97
+ else:
98
+ raise HTTPException(
99
+ status_code=400,
100
+ detail=f"Pipeline '{schedule.pipeline_name}' found in history but no definition stored. Please run the pipeline again to enable scheduling.",
101
+ )
102
+ else:
103
+ raise HTTPException(
104
+ status_code=404,
105
+ detail=f"Pipeline '{schedule.pipeline_name}' not found in registry. Please register it using @register_pipeline.",
106
+ )
107
+ else:
108
+ # Create a wrapper to instantiate and run the pipeline
109
+ def pipeline_wrapper():
110
+ # Instantiate fresh pipeline for each run
111
+ p = pipeline_factory()
112
+ return p.run()
113
+
114
+ pipeline_func = pipeline_wrapper
115
+
116
+ # 2. Schedule it
117
+ try:
118
+ if schedule.schedule_type == "daily":
119
+ scheduler.schedule_daily(
120
+ name=schedule.name,
121
+ pipeline_func=pipeline_func,
122
+ hour=schedule.hour,
123
+ minute=schedule.minute,
124
+ timezone=schedule.timezone,
125
+ )
126
+ elif schedule.schedule_type == "hourly":
127
+ scheduler.schedule_hourly(
128
+ name=schedule.name,
129
+ pipeline_func=pipeline_func,
130
+ minute=schedule.minute,
131
+ timezone=schedule.timezone,
132
+ )
133
+ elif schedule.schedule_type == "interval":
134
+ scheduler.schedule_interval(
135
+ name=schedule.name,
136
+ pipeline_func=pipeline_func,
137
+ seconds=schedule.interval_seconds,
138
+ timezone=schedule.timezone,
139
+ )
140
+ elif schedule.schedule_type == "cron":
141
+ if not schedule.cron_expression:
142
+ raise HTTPException(status_code=400, detail="Cron expression required for cron schedule")
143
+ scheduler.schedule_cron(
144
+ name=schedule.name,
145
+ pipeline_func=pipeline_func,
146
+ cron_expression=schedule.cron_expression,
147
+ timezone=schedule.timezone,
148
+ )
149
+ else:
150
+ raise HTTPException(status_code=400, detail="Invalid schedule type")
151
+
152
+ return {"status": "success", "message": f"Scheduled '{schedule.name}'"}
153
+
154
+ except ImportError as e:
155
+ raise HTTPException(status_code=501, detail=str(e))
156
+ except Exception as e:
157
+ raise HTTPException(status_code=500, detail=str(e))
158
+
159
+
160
+ @router.delete("/{schedule_name}")
161
+ async def delete_schedule(schedule_name: str):
162
+ """Remove a schedule."""
163
+ scheduler.unschedule(schedule_name)
164
+ return {"status": "success", "message": f"Schedule {schedule_name} removed"}
165
+
166
+
167
+ @router.post("/{schedule_name}/enable")
168
+ async def enable_schedule(schedule_name: str):
169
+ """Enable a schedule."""
170
+ scheduler.enable(schedule_name)
171
+ return {"status": "success"}
172
+
173
+
174
+ @router.post("/{schedule_name}/disable")
175
+ async def disable_schedule(schedule_name: str):
176
+ """Disable a schedule."""
177
+ scheduler.disable(schedule_name)
178
+ return {"status": "success"}
179
+
180
+
181
+ @router.get("/{schedule_name}/history")
182
+ async def get_schedule_history(schedule_name: str, limit: int = 50):
183
+ """Get execution history for a schedule."""
184
+ return scheduler.get_history(schedule_name, limit)
185
+
186
+
187
+ @router.get("/registered-pipelines")
188
+ async def list_registered_pipelines(project: str = None):
189
+ """List all pipelines available for scheduling."""
190
+ from flowyml.core.templates import list_templates
191
+ from flowyml.storage.metadata import SQLiteMetadataStore
192
+
193
+ registered = pipeline_registry.list_pipelines()
194
+ templates = list_templates()
195
+
196
+ # Also get pipelines from metadata store (historical runs)
197
+ metadata_pipelines = []
198
+ try:
199
+ store = SQLiteMetadataStore()
200
+ import sqlite3
201
+
202
+ conn = sqlite3.connect(store.db_path)
203
+ cursor = conn.cursor()
204
+
205
+ if project:
206
+ cursor.execute(
207
+ "SELECT DISTINCT pipeline_name FROM runs WHERE project = ? ORDER BY pipeline_name",
208
+ (project,),
209
+ )
210
+ else:
211
+ cursor.execute("SELECT DISTINCT pipeline_name FROM runs ORDER BY pipeline_name")
212
+
213
+ metadata_pipelines = [row[0] for row in cursor.fetchall()]
214
+ conn.close()
215
+ except Exception as e:
216
+ print(f"Failed to fetch pipelines from metadata store: {e}")
217
+
218
+ return {
219
+ "registered": list(registered.keys()),
220
+ "templates": templates,
221
+ "metadata": metadata_pipelines,
222
+ }
@@ -0,0 +1,84 @@
1
+ from fastapi import APIRouter, HTTPException
2
+ from flowyml.storage.metadata import SQLiteMetadataStore
3
+ import contextlib
4
+ import builtins
5
+
6
+ router = APIRouter()
7
+
8
+
9
+ @router.get("/")
10
+ async def list_traces(
11
+ limit: int = 50,
12
+ trace_id: str | None = None,
13
+ event_type: str | None = None,
14
+ project: str | None = None,
15
+ ):
16
+ """List traces, optionally filtered by project."""
17
+ store = SQLiteMetadataStore()
18
+
19
+ # We need to implement list_traces in metadata store or query manually
20
+ # For now, let's query manually via sqlite
21
+ import sqlite3
22
+
23
+ conn = sqlite3.connect(store.db_path)
24
+ cursor = conn.cursor()
25
+
26
+ query = "SELECT * FROM traces"
27
+ params = []
28
+ conditions = []
29
+
30
+ if trace_id:
31
+ conditions.append("trace_id = ?")
32
+ params.append(trace_id)
33
+
34
+ if event_type:
35
+ conditions.append("event_type = ?")
36
+ params.append(event_type)
37
+
38
+ if project:
39
+ conditions.append("project = ?")
40
+ params.append(project)
41
+
42
+ if conditions:
43
+ query += " WHERE " + " AND ".join(conditions)
44
+
45
+ query += " ORDER BY start_time DESC LIMIT ?"
46
+ params.append(limit)
47
+
48
+ cursor.execute(query, params)
49
+ columns = [description[0] for description in cursor.description]
50
+ rows = cursor.fetchall()
51
+
52
+ traces = []
53
+ import json
54
+
55
+ for row in rows:
56
+ trace = dict(zip(columns, row, strict=False))
57
+ # Parse JSON fields
58
+ for field in ["inputs", "outputs", "metadata"]:
59
+ if trace[field]:
60
+ with contextlib.suppress(builtins.BaseException):
61
+ trace[field] = json.loads(trace[field])
62
+ traces.append(trace)
63
+
64
+ conn.close()
65
+ return traces
66
+
67
+
68
+ @router.get("/{trace_id}")
69
+ async def get_trace(trace_id: str):
70
+ """Get a specific trace tree."""
71
+ store = SQLiteMetadataStore()
72
+ events = store.get_trace(trace_id)
73
+ if not events:
74
+ raise HTTPException(status_code=404, detail="Trace not found")
75
+
76
+ # Reconstruct tree
77
+ root_events = [e for e in events if not e["parent_id"]]
78
+
79
+ def build_tree(event):
80
+ children = [e for e in events if e["parent_id"] == event["event_id"]]
81
+ event["children"] = [build_tree(child) for child in children]
82
+ return event
83
+
84
+ return [build_tree(root) for root in root_events]
@@ -0,0 +1,20 @@
1
+ # Build stage
2
+ FROM node:18-alpine as build
3
+
4
+ WORKDIR /app
5
+
6
+ COPY package.json package-lock.json ./
7
+ RUN npm ci
8
+
9
+ COPY . .
10
+ RUN npm run build
11
+
12
+ # Serve stage
13
+ FROM nginx:alpine
14
+
15
+ COPY --from=build /app/dist /usr/share/nginx/html
16
+ COPY nginx.conf /etc/nginx/conf.d/default.conf
17
+
18
+ EXPOSE 80
19
+
20
+ CMD ["nginx", "-g", "daemon off;"]
@@ -0,0 +1,315 @@
1
+ # flowyml UI Frontend
2
+
3
+ Modern React-based frontend for the flowyml ML pipeline orchestration framework.
4
+
5
+ ## 🚀 Quick Start
6
+
7
+ ### Prerequisites
8
+
9
+ - **Node.js**: >= 16.x (recommended: 18.x or 20.x)
10
+ - **npm**: >= 8.x (comes with Node.js)
11
+
12
+ ### Installation
13
+
14
+ ```bash
15
+ # Install dependencies
16
+ npm install
17
+ ```
18
+
19
+ ### Development
20
+
21
+ ```bash
22
+ # Start development server with hot reload
23
+ npm run dev
24
+ ```
25
+
26
+ This will start the Vite dev server on `http://localhost:5173`. The dev server will:
27
+ - Hot reload on file changes
28
+ - Proxy API requests to `http://localhost:8080` (backend)
29
+ - Provide fast HMR (Hot Module Replacement)
30
+
31
+ **Note**: Make sure the backend is running before starting the frontend:
32
+
33
+ ```bash
34
+ # In another terminal, start the backend
35
+ cd ../../.. # Back to repo root
36
+ flowyml ui start --port 8080
37
+ ```
38
+
39
+ ### Production Build
40
+
41
+ ```bash
42
+ # Build for production
43
+ npm run build
44
+ ```
45
+
46
+ Production artifacts will be in the `dist/` directory. The flowyml Python backend automatically serves these files when available.
47
+
48
+ ## 📁 Project Structure
49
+
50
+ ```
51
+ frontend/
52
+ ├── src/
53
+ │ ├── components/ # Reusable UI components
54
+ │ ├── pages/ # Page-level components
55
+ │ ├── hooks/ # Custom React hooks
56
+ │ ├── utils/ # Utility functions
57
+ │ ├── api/ # API client functions
58
+ │ ├── types/ # TypeScript type definitions
59
+ │ ├── styles/ # Global styles and themes
60
+ │ ├── App.tsx # Main app component
61
+ │ └── main.tsx # Application entry point
62
+ ├── public/ # Static assets
63
+ ├── dist/ # Production build output (generated)
64
+ ├── index.html # HTML entry point
65
+ ├── package.json # Dependencies and scripts
66
+ ├── vite.config.ts # Vite configuration
67
+ ├── tsconfig.json # TypeScript configuration
68
+ └── README.md # This file
69
+ ```
70
+
71
+ ## 🛠️ Available Scripts
72
+
73
+ ### `npm run dev`
74
+
75
+ Starts the development server with hot reload.
76
+
77
+ - **URL**: http://localhost:5173
78
+ - **Features**: HMR, fast refresh, TypeScript checking
79
+ - **API Proxy**: Proxies `/api/*` to backend at `localhost:8080`
80
+
81
+ ### `npm run build`
82
+
83
+ Builds the app for production to the `dist/` folder.
84
+
85
+ - Optimized for performance
86
+ - Minified and bundled
87
+ - TypeScript type checking
88
+ - Ready to be served by the Python backend
89
+
90
+ ### `npm run preview`
91
+
92
+ Preview the production build locally:
93
+
94
+ ```bash
95
+ npm run build
96
+ npm run preview
97
+ ```
98
+
99
+ Serves the production build at `http://localhost:4173`.
100
+
101
+ ### `npm run lint`
102
+
103
+ Run ESLint to check code quality:
104
+
105
+ ```bash
106
+ npm run lint
107
+ ```
108
+
109
+ ### `npm run type-check`
110
+
111
+ Run TypeScript type checking without building:
112
+
113
+ ```bash
114
+ npm run type-check
115
+ ```
116
+
117
+ ## 🎨 Tech Stack
118
+
119
+ - **Framework**: React 18
120
+ - **Build Tool**: Vite
121
+ - **Language**: TypeScript
122
+ - **Styling**: CSS Modules / Tailwind CSS (if configured)
123
+ - **State Management**: React Context / Zustand (if needed)
124
+ - **Routing**: React Router (if multi-page)
125
+ - **API Client**: Fetch API / Axios
126
+
127
+ ## 🔌 API Integration
128
+
129
+ The frontend communicates with the flowyml backend via REST API:
130
+
131
+ ### Development Mode
132
+
133
+ In development, Vite proxies API requests to avoid CORS issues:
134
+
135
+ ```typescript
136
+ // vite.config.ts
137
+ export default defineConfig({
138
+ server: {
139
+ proxy: {
140
+ '/api': {
141
+ target: 'http://localhost:8080',
142
+ changeOrigin: true
143
+ }
144
+ }
145
+ }
146
+ })
147
+ ```
148
+
149
+ ### Production Mode
150
+
151
+ In production, the frontend is served by the Python backend from the `dist/` folder, so all API requests naturally go to the same origin.
152
+
153
+ ### Example API Usage
154
+
155
+ ```typescript
156
+ // src/api/pipelines.ts
157
+ export async function getPipelines() {
158
+ const response = await fetch('/api/pipelines');
159
+ return response.json();
160
+ }
161
+
162
+ export async function getPipeline(id: string) {
163
+ const response = await fetch(`/api/pipelines/${id}`);
164
+ return response.json();
165
+ }
166
+ ```
167
+
168
+ ## 🏗️ Development Workflow
169
+
170
+ ### 1. Setup (First Time)
171
+
172
+ ```bash
173
+ # Clone the repo and navigate to frontend
174
+ cd flowyml/ui/frontend
175
+
176
+ # Install dependencies
177
+ npm install
178
+ ```
179
+
180
+ ### 2. Development
181
+
182
+ ```bash
183
+ # Terminal 1: Start backend
184
+ cd ../../..
185
+ flowyml ui start --dev
186
+
187
+ # Terminal 2: Start frontend
188
+ cd flowyml/ui/frontend
189
+ npm run dev
190
+ ```
191
+
192
+ ### 3. Making Changes
193
+
194
+ 1. Edit files in `src/`
195
+ 2. Hot reload will update the browser automatically
196
+ 3. Check console for errors
197
+ 4. Test API integration with backend running
198
+
199
+ ### 4. Building for Production
200
+
201
+ ```bash
202
+ # Build frontend
203
+ npm run build
204
+
205
+ # Test the backend serves it correctly
206
+ cd ../../..
207
+ flowyml ui start
208
+ # Visit http://localhost:8080
209
+ ```
210
+
211
+ ## 🐛 Troubleshooting
212
+
213
+ ### Issue: `npm install` fails
214
+
215
+ **Solution**: Make sure you have Node.js 16+ installed:
216
+
217
+ ```bash
218
+ node --version # Should be >= 16.x
219
+ npm --version # Should be >= 8.x
220
+ ```
221
+
222
+ ### Issue: API calls return 404
223
+
224
+ **Problem**: Backend not running or wrong port.
225
+
226
+ **Solution**:
227
+ 1. Start the backend: `flowyml ui start --port 8080`
228
+ 2. Check proxy config in `vite.config.ts`
229
+ 3. Verify backend is on `http://localhost:8080/api/health`
230
+
231
+ ### Issue: Hot reload not working
232
+
233
+ **Solution**:
234
+ 1. Make sure you're running `npm run dev` (not `npm run build`)
235
+ 2. Check for errors in the terminal
236
+ 3. Try clearing Vite cache: `rm -rf node_modules/.vite`
237
+
238
+ ### Issue: Build succeeds but backend shows "Frontend not built"
239
+
240
+ **Cause**: Backend looking in wrong directory.
241
+
242
+ **Solution**: Ensure the `dist/` folder exists in `flowyml/ui/frontend/dist/`
243
+
244
+ ```bash
245
+ # Check if dist exists
246
+ ls -la dist/
247
+
248
+ # If not, rebuild
249
+ npm run build
250
+ ```
251
+
252
+ ## 🎯 Key Features to Implement
253
+
254
+ The frontend should provide:
255
+
256
+ 1. **Dashboard**
257
+ - Overview of recent pipeline runs
258
+ - Active/running pipelines
259
+ - Quick stats (success rate, avg duration)
260
+
261
+ 2. **Pipeline List**
262
+ - All registered pipelines
263
+ - Search and filter
264
+ - Click to view details
265
+
266
+ 3. **Pipeline Details**
267
+ - DAG visualization
268
+ - Step-by-step execution status
269
+ - Logs per step
270
+ - Artifacts produced
271
+
272
+ 4. **Run History**
273
+ - List of all runs
274
+ - Filter by pipeline, status, date
275
+ - Compare runs side-by-side
276
+
277
+ 5. **Asset Explorer**
278
+ - Browse datasets, models, metrics
279
+ - View lineage graph
280
+ - Download artifacts
281
+
282
+ 6. **Experiment Tracking**
283
+ - Compare experiments
284
+ - Metrics visualization (charts)
285
+ - Parameter comparison
286
+
287
+ 7. **Real-Time Updates** (Future)
288
+ - WebSocket connection for live updates
289
+ - Live metric streaming
290
+ - Real-time log tailing
291
+
292
+ ## 📚 Resources
293
+
294
+ - [Vite Documentation](https://vitejs.dev/)
295
+ - [React Documentation](https://react.dev/)
296
+ - [TypeScript Handbook](https://www.typescriptlang.org/docs/)
297
+ - [flowyml UI Guide](../../../UI_GUIDE.md)
298
+
299
+ ## 🤝 Contributing
300
+
301
+ When contributing to the frontend:
302
+
303
+ 1. Follow the existing code style
304
+ 2. Use TypeScript for type safety
305
+ 3. Write meaningful component names
306
+ 4. Add comments for complex logic
307
+ 5. Test API integration thoroughly
308
+ 6. Ensure production build works
309
+
310
+ ## 📝 Notes
311
+
312
+ - The Python backend serves the built frontend from `dist/`
313
+ - In production, there's no separate frontend server
314
+ - All state is fetched from the backend API
315
+ - WebSocket support is planned for future releases