@ryanfw/prompt-orchestration-pipeline 0.5.0 → 0.6.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/package.json +1 -1
- package/src/components/JobCard.jsx +1 -1
- package/src/components/JobDetail.jsx +45 -12
- package/src/components/JobTable.jsx +40 -1
- package/src/components/Layout.jsx +146 -22
- package/src/components/PageSubheader.jsx +75 -0
- package/src/components/UploadSeed.jsx +0 -70
- package/src/components/ui/Logo.jsx +16 -0
- package/src/core/config.js +145 -13
- package/src/core/file-io.js +12 -27
- package/src/core/pipeline-runner.js +13 -6
- package/src/core/status-writer.js +63 -52
- package/src/core/task-runner.js +61 -1
- package/src/llm/index.js +97 -40
- package/src/pages/Code.jsx +297 -0
- package/src/pages/PipelineDetail.jsx +47 -8
- package/src/pages/PromptPipelineDashboard.jsx +6 -53
- package/src/providers/deepseek.js +17 -1
- package/src/providers/openai.js +1 -1
- package/src/ui/client/adapters/job-adapter.js +26 -2
- package/src/ui/client/hooks/useJobDetailWithUpdates.js +0 -1
- package/src/ui/client/index.css +6 -0
- package/src/ui/client/index.html +1 -1
- package/src/ui/client/main.jsx +2 -0
- package/src/ui/dist/assets/{index-CxcrauYR.js → index-WgJUlSmE.js} +716 -307
- package/src/ui/dist/assets/style-x0V-5m8e.css +62 -0
- package/src/ui/dist/index.html +3 -3
- package/src/ui/job-reader.js +0 -108
- package/src/ui/server.js +54 -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 +11 -11
- package/src/utils/token-cost-calculator.js +297 -0
- package/src/utils/ui.jsx +4 -4
- package/src/ui/dist/assets/style-D6K_oQ12.css +0 -62
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import React, { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { useNavigate } from "react-router-dom";
|
|
4
4
|
|
|
5
|
-
import { Box, Flex, Text, Heading, Tabs
|
|
5
|
+
import { Box, Flex, Text, Heading, Tabs } from "@radix-ui/themes";
|
|
6
6
|
|
|
7
7
|
import { Progress } from "../components/ui/progress";
|
|
8
8
|
import { useJobListWithUpdates } from "../ui/client/hooks/useJobListWithUpdates";
|
|
@@ -12,7 +12,6 @@ import { useTicker } from "../ui/client/hooks/useTicker";
|
|
|
12
12
|
|
|
13
13
|
// Referenced components — leave these alone
|
|
14
14
|
import JobTable from "../components/JobTable";
|
|
15
|
-
import UploadSeed from "../components/UploadSeed";
|
|
16
15
|
import Layout from "../components/Layout.jsx";
|
|
17
16
|
|
|
18
17
|
export default function PromptPipelineDashboard({ isConnected }) {
|
|
@@ -55,8 +54,6 @@ export default function PromptPipelineDashboard({ isConnected }) {
|
|
|
55
54
|
return src.map(adaptJobSummary);
|
|
56
55
|
}, [apiJobs, error]);
|
|
57
56
|
const [activeTab, setActiveTab] = useState("current");
|
|
58
|
-
const [seedUploadSuccess, setSeedUploadSuccess] = useState(null);
|
|
59
|
-
const [seedUploadTimer, setSeedUploadTimer] = useState(null);
|
|
60
57
|
|
|
61
58
|
// Shared ticker for live duration updates
|
|
62
59
|
const now = useTicker(10000);
|
|
@@ -111,34 +108,6 @@ export default function PromptPipelineDashboard({ isConnected }) {
|
|
|
111
108
|
}
|
|
112
109
|
};
|
|
113
110
|
|
|
114
|
-
// Handle seed upload success
|
|
115
|
-
const handleSeedUploadSuccess = ({ jobName }) => {
|
|
116
|
-
// Clear any existing timer
|
|
117
|
-
if (seedUploadTimer) {
|
|
118
|
-
clearTimeout(seedUploadTimer);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Set success message
|
|
122
|
-
setSeedUploadSuccess(jobName);
|
|
123
|
-
|
|
124
|
-
// Auto-clear after exactly 5000 ms
|
|
125
|
-
const timer = setTimeout(() => {
|
|
126
|
-
setSeedUploadSuccess(null);
|
|
127
|
-
setSeedUploadTimer(null);
|
|
128
|
-
}, 5000);
|
|
129
|
-
|
|
130
|
-
setSeedUploadTimer(timer);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// Cleanup timer on unmount
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
return () => {
|
|
136
|
-
if (seedUploadTimer) {
|
|
137
|
-
clearTimeout(seedUploadTimer);
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
}, [seedUploadTimer]);
|
|
141
|
-
|
|
142
111
|
// Header actions for Layout
|
|
143
112
|
const headerActions = runningJobs.length > 0 && (
|
|
144
113
|
<Flex align="center" gap="2" className="text-gray-11">
|
|
@@ -154,26 +123,6 @@ export default function PromptPipelineDashboard({ isConnected }) {
|
|
|
154
123
|
|
|
155
124
|
return (
|
|
156
125
|
<Layout title="Prompt Pipeline" actions={headerActions}>
|
|
157
|
-
{/* Upload Seed File Section */}
|
|
158
|
-
<Card className="mb-6">
|
|
159
|
-
<Flex direction="column" gap="3">
|
|
160
|
-
<Heading size="4" weight="medium" className="text-gray-12">
|
|
161
|
-
Upload Seed File
|
|
162
|
-
</Heading>
|
|
163
|
-
|
|
164
|
-
{/* Success Message */}
|
|
165
|
-
{seedUploadSuccess && (
|
|
166
|
-
<Box className="rounded-md bg-green-50 p-3 border border-green-200">
|
|
167
|
-
<Text size="2" className="text-green-800">
|
|
168
|
-
Job <strong>{seedUploadSuccess}</strong> created successfully
|
|
169
|
-
</Text>
|
|
170
|
-
</Box>
|
|
171
|
-
)}
|
|
172
|
-
|
|
173
|
-
<UploadSeed onUploadSuccess={handleSeedUploadSuccess} />
|
|
174
|
-
</Flex>
|
|
175
|
-
</Card>
|
|
176
|
-
|
|
177
126
|
{error && (
|
|
178
127
|
<Box className="mb-4 rounded-md bg-yellow-50 p-3 border border-yellow-200">
|
|
179
128
|
<Text size="2" className="text-yellow-800">
|
|
@@ -181,7 +130,11 @@ export default function PromptPipelineDashboard({ isConnected }) {
|
|
|
181
130
|
</Text>
|
|
182
131
|
</Box>
|
|
183
132
|
)}
|
|
184
|
-
<Tabs.Root
|
|
133
|
+
<Tabs.Root
|
|
134
|
+
value={activeTab}
|
|
135
|
+
onValueChange={setActiveTab}
|
|
136
|
+
className="mt-4"
|
|
137
|
+
>
|
|
185
138
|
<Tabs.List aria-label="Job filters">
|
|
186
139
|
<Tabs.Trigger value="current">Current ({currentCount})</Tabs.Trigger>
|
|
187
140
|
<Tabs.Trigger value="errors">Errors ({errorCount})</Tabs.Trigger>
|
|
@@ -71,9 +71,25 @@ export async function deepseekChat({
|
|
|
71
71
|
const data = await response.json();
|
|
72
72
|
const content = data.choices[0].message.content;
|
|
73
73
|
|
|
74
|
+
// Only try JSON parsing if responseFormat indicates JSON output
|
|
75
|
+
if (responseFormat?.type === "json_object" || responseFormat === "json") {
|
|
76
|
+
const parsed = tryParseJSON(content);
|
|
77
|
+
if (parsed === null && attempt < maxRetries) {
|
|
78
|
+
// JSON parsing failed, retry
|
|
79
|
+
lastError = new Error("Failed to parse JSON response");
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
content: parsed,
|
|
84
|
+
usage: data.usage,
|
|
85
|
+
raw: data,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
74
89
|
return {
|
|
75
|
-
content
|
|
90
|
+
content,
|
|
76
91
|
usage: data.usage,
|
|
92
|
+
raw: data,
|
|
77
93
|
};
|
|
78
94
|
} catch (error) {
|
|
79
95
|
lastError = error;
|
package/src/providers/openai.js
CHANGED
|
@@ -31,7 +31,7 @@ export async function openaiChat({
|
|
|
31
31
|
temperature,
|
|
32
32
|
maxTokens,
|
|
33
33
|
max_tokens, // Explicitly destructure to prevent it from being in ...rest
|
|
34
|
-
responseFormat, //
|
|
34
|
+
responseFormat = "json", // Default to JSON for legacy compatibility
|
|
35
35
|
tools,
|
|
36
36
|
toolChoice,
|
|
37
37
|
seed,
|
|
@@ -66,6 +66,8 @@ function normalizeTasks(rawTasks) {
|
|
|
66
66
|
artifacts: Array.isArray(t && t.artifacts)
|
|
67
67
|
? t.artifacts.slice()
|
|
68
68
|
: undefined,
|
|
69
|
+
// Preserve tokenUsage if present
|
|
70
|
+
...(t && t.tokenUsage ? { tokenUsage: t.tokenUsage } : {}),
|
|
69
71
|
};
|
|
70
72
|
tasks[name] = taskObj;
|
|
71
73
|
});
|
|
@@ -100,6 +102,8 @@ function normalizeTasks(rawTasks) {
|
|
|
100
102
|
artifacts: Array.isArray(t && t.artifacts)
|
|
101
103
|
? t.artifacts.slice()
|
|
102
104
|
: undefined,
|
|
105
|
+
// Preserve tokenUsage if present
|
|
106
|
+
...(t && t.tokenUsage ? { tokenUsage: t.tokenUsage } : {}),
|
|
103
107
|
};
|
|
104
108
|
});
|
|
105
109
|
return { tasks, warnings };
|
|
@@ -158,7 +162,7 @@ export function adaptJobSummary(apiJob) {
|
|
|
158
162
|
// Demo-only: read canonical fields strictly
|
|
159
163
|
const id = apiJob.jobId;
|
|
160
164
|
const name = apiJob.title || "";
|
|
161
|
-
const rawTasks = apiJob.
|
|
165
|
+
const rawTasks = apiJob.tasks;
|
|
162
166
|
const location = apiJob.location;
|
|
163
167
|
|
|
164
168
|
// Job-level stage metadata
|
|
@@ -197,6 +201,21 @@ export function adaptJobSummary(apiJob) {
|
|
|
197
201
|
if (pipeline != null) job.pipeline = pipeline;
|
|
198
202
|
if (pipelineLabel != null) job.pipelineLabel = pipelineLabel;
|
|
199
203
|
|
|
204
|
+
// Costs summary from API
|
|
205
|
+
if (apiJob.costsSummary) {
|
|
206
|
+
job.costsSummary = {
|
|
207
|
+
totalTokens: apiJob.costsSummary.totalTokens || 0,
|
|
208
|
+
totalInputTokens: apiJob.costsSummary.totalInputTokens || 0,
|
|
209
|
+
totalOutputTokens: apiJob.costsSummary.totalOutputTokens || 0,
|
|
210
|
+
totalCost: apiJob.costsSummary.totalCost || 0,
|
|
211
|
+
totalInputCost: apiJob.costsSummary.totalInputCost || 0,
|
|
212
|
+
totalOutputCost: apiJob.costsSummary.totalOutputCost || 0,
|
|
213
|
+
};
|
|
214
|
+
// Add top-level numeric mirrors for convenience
|
|
215
|
+
job.totalCost = job.costsSummary.totalCost;
|
|
216
|
+
job.totalTokens = job.costsSummary.totalTokens;
|
|
217
|
+
}
|
|
218
|
+
|
|
200
219
|
// Include warnings for debugging
|
|
201
220
|
if (warnings.length > 0) job.__warnings = warnings;
|
|
202
221
|
|
|
@@ -212,7 +231,7 @@ export function adaptJobDetail(apiDetail) {
|
|
|
212
231
|
// Demo-only: read canonical fields strictly
|
|
213
232
|
const id = apiDetail.jobId;
|
|
214
233
|
const name = apiDetail.title || "";
|
|
215
|
-
const rawTasks = apiDetail.
|
|
234
|
+
const rawTasks = apiDetail.tasks;
|
|
216
235
|
const location = apiDetail.location;
|
|
217
236
|
|
|
218
237
|
// Job-level stage metadata
|
|
@@ -251,6 +270,11 @@ export function adaptJobDetail(apiDetail) {
|
|
|
251
270
|
if (pipeline != null) detail.pipeline = pipeline;
|
|
252
271
|
if (pipelineLabel != null) detail.pipelineLabel = pipelineLabel;
|
|
253
272
|
|
|
273
|
+
// Preserve job detail costs
|
|
274
|
+
if (apiDetail.costs) {
|
|
275
|
+
detail.costs = apiDetail.costs;
|
|
276
|
+
}
|
|
277
|
+
|
|
254
278
|
// Include warnings for debugging
|
|
255
279
|
if (warnings.length > 0) detail.__warnings = warnings;
|
|
256
280
|
|
|
@@ -242,7 +242,6 @@ export function useJobDetailWithUpdates(jobId) {
|
|
|
242
242
|
status: jobData?.status,
|
|
243
243
|
hasTasks: !!jobData?.tasks,
|
|
244
244
|
taskKeys: jobData?.tasks ? Object.keys(jobData.tasks) : [],
|
|
245
|
-
hasTasksStatus: !!jobData?.tasksStatus,
|
|
246
245
|
});
|
|
247
246
|
if (mountedRef.current) {
|
|
248
247
|
logger.log("Refetch successful, updating data");
|
package/src/ui/client/index.css
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
@import "@radix-ui/themes/styles.css" layer(radix);
|
|
3
3
|
@import "tailwindcss";
|
|
4
4
|
|
|
5
|
+
.radix-themes {
|
|
6
|
+
--heading-font-family: "Source Sans 3", sans-serif;
|
|
7
|
+
--body-font-family: "Source Sans 3", sans-serif;
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
/* Reset and base styles */
|
|
6
11
|
@layer base {
|
|
7
12
|
* {
|
|
@@ -22,6 +27,7 @@ body {
|
|
|
22
27
|
font-family: "Inter", sans-serif;
|
|
23
28
|
line-height: 1.6;
|
|
24
29
|
min-height: 100vh;
|
|
30
|
+
background-color: #dff2fe;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
.container {
|
package/src/ui/client/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
7
7
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
8
8
|
<link
|
|
9
|
-
href="https://fonts.googleapis.com/css2?family=
|
|
9
|
+
href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap"
|
|
10
10
|
rel="stylesheet"
|
|
11
11
|
/>
|
|
12
12
|
<title>Prompt Pipeline Dashboard</title>
|
package/src/ui/client/main.jsx
CHANGED
|
@@ -16,6 +16,7 @@ import ReactDOM from "react-dom/client";
|
|
|
16
16
|
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
|
17
17
|
import PromptPipelineDashboard from "@/pages/PromptPipelineDashboard.jsx";
|
|
18
18
|
import PipelineDetail from "@/pages/PipelineDetail.jsx";
|
|
19
|
+
import Code from "@/pages/Code.jsx";
|
|
19
20
|
import { Theme } from "@radix-ui/themes";
|
|
20
21
|
|
|
21
22
|
ReactDOM.createRoot(document.getElementById("root")).render(
|
|
@@ -31,6 +32,7 @@ ReactDOM.createRoot(document.getElementById("root")).render(
|
|
|
31
32
|
<Routes>
|
|
32
33
|
<Route path="/" element={<PromptPipelineDashboard />} />
|
|
33
34
|
<Route path="/pipeline/:jobId" element={<PipelineDetail />} />
|
|
35
|
+
<Route path="/code" element={<Code />} />
|
|
34
36
|
</Routes>
|
|
35
37
|
</BrowserRouter>
|
|
36
38
|
</Theme>
|