@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,40 @@
1
+ /**
2
+ * Time utilities for handling timestamp conversions
3
+ */
4
+
5
+ /**
6
+ * Converts a timestamp string or number to milliseconds since epoch
7
+ * @param {string|number|null|undefined} timestamp - ISO string, milliseconds, or null/undefined
8
+ * @returns {number|null} Milliseconds since epoch, or null if input is invalid
9
+ */
10
+ export function toMilliseconds(timestamp) {
11
+ if (timestamp === null || timestamp === undefined) {
12
+ return null;
13
+ }
14
+
15
+ // If it's already a number, return as-is
16
+ if (typeof timestamp === "number") {
17
+ return isNaN(timestamp) ? null : timestamp;
18
+ }
19
+
20
+ // If it's a string, try to parse it as an ISO date
21
+ if (typeof timestamp === "string") {
22
+ const parsed = Date.parse(timestamp);
23
+ return isNaN(parsed) ? null : parsed;
24
+ }
25
+
26
+ // Invalid type
27
+ return null;
28
+ }
29
+
30
+ /**
31
+ * Safely converts startedAt/endedAt timestamps for TimerText components
32
+ * @param {Object} task - Task object with startedAt and/or endedAt
33
+ * @returns {Object} Object with startMs and endMs as numbers or null
34
+ */
35
+ export function taskToTimerProps(task) {
36
+ return {
37
+ startMs: toMilliseconds(task?.startedAt),
38
+ endMs: toMilliseconds(task?.endedAt),
39
+ };
40
+ }
@@ -5,7 +5,7 @@
5
5
  * by cross-referencing with LLM model pricing configuration.
6
6
  */
7
7
 
8
- import { getConfig } from "../core/config.js";
8
+ import { MODEL_CONFIG } from "../config/models.js";
9
9
 
10
10
  /**
11
11
  * Calculate cost for a single token usage entry
@@ -29,7 +29,7 @@ export function calculateSingleTokenCost(tokenUsageEntry, modelsConfig = null) {
29
29
  const [modelKey, inputTokens, outputTokens] = tokenUsageEntry;
30
30
 
31
31
  // Get models config if not provided
32
- const config = modelsConfig || getConfig()?.llm?.models || {};
32
+ const config = modelsConfig || MODEL_CONFIG;
33
33
  const modelConfig = config[modelKey];
34
34
 
35
35
  if (!modelConfig) {
@@ -259,8 +259,7 @@ export function formatCostDataForAPI(costData) {
259
259
  * @returns {Object|null} Model pricing information
260
260
  */
261
261
  export function getModelPricing(modelKey) {
262
- const config = getConfig()?.llm?.models || {};
263
- const modelConfig = config[modelKey];
262
+ const modelConfig = MODEL_CONFIG[modelKey];
264
263
 
265
264
  if (!modelConfig) {
266
265
  return null;
@@ -280,10 +279,8 @@ export function getModelPricing(modelKey) {
280
279
  * @returns {Object} All model pricing information
281
280
  */
282
281
  export function getAllModelPricing() {
283
- const config = getConfig()?.llm?.models || {};
284
-
285
282
  const pricing = {};
286
- for (const [modelKey, modelConfig] of Object.entries(config)) {
283
+ for (const [modelKey, modelConfig] of Object.entries(MODEL_CONFIG)) {
287
284
  pricing[modelKey] = {
288
285
  modelKey,
289
286
  provider: modelConfig.provider,
package/src/utils/ui.jsx CHANGED
@@ -1,29 +1,29 @@
1
1
  import React from "react";
2
2
  import { Badge } from "../components/ui/badge.jsx";
3
3
  import { CheckCircle2, Loader2, AlertTriangle, Circle } from "lucide-react";
4
+ import { TaskState } from "../config/statuses.js";
4
5
 
5
6
  export const statusBadge = (status) => {
6
7
  switch (status) {
7
- case "running":
8
+ case TaskState.RUNNING:
8
9
  return (
9
10
  <Badge intent="blue" aria-label="Running">
10
11
  Running
11
12
  </Badge>
12
13
  );
13
- case "failed":
14
+ case TaskState.FAILED:
14
15
  return (
15
16
  <Badge intent="red" aria-label="Failed">
16
17
  Failed
17
18
  </Badge>
18
19
  );
19
- case "completed":
20
- case "complete":
20
+ case TaskState.DONE:
21
21
  return (
22
22
  <Badge intent="green" aria-label="Completed">
23
23
  Completed
24
24
  </Badge>
25
25
  );
26
- case "pending":
26
+ case TaskState.PENDING:
27
27
  return (
28
28
  <Badge intent="gray" aria-label="Pending">
29
29
  Pending
@@ -36,12 +36,11 @@ export const statusBadge = (status) => {
36
36
 
37
37
  export const taskStatusIcon = (state) => {
38
38
  switch (state) {
39
- case "completed":
40
- case "complete":
39
+ case TaskState.DONE:
41
40
  return <CheckCircle2 className="h-4 w-4 text-success" aria-hidden />;
42
- case "running":
41
+ case TaskState.RUNNING:
43
42
  return <Loader2 className="h-4 w-4 animate-spin text-info" aria-hidden />;
44
- case "failed":
43
+ case TaskState.FAILED:
45
44
  return <AlertTriangle className="h-4 w-4 text-destructive" aria-hidden />;
46
45
  default:
47
46
  return <Circle className="h-4 w-4 text-slate-500" aria-hidden />;
@@ -50,11 +49,11 @@ export const taskStatusIcon = (state) => {
50
49
 
51
50
  export const progressClasses = (status) => {
52
51
  switch (status) {
53
- case "running":
52
+ case TaskState.RUNNING:
54
53
  return "bg-info/20 [&>div]:bg-info";
55
- case "failed":
54
+ case TaskState.FAILED:
56
55
  return "bg-destructive/20 [&>div]:bg-destructive";
57
- case "completed":
56
+ case TaskState.DONE:
58
57
  return "bg-success/20 [&>div]:bg-success";
59
58
  default:
60
59
  return "bg-muted [&>div]:bg-muted-foreground";
@@ -63,12 +62,11 @@ export const progressClasses = (status) => {
63
62
 
64
63
  export const barColorForState = (state) => {
65
64
  switch (state) {
66
- case "running":
65
+ case TaskState.RUNNING:
67
66
  return "bg-info";
68
- case "failed":
67
+ case TaskState.FAILED:
69
68
  return "bg-destructive";
70
- case "completed":
71
- case "complete":
69
+ case TaskState.DONE:
72
70
  return "bg-success";
73
71
  default:
74
72
  return "bg-muted-foreground";
@@ -1,27 +0,0 @@
1
- import React from "react";
2
- export function Select({ value, onValueChange, children, className = "" }) {
3
- return (
4
- <select
5
- className={[
6
- "h-9 rounded-md border px-3 text-sm bg-white",
7
- className,
8
- ].join(" ")}
9
- value={value}
10
- onChange={(e) => onValueChange?.(e.target.value)}
11
- >
12
- {children}
13
- </select>
14
- );
15
- }
16
- export function SelectItem({ value, children }) {
17
- return <option value={value}>{children}</option>;
18
- }
19
- export function SelectTrigger({ children, ...p }) {
20
- return <>{children}</>;
21
- } // keep API compatible
22
- export function SelectContent({ children }) {
23
- return <>{children}</>;
24
- }
25
- export function SelectValue({ placeholder }) {
26
- return <>{placeholder}</>;
27
- }
package/src/lib/utils.js DELETED
@@ -1,6 +0,0 @@
1
- import { clsx } from "clsx";
2
- import { twMerge } from "tailwind-merge";
3
-
4
- export function cn(...inputs) {
5
- return twMerge(clsx(inputs));
6
- }
@@ -1,26 +0,0 @@
1
- import { useState, useEffect, useRef } from "react";
2
-
3
- /**
4
- * Reactive ticker hook that provides updating timestamp
5
- * @param {number} intervalMs - Update interval in milliseconds (default: 1000)
6
- * @returns {number} Current timestamp that updates on interval
7
- */
8
- export function useTicker(intervalMs = 1000) {
9
- const [now, setNow] = useState(() => Date.now());
10
-
11
- useEffect(() => {
12
- // Set up interval to update timestamp
13
- const intervalId = setInterval(() => {
14
- setNow(Date.now());
15
- }, intervalMs);
16
-
17
- // Cleanup interval on unmount
18
- return () => {
19
- clearInterval(intervalId);
20
- };
21
- }, [intervalMs]);
22
-
23
- return now;
24
- }
25
-
26
- export default useTicker;
@@ -1,149 +0,0 @@
1
- /**
2
- * Browser-specific configuration bridge for UI helpers.
3
- * Contains only browser-safe utilities (no Node APIs).
4
- */
5
-
6
- /**
7
- * Global constants and contracts for project data display system
8
- * @namespace Constants
9
- */
10
- export const Constants = {
11
- /**
12
- * Job ID validation regex
13
- * @type {RegExp}
14
- */
15
- JOB_ID_REGEX: /^[A-Za-z0-9-_]+$/,
16
-
17
- /**
18
- * Valid task states
19
- * @type {string[]}
20
- */
21
- TASK_STATES: ["pending", "running", "done", "failed"],
22
-
23
- /**
24
- * Valid job locations
25
- * @type {string[]}
26
- */
27
- JOB_LOCATIONS: ["current", "complete"],
28
-
29
- /**
30
- * Status sort order (descending priority)
31
- * @type {string[]}
32
- */
33
- STATUS_ORDER: ["running", "failed", "pending", "complete"],
34
-
35
- /**
36
- * File size limits for reading
37
- * @type {Object}
38
- */
39
- FILE_LIMITS: {
40
- MAX_FILE_SIZE: 5 * 1024 * 1024, // 5MB
41
- },
42
-
43
- /**
44
- * Retry configuration for atomic reads
45
- * @type {Object}
46
- */
47
- RETRY_CONFIG: {
48
- MAX_ATTEMPTS: 3,
49
- DELAY_MS: 1000,
50
- },
51
-
52
- /**
53
- * SSE debounce configuration
54
- * @type {Object}
55
- */
56
- SSE_CONFIG: {
57
- DEBOUNCE_MS: 200,
58
- },
59
-
60
- /**
61
- * Error codes for structured error responses
62
- * @type {Object}
63
- */
64
- ERROR_CODES: {
65
- NOT_FOUND: "not_found",
66
- INVALID_JSON: "invalid_json",
67
- FS_ERROR: "fs_error",
68
- JOB_NOT_FOUND: "job_not_found",
69
- BAD_REQUEST: "bad_request",
70
- },
71
- };
72
-
73
- /**
74
- * Validates a job ID against the global contract
75
- * @param {string} jobId - Job ID to validate
76
- * @returns {boolean} True if valid
77
- */
78
- export function validateJobId(jobId) {
79
- return Constants.JOB_ID_REGEX.test(jobId);
80
- }
81
-
82
- /**
83
- * Validates a task state against the global contract
84
- * @param {string} state - Task state to validate
85
- * @returns {boolean} True if valid
86
- */
87
- export function validateTaskState(state) {
88
- return Constants.TASK_STATES.includes(state);
89
- }
90
-
91
- /**
92
- * Gets the status sort priority for a job status
93
- * @param {string} status - Job status
94
- * @returns {number} Sort priority (lower number = higher priority)
95
- */
96
- export function getStatusPriority(status) {
97
- const index = Constants.STATUS_ORDER.indexOf(status);
98
- return index === -1 ? Constants.STATUS_ORDER.length : index;
99
- }
100
-
101
- /**
102
- * Determines job status based on task states
103
- * @param {Object} tasks - Tasks object from tasks-status.json
104
- * @returns {string} Job status
105
- */
106
- 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";
128
- }
129
-
130
- /**
131
- * Creates a structured error response
132
- * @param {string} code - Error code
133
- * @param {string} message - Error message
134
- * @param {string} [path] - Optional file path
135
- * @returns {Object} Structured error object
136
- */
137
- export function createErrorResponse(code, message, path = null) {
138
- const error = {
139
- ok: false,
140
- code,
141
- message,
142
- };
143
-
144
- if (path) {
145
- error.path = path;
146
- }
147
-
148
- return error;
149
- }