@ryanfw/prompt-orchestration-pipeline 0.6.0 → 0.7.0

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 (61) hide show
  1. package/README.md +1 -2
  2. package/package.json +1 -2
  3. package/src/api/validators/json.js +39 -0
  4. package/src/components/DAGGrid.jsx +392 -303
  5. package/src/components/JobCard.jsx +13 -11
  6. package/src/components/JobDetail.jsx +41 -71
  7. package/src/components/JobTable.jsx +32 -22
  8. package/src/components/Layout.jsx +0 -21
  9. package/src/components/LiveText.jsx +47 -0
  10. package/src/components/TaskDetailSidebar.jsx +216 -0
  11. package/src/components/TimerText.jsx +82 -0
  12. package/src/components/ui/RestartJobModal.jsx +140 -0
  13. package/src/components/ui/toast.jsx +138 -0
  14. package/src/config/models.js +322 -0
  15. package/src/config/statuses.js +119 -0
  16. package/src/core/config.js +2 -164
  17. package/src/core/file-io.js +1 -1
  18. package/src/core/module-loader.js +54 -40
  19. package/src/core/pipeline-runner.js +52 -20
  20. package/src/core/status-writer.js +147 -3
  21. package/src/core/symlink-bridge.js +57 -0
  22. package/src/core/symlink-utils.js +94 -0
  23. package/src/core/task-runner.js +267 -443
  24. package/src/llm/index.js +167 -52
  25. package/src/pages/Code.jsx +57 -3
  26. package/src/pages/PipelineDetail.jsx +92 -22
  27. package/src/pages/PromptPipelineDashboard.jsx +15 -36
  28. package/src/providers/anthropic.js +83 -69
  29. package/src/providers/base.js +52 -0
  30. package/src/providers/deepseek.js +17 -34
  31. package/src/providers/gemini.js +226 -0
  32. package/src/providers/openai.js +36 -106
  33. package/src/providers/zhipu.js +136 -0
  34. package/src/ui/client/adapters/job-adapter.js +16 -26
  35. package/src/ui/client/api.js +134 -0
  36. package/src/ui/client/hooks/useJobDetailWithUpdates.js +65 -178
  37. package/src/ui/client/index.css +9 -0
  38. package/src/ui/client/index.html +1 -0
  39. package/src/ui/client/main.jsx +18 -15
  40. package/src/ui/client/time-store.js +161 -0
  41. package/src/ui/config-bridge.js +15 -24
  42. package/src/ui/config-bridge.node.js +15 -24
  43. package/src/ui/dist/assets/{index-WgJUlSmE.js → index-DqkbzXZ1.js} +1408 -771
  44. package/src/ui/dist/assets/style-DBF9NQGk.css +62 -0
  45. package/src/ui/dist/index.html +3 -2
  46. package/src/ui/public/favicon.svg +12 -0
  47. package/src/ui/server.js +231 -33
  48. package/src/ui/transformers/status-transformer.js +18 -31
  49. package/src/utils/dag.js +8 -4
  50. package/src/utils/duration.js +13 -19
  51. package/src/utils/formatters.js +27 -0
  52. package/src/utils/geometry-equality.js +83 -0
  53. package/src/utils/pipelines.js +5 -1
  54. package/src/utils/time-utils.js +40 -0
  55. package/src/utils/token-cost-calculator.js +4 -7
  56. package/src/utils/ui.jsx +14 -16
  57. package/src/components/ui/select.jsx +0 -27
  58. package/src/lib/utils.js +0 -6
  59. package/src/ui/client/hooks/useTicker.js +0 -26
  60. package/src/ui/config-bridge.browser.js +0 -149
  61. package/src/ui/dist/assets/style-x0V-5m8e.css +0 -62
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Global time store for efficient timer updates
3
+ * Provides a single source of truth for time-based updates with dynamic cadence
4
+ */
5
+
6
+ // Internal state
7
+ const offset = Date.now() - performance.now();
8
+ let currentNow = Math.floor(performance.now() + offset);
9
+ const listeners = new Set();
10
+ const cadenceHints = new Map();
11
+ let timerId = null;
12
+ let activeIntervalMs = 1000;
13
+ let isBackground = false;
14
+
15
+ /**
16
+ * Subscribe to time updates
17
+ * @param {Function} listener - Callback function called on each tick
18
+ * @returns {Function} Unsubscribe function
19
+ */
20
+ function subscribe(listener) {
21
+ listeners.add(listener);
22
+
23
+ // If this is the first listener, start the timer
24
+ if (listeners.size === 1) {
25
+ startTimer();
26
+ }
27
+
28
+ // Return unsubscribe function
29
+ return () => {
30
+ listeners.delete(listener);
31
+
32
+ // If no more listeners, stop the timer
33
+ if (listeners.size === 0) {
34
+ stopTimer();
35
+ }
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Get current time snapshot
41
+ * @returns {number} Current timestamp in milliseconds
42
+ */
43
+ function getSnapshot() {
44
+ return currentNow;
45
+ }
46
+
47
+ /**
48
+ * Get server-side snapshot for SSR safety
49
+ * @returns {number} Current timestamp in milliseconds
50
+ */
51
+ function getServerSnapshot() {
52
+ return Date.now();
53
+ }
54
+
55
+ /**
56
+ * Add cadence hint for timer frequency
57
+ * @param {string} id - Unique identifier for the hint
58
+ * @param {number} ms - Cadence in milliseconds
59
+ */
60
+ function addCadenceHint(id, ms) {
61
+ cadenceHints.set(id, ms);
62
+ recalculateInterval();
63
+ }
64
+
65
+ /**
66
+ * Remove cadence hint
67
+ * @param {string} id - Unique identifier for the hint
68
+ */
69
+ function removeCadenceHint(id) {
70
+ cadenceHints.delete(id);
71
+ recalculateInterval();
72
+ }
73
+
74
+ /**
75
+ * Recalculate the active interval based on current hints and visibility state
76
+ */
77
+ function recalculateInterval() {
78
+ const minCadence = Math.min(...cadenceHints.values(), 1000);
79
+ const newIntervalMs = isBackground ? Math.max(minCadence, 60000) : minCadence;
80
+
81
+ if (newIntervalMs !== activeIntervalMs) {
82
+ activeIntervalMs = newIntervalMs;
83
+
84
+ // Restart timer if we have listeners
85
+ if (listeners.size > 0) {
86
+ stopTimer();
87
+ startTimer();
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Start the timer interval with minute boundary alignment for >= 60s cadence
94
+ */
95
+ function startTimer() {
96
+ if (timerId !== null) return;
97
+
98
+ const tick = () => {
99
+ currentNow = Math.floor(performance.now() + offset);
100
+
101
+ // Notify all listeners
102
+ listeners.forEach((listener) => {
103
+ try {
104
+ listener();
105
+ } catch (error) {
106
+ console.error("Error in time store listener:", error);
107
+ }
108
+ });
109
+ };
110
+
111
+ // Align to minute boundary for >= 60s cadence
112
+ if (activeIntervalMs >= 60000) {
113
+ const now = Date.now();
114
+ const nextMinuteBoundary = Math.ceil(now / 60000) * 60000;
115
+ const initialDelay = nextMinuteBoundary - now;
116
+
117
+ // Initial delay to align to minute boundary, then regular intervals
118
+ timerId = setTimeout(() => {
119
+ tick();
120
+ timerId = setInterval(tick, 60000);
121
+ }, initialDelay);
122
+ } else {
123
+ // Immediate start for sub-minute cadences
124
+ timerId = setInterval(tick, activeIntervalMs);
125
+ }
126
+ }
127
+ /**
128
+ * Stop the timer interval
129
+ */
130
+ function stopTimer() {
131
+ if (timerId !== null) {
132
+ clearTimeout(timerId);
133
+ clearInterval(timerId);
134
+ timerId = null;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Handle visibility change events
140
+ */
141
+ function handleVisibilityChange() {
142
+ const wasBackground = isBackground;
143
+ isBackground = document.visibilityState === "hidden";
144
+
145
+ if (wasBackground !== isBackground) {
146
+ recalculateInterval();
147
+ }
148
+ }
149
+
150
+ // Set up visibility change listener
151
+ if (typeof document !== "undefined") {
152
+ document.addEventListener("visibilitychange", handleVisibilityChange);
153
+ }
154
+
155
+ export {
156
+ subscribe,
157
+ getSnapshot,
158
+ getServerSnapshot,
159
+ addCadenceHint,
160
+ removeCadenceHint,
161
+ };
@@ -2,6 +2,12 @@
2
2
  * Universal configuration bridge for UI helpers.
3
3
  * Works in both browser and Node environments.
4
4
  */
5
+ import {
6
+ TaskState,
7
+ JobStatus,
8
+ JobLocation,
9
+ deriveJobStatusFromTasks,
10
+ } from "../config/statuses.js";
5
11
 
6
12
  /**
7
13
  * Global constants and contracts for project data display system
@@ -18,19 +24,24 @@ export const Constants = {
18
24
  * Valid task states
19
25
  * @type {string[]}
20
26
  */
21
- TASK_STATES: ["pending", "running", "done", "failed"],
27
+ TASK_STATES: Object.values(TaskState),
22
28
 
23
29
  /**
24
30
  * Valid job locations
25
31
  * @type {string[]}
26
32
  */
27
- JOB_LOCATIONS: ["current", "complete"],
33
+ JOB_LOCATIONS: Object.values(JobLocation),
28
34
 
29
35
  /**
30
36
  * Status sort order (descending priority)
31
37
  * @type {string[]}
32
38
  */
33
- STATUS_ORDER: ["running", "failed", "pending", "complete"],
39
+ STATUS_ORDER: [
40
+ JobStatus.RUNNING,
41
+ JobStatus.FAILED,
42
+ JobStatus.PENDING,
43
+ JobStatus.COMPLETE,
44
+ ],
34
45
 
35
46
  /**
36
47
  * File size limits for reading
@@ -104,27 +115,7 @@ export function getStatusPriority(status) {
104
115
  * @returns {string} Job status
105
116
  */
106
117
  export function determineJobStatus(tasks = {}) {
107
- const taskEntries = Object.entries(tasks);
108
-
109
- if (taskEntries.length === 0) {
110
- return "pending";
111
- }
112
-
113
- const taskStates = taskEntries.map(([_, task]) => task.state);
114
-
115
- if (taskStates.includes("failed")) {
116
- return "failed";
117
- }
118
-
119
- if (taskStates.includes("running")) {
120
- return "running";
121
- }
122
-
123
- if (taskStates.every((state) => state === "done")) {
124
- return "complete";
125
- }
126
-
127
- return "pending";
118
+ return deriveJobStatusFromTasks(Object.values(tasks));
128
119
  }
129
120
 
130
121
  /**
@@ -7,6 +7,12 @@
7
7
  import path from "node:path";
8
8
  import { promises as fs } from "node:fs";
9
9
  import { fileURLToPath } from "node:url";
10
+ import {
11
+ TaskState,
12
+ JobStatus,
13
+ JobLocation,
14
+ deriveJobStatusFromTasks,
15
+ } from "../config/statuses.js";
10
16
 
11
17
  /**
12
18
  * Global constants and contracts for project data display system
@@ -23,19 +29,24 @@ export const Constants = {
23
29
  * Valid task states
24
30
  * @type {string[]}
25
31
  */
26
- TASK_STATES: ["pending", "running", "done", "failed"],
32
+ TASK_STATES: Object.values(TaskState),
27
33
 
28
34
  /**
29
35
  * Valid job locations
30
36
  * @type {string[]}
31
37
  */
32
- JOB_LOCATIONS: ["current", "complete"],
38
+ JOB_LOCATIONS: Object.values(JobLocation),
33
39
 
34
40
  /**
35
41
  * Status sort order (descending priority)
36
42
  * @type {string[]}
37
43
  */
38
- STATUS_ORDER: ["running", "failed", "pending", "complete"],
44
+ STATUS_ORDER: [
45
+ JobStatus.RUNNING,
46
+ JobStatus.FAILED,
47
+ JobStatus.PENDING,
48
+ JobStatus.COMPLETE,
49
+ ],
39
50
 
40
51
  /**
41
52
  * File size limits for reading
@@ -240,27 +251,7 @@ export function getStatusPriority(status) {
240
251
  * @returns {string} Job status
241
252
  */
242
253
  export function determineJobStatus(tasks = {}) {
243
- const taskEntries = Object.entries(tasks);
244
-
245
- if (taskEntries.length === 0) {
246
- return "pending";
247
- }
248
-
249
- const taskStates = taskEntries.map(([_, task]) => task.state);
250
-
251
- if (taskStates.includes("failed")) {
252
- return "failed";
253
- }
254
-
255
- if (taskStates.includes("running")) {
256
- return "running";
257
- }
258
-
259
- if (taskStates.every((state) => state === "done")) {
260
- return "complete";
261
- }
262
-
263
- return "pending";
254
+ return deriveJobStatusFromTasks(Object.values(tasks));
264
255
  }
265
256
 
266
257
  // Export helper to resolve paths lazily for server use