@ryanfw/prompt-orchestration-pipeline 0.5.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.
- 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 +14 -12
- package/src/components/JobDetail.jsx +54 -51
- package/src/components/JobTable.jsx +72 -23
- package/src/components/Layout.jsx +145 -42
- package/src/components/LiveText.jsx +47 -0
- package/src/components/PageSubheader.jsx +75 -0
- package/src/components/TaskDetailSidebar.jsx +216 -0
- package/src/components/TimerText.jsx +82 -0
- package/src/components/UploadSeed.jsx +0 -70
- package/src/components/ui/Logo.jsx +16 -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 +4 -34
- package/src/core/file-io.js +13 -28
- package/src/core/module-loader.js +54 -40
- package/src/core/pipeline-runner.js +65 -26
- package/src/core/status-writer.js +213 -58
- package/src/core/symlink-bridge.js +57 -0
- package/src/core/symlink-utils.js +94 -0
- package/src/core/task-runner.js +321 -437
- package/src/llm/index.js +258 -86
- package/src/pages/Code.jsx +351 -0
- package/src/pages/PipelineDetail.jsx +124 -15
- package/src/pages/PromptPipelineDashboard.jsx +20 -88
- package/src/providers/anthropic.js +83 -69
- package/src/providers/base.js +52 -0
- package/src/providers/deepseek.js +20 -21
- 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 +42 -28
- package/src/ui/client/api.js +134 -0
- package/src/ui/client/hooks/useJobDetailWithUpdates.js +65 -179
- package/src/ui/client/index.css +15 -0
- package/src/ui/client/index.html +2 -1
- package/src/ui/client/main.jsx +19 -14
- 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-CxcrauYR.js → index-DqkbzXZ1.js} +2132 -1086
- package/src/ui/dist/assets/style-DBF9NQGk.css +62 -0
- package/src/ui/dist/index.html +4 -3
- package/src/ui/job-reader.js +0 -108
- package/src/ui/public/favicon.svg +12 -0
- package/src/ui/server.js +252 -0
- package/src/ui/sse-enhancer.js +0 -1
- package/src/ui/transformers/list-transformer.js +32 -12
- package/src/ui/transformers/status-transformer.js +29 -42
- 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 +294 -0
- package/src/utils/ui.jsx +18 -20
- 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-D6K_oQ12.css +0 -62
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { derivePipelineMetadata } from "../../../utils/pipelines.js";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
normalizeTaskState,
|
|
4
|
+
deriveJobStatusFromTasks,
|
|
5
|
+
} from "../../../config/statuses.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Normalize a raw task state into canonical enum.
|
|
7
9
|
* Returns { state, warning? } where warning is a string if normalization occurred.
|
|
8
10
|
*/
|
|
9
|
-
function
|
|
11
|
+
function normalizeTaskStateWithWarning(raw) {
|
|
10
12
|
if (!raw || typeof raw !== "string")
|
|
11
13
|
return { state: "pending", warning: "missing_state" };
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
const normalizedState = normalizeTaskState(raw);
|
|
16
|
+
|
|
17
|
+
if (raw !== normalizedState) {
|
|
18
|
+
return { state: normalizedState, warning: `unknown_state:${raw}` };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return { state: normalizedState };
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
/**
|
|
@@ -28,7 +35,7 @@ function normalizeTasks(rawTasks) {
|
|
|
28
35
|
// Object shape - canonical format
|
|
29
36
|
const tasks = {};
|
|
30
37
|
Object.entries(rawTasks).forEach(([name, t]) => {
|
|
31
|
-
const ns =
|
|
38
|
+
const ns = normalizeTaskStateWithWarning(t && t.state);
|
|
32
39
|
if (ns.warning) warnings.push(`${name}:${ns.warning}`);
|
|
33
40
|
const taskObj = {
|
|
34
41
|
name,
|
|
@@ -66,6 +73,8 @@ function normalizeTasks(rawTasks) {
|
|
|
66
73
|
artifacts: Array.isArray(t && t.artifacts)
|
|
67
74
|
? t.artifacts.slice()
|
|
68
75
|
: undefined,
|
|
76
|
+
// Preserve tokenUsage if present
|
|
77
|
+
...(t && t.tokenUsage ? { tokenUsage: t.tokenUsage } : {}),
|
|
69
78
|
};
|
|
70
79
|
tasks[name] = taskObj;
|
|
71
80
|
});
|
|
@@ -77,7 +86,7 @@ function normalizeTasks(rawTasks) {
|
|
|
77
86
|
const tasks = {};
|
|
78
87
|
rawTasks.forEach((t, idx) => {
|
|
79
88
|
const name = t && t.name ? String(t.name) : `task-${idx}`;
|
|
80
|
-
const ns =
|
|
89
|
+
const ns = normalizeTaskStateWithWarning(t && t.state);
|
|
81
90
|
if (ns.warning) warnings.push(`${name}:${ns.warning}`);
|
|
82
91
|
tasks[name] = {
|
|
83
92
|
name,
|
|
@@ -100,6 +109,8 @@ function normalizeTasks(rawTasks) {
|
|
|
100
109
|
artifacts: Array.isArray(t && t.artifacts)
|
|
101
110
|
? t.artifacts.slice()
|
|
102
111
|
: undefined,
|
|
112
|
+
// Preserve tokenUsage if present
|
|
113
|
+
...(t && t.tokenUsage ? { tokenUsage: t.tokenUsage } : {}),
|
|
103
114
|
};
|
|
104
115
|
});
|
|
105
116
|
return { tasks, warnings };
|
|
@@ -108,23 +119,6 @@ function normalizeTasks(rawTasks) {
|
|
|
108
119
|
return { tasks: {}, warnings: ["invalid_tasks_shape"] };
|
|
109
120
|
}
|
|
110
121
|
|
|
111
|
-
/**
|
|
112
|
-
* Derive status from tasks when status is missing/invalid.
|
|
113
|
-
* Rules:
|
|
114
|
-
* - failed if any task state === 'failed'
|
|
115
|
-
* - running if >=1 running and none failed
|
|
116
|
-
* - complete if all done
|
|
117
|
-
* - pending otherwise
|
|
118
|
-
*/
|
|
119
|
-
function deriveStatusFromTasks(tasks) {
|
|
120
|
-
const taskList = Object.values(tasks);
|
|
121
|
-
if (!Array.isArray(taskList) || taskList.length === 0) return "pending";
|
|
122
|
-
if (taskList.some((t) => t.state === "failed")) return "failed";
|
|
123
|
-
if (taskList.some((t) => t.state === "running")) return "running";
|
|
124
|
-
if (taskList.every((t) => t.state === "done")) return "complete";
|
|
125
|
-
return "pending";
|
|
126
|
-
}
|
|
127
|
-
|
|
128
122
|
/**
|
|
129
123
|
* Clamp number to 0..100 and ensure integer.
|
|
130
124
|
*/
|
|
@@ -143,7 +137,7 @@ function computeJobSummaryStats(tasks) {
|
|
|
143
137
|
(acc, t) => acc + (t.state === "done" ? 1 : 0),
|
|
144
138
|
0
|
|
145
139
|
);
|
|
146
|
-
const status =
|
|
140
|
+
const status = deriveJobStatusFromTasks(Object.values(tasks));
|
|
147
141
|
const progress =
|
|
148
142
|
taskCount > 0 ? Math.round((doneCount / taskCount) * 100) : 0;
|
|
149
143
|
return { status, progress, doneCount, taskCount };
|
|
@@ -158,7 +152,7 @@ export function adaptJobSummary(apiJob) {
|
|
|
158
152
|
// Demo-only: read canonical fields strictly
|
|
159
153
|
const id = apiJob.jobId;
|
|
160
154
|
const name = apiJob.title || "";
|
|
161
|
-
const rawTasks = apiJob.
|
|
155
|
+
const rawTasks = apiJob.tasks;
|
|
162
156
|
const location = apiJob.location;
|
|
163
157
|
|
|
164
158
|
// Job-level stage metadata
|
|
@@ -197,6 +191,21 @@ export function adaptJobSummary(apiJob) {
|
|
|
197
191
|
if (pipeline != null) job.pipeline = pipeline;
|
|
198
192
|
if (pipelineLabel != null) job.pipelineLabel = pipelineLabel;
|
|
199
193
|
|
|
194
|
+
// Costs summary from API
|
|
195
|
+
if (apiJob.costsSummary) {
|
|
196
|
+
job.costsSummary = {
|
|
197
|
+
totalTokens: apiJob.costsSummary.totalTokens || 0,
|
|
198
|
+
totalInputTokens: apiJob.costsSummary.totalInputTokens || 0,
|
|
199
|
+
totalOutputTokens: apiJob.costsSummary.totalOutputTokens || 0,
|
|
200
|
+
totalCost: apiJob.costsSummary.totalCost || 0,
|
|
201
|
+
totalInputCost: apiJob.costsSummary.totalInputCost || 0,
|
|
202
|
+
totalOutputCost: apiJob.costsSummary.totalOutputCost || 0,
|
|
203
|
+
};
|
|
204
|
+
// Add top-level numeric mirrors for convenience
|
|
205
|
+
job.totalCost = job.costsSummary.totalCost;
|
|
206
|
+
job.totalTokens = job.costsSummary.totalTokens;
|
|
207
|
+
}
|
|
208
|
+
|
|
200
209
|
// Include warnings for debugging
|
|
201
210
|
if (warnings.length > 0) job.__warnings = warnings;
|
|
202
211
|
|
|
@@ -212,7 +221,7 @@ export function adaptJobDetail(apiDetail) {
|
|
|
212
221
|
// Demo-only: read canonical fields strictly
|
|
213
222
|
const id = apiDetail.jobId;
|
|
214
223
|
const name = apiDetail.title || "";
|
|
215
|
-
const rawTasks = apiDetail.
|
|
224
|
+
const rawTasks = apiDetail.tasks;
|
|
216
225
|
const location = apiDetail.location;
|
|
217
226
|
|
|
218
227
|
// Job-level stage metadata
|
|
@@ -251,6 +260,11 @@ export function adaptJobDetail(apiDetail) {
|
|
|
251
260
|
if (pipeline != null) detail.pipeline = pipeline;
|
|
252
261
|
if (pipelineLabel != null) detail.pipelineLabel = pipelineLabel;
|
|
253
262
|
|
|
263
|
+
// Preserve job detail costs
|
|
264
|
+
if (apiDetail.costs) {
|
|
265
|
+
detail.costs = apiDetail.costs;
|
|
266
|
+
}
|
|
267
|
+
|
|
254
268
|
// Include warnings for debugging
|
|
255
269
|
if (warnings.length > 0) detail.__warnings = warnings;
|
|
256
270
|
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side API helpers for making HTTP requests to the backend
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Restart a job with clean-slate mode
|
|
7
|
+
*
|
|
8
|
+
* @param {string} jobId - The ID of the job to restart
|
|
9
|
+
* @param {Object} opts - Options object
|
|
10
|
+
* @param {Object} opts.options - Additional options for the restart
|
|
11
|
+
* @param {boolean} opts.options.clearTokenUsage - Whether to clear token usage (default: true)
|
|
12
|
+
* @returns {Promise<Object>} Parsed JSON response from the server
|
|
13
|
+
* @throws {Object} Structured error object with { code, message } for non-2xx responses
|
|
14
|
+
*/
|
|
15
|
+
export async function restartJob(jobId, opts = {}) {
|
|
16
|
+
const options = {
|
|
17
|
+
clearTokenUsage: true,
|
|
18
|
+
...opts.options,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const requestBody = opts.fromTask
|
|
22
|
+
? { fromTask: opts.fromTask, options }
|
|
23
|
+
: { mode: "clean-slate", options };
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const response = await fetch(
|
|
27
|
+
`/api/jobs/${encodeURIComponent(jobId)}/restart`,
|
|
28
|
+
{
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: {
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify(requestBody),
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
// Try to parse error response, fall back to status text if parsing fails
|
|
39
|
+
let errorData;
|
|
40
|
+
try {
|
|
41
|
+
errorData = await response.json();
|
|
42
|
+
} catch {
|
|
43
|
+
errorData = { message: response.statusText };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Throw structured error with code and message
|
|
47
|
+
throw {
|
|
48
|
+
code: errorData.code || getErrorCodeFromStatus(response.status),
|
|
49
|
+
message: getRestartErrorMessage(errorData, response.status),
|
|
50
|
+
status: response.status,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Return parsed JSON for successful responses
|
|
55
|
+
return await response.json();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
// Re-throw structured errors as-is
|
|
58
|
+
if (error.code && error.message) {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle network errors or other unexpected errors
|
|
63
|
+
throw {
|
|
64
|
+
code: "network_error",
|
|
65
|
+
message: error.message || "Failed to connect to server",
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Map HTTP status codes to error codes for structured error handling
|
|
72
|
+
*/
|
|
73
|
+
function getErrorCodeFromStatus(status) {
|
|
74
|
+
switch (status) {
|
|
75
|
+
case 404:
|
|
76
|
+
return "job_not_found";
|
|
77
|
+
case 409:
|
|
78
|
+
return "conflict";
|
|
79
|
+
case 500:
|
|
80
|
+
return "spawn_failed";
|
|
81
|
+
default:
|
|
82
|
+
return "unknown_error";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Map HTTP status codes to error messages for structured error handling
|
|
88
|
+
*/
|
|
89
|
+
function getErrorMessageFromStatus(status) {
|
|
90
|
+
switch (status) {
|
|
91
|
+
case 404:
|
|
92
|
+
return "Job not found";
|
|
93
|
+
case 409:
|
|
94
|
+
return "Job restart conflict";
|
|
95
|
+
case 500:
|
|
96
|
+
return "Failed to start restart";
|
|
97
|
+
default:
|
|
98
|
+
return `Request failed with status ${status}`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get specific error message from error response for restart functionality
|
|
104
|
+
*/
|
|
105
|
+
function getRestartErrorMessage(errorData, status) {
|
|
106
|
+
// Handle specific 409 conflict errors
|
|
107
|
+
if (status === 409) {
|
|
108
|
+
if (errorData.code === "job_running") {
|
|
109
|
+
return "Job is currently running; restart is unavailable.";
|
|
110
|
+
}
|
|
111
|
+
if (errorData.code === "unsupported_lifecycle") {
|
|
112
|
+
return "Job must be in current to restart.";
|
|
113
|
+
}
|
|
114
|
+
if (errorData.message?.includes("job_running")) {
|
|
115
|
+
return "Job is currently running; restart is unavailable.";
|
|
116
|
+
}
|
|
117
|
+
if (errorData.message?.includes("unsupported_lifecycle")) {
|
|
118
|
+
return "Job must be in current to restart.";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Handle 404 errors
|
|
123
|
+
if (status === 404) {
|
|
124
|
+
return "Job not found.";
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Handle 500 errors
|
|
128
|
+
if (status === 500) {
|
|
129
|
+
return "Failed to start restart. Try again.";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Fall back to provided message or default
|
|
133
|
+
return errorData.message || "Failed to restart job.";
|
|
134
|
+
}
|