@ryanfw/prompt-orchestration-pipeline 0.6.0 → 0.8.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.
- package/README.md +1 -2
- package/package.json +1 -2
- package/src/api/validators/json.js +39 -0
- package/src/components/DAGGrid.jsx +392 -303
- package/src/components/JobCard.jsx +13 -11
- package/src/components/JobDetail.jsx +41 -71
- package/src/components/JobTable.jsx +32 -22
- package/src/components/Layout.jsx +0 -21
- package/src/components/LiveText.jsx +47 -0
- package/src/components/TaskDetailSidebar.jsx +216 -0
- package/src/components/TimerText.jsx +82 -0
- package/src/components/ui/RestartJobModal.jsx +140 -0
- package/src/components/ui/toast.jsx +138 -0
- package/src/config/models.js +322 -0
- package/src/config/statuses.js +119 -0
- package/src/core/config.js +2 -164
- package/src/core/file-io.js +1 -1
- package/src/core/module-loader.js +54 -40
- package/src/core/pipeline-runner.js +52 -26
- package/src/core/status-writer.js +147 -3
- package/src/core/symlink-bridge.js +55 -0
- package/src/core/symlink-utils.js +94 -0
- package/src/core/task-runner.js +267 -443
- package/src/llm/index.js +167 -52
- package/src/pages/Code.jsx +57 -3
- package/src/pages/PipelineDetail.jsx +92 -22
- package/src/pages/PromptPipelineDashboard.jsx +15 -36
- package/src/providers/anthropic.js +83 -69
- package/src/providers/base.js +52 -0
- package/src/providers/deepseek.js +17 -34
- package/src/providers/gemini.js +226 -0
- package/src/providers/openai.js +36 -106
- package/src/providers/zhipu.js +136 -0
- package/src/ui/client/adapters/job-adapter.js +16 -26
- package/src/ui/client/api.js +134 -0
- package/src/ui/client/hooks/useJobDetailWithUpdates.js +65 -178
- package/src/ui/client/index.css +9 -0
- package/src/ui/client/index.html +1 -0
- package/src/ui/client/main.jsx +18 -15
- package/src/ui/client/time-store.js +161 -0
- package/src/ui/config-bridge.js +15 -24
- package/src/ui/config-bridge.node.js +15 -24
- package/src/ui/dist/assets/{index-WgJUlSmE.js → index-DqkbzXZ1.js} +1408 -771
- package/src/ui/dist/assets/style-DBF9NQGk.css +62 -0
- package/src/ui/dist/index.html +3 -2
- package/src/ui/public/favicon.svg +12 -0
- package/src/ui/server.js +231 -38
- package/src/ui/transformers/status-transformer.js +18 -31
- package/src/ui/watcher.js +5 -1
- package/src/utils/dag.js +8 -4
- package/src/utils/duration.js +13 -19
- package/src/utils/formatters.js +27 -0
- package/src/utils/geometry-equality.js +83 -0
- package/src/utils/pipelines.js +5 -1
- package/src/utils/time-utils.js +40 -0
- package/src/utils/token-cost-calculator.js +4 -7
- package/src/utils/ui.jsx +14 -16
- package/src/components/ui/select.jsx +0 -27
- package/src/lib/utils.js +0 -6
- package/src/ui/client/hooks/useTicker.js +0 -26
- package/src/ui/config-bridge.browser.js +0 -149
- 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 {
|
|
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 ||
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
40
|
-
case "complete":
|
|
39
|
+
case TaskState.DONE:
|
|
41
40
|
return <CheckCircle2 className="h-4 w-4 text-success" aria-hidden />;
|
|
42
|
-
case
|
|
41
|
+
case TaskState.RUNNING:
|
|
43
42
|
return <Loader2 className="h-4 w-4 animate-spin text-info" aria-hidden />;
|
|
44
|
-
case
|
|
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
|
|
52
|
+
case TaskState.RUNNING:
|
|
54
53
|
return "bg-info/20 [&>div]:bg-info";
|
|
55
|
-
case
|
|
54
|
+
case TaskState.FAILED:
|
|
56
55
|
return "bg-destructive/20 [&>div]:bg-destructive";
|
|
57
|
-
case
|
|
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
|
|
65
|
+
case TaskState.RUNNING:
|
|
67
66
|
return "bg-info";
|
|
68
|
-
case
|
|
67
|
+
case TaskState.FAILED:
|
|
69
68
|
return "bg-destructive";
|
|
70
|
-
case
|
|
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,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
|
-
}
|