@ryanfw/prompt-orchestration-pipeline 0.11.0 → 0.13.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 (83) hide show
  1. package/package.json +11 -1
  2. package/src/cli/analyze-task.js +51 -0
  3. package/src/cli/index.js +8 -0
  4. package/src/components/AddPipelineSidebar.jsx +144 -0
  5. package/src/components/AnalysisProgressTray.jsx +87 -0
  6. package/src/components/DAGGrid.jsx +157 -47
  7. package/src/components/JobTable.jsx +4 -3
  8. package/src/components/Layout.jsx +142 -139
  9. package/src/components/MarkdownRenderer.jsx +149 -0
  10. package/src/components/PipelineDAGGrid.jsx +404 -0
  11. package/src/components/PipelineTypeTaskSidebar.jsx +96 -0
  12. package/src/components/SchemaPreviewPanel.jsx +97 -0
  13. package/src/components/StageTimeline.jsx +36 -0
  14. package/src/components/TaskAnalysisDisplay.jsx +227 -0
  15. package/src/components/TaskCreationSidebar.jsx +447 -0
  16. package/src/components/TaskDetailSidebar.jsx +119 -117
  17. package/src/components/TaskFilePane.jsx +94 -39
  18. package/src/components/ui/RestartJobModal.jsx +26 -6
  19. package/src/components/ui/StopJobModal.jsx +183 -0
  20. package/src/components/ui/button.jsx +59 -27
  21. package/src/components/ui/sidebar.jsx +118 -0
  22. package/src/config/models.js +99 -67
  23. package/src/core/config.js +11 -4
  24. package/src/core/lifecycle-policy.js +62 -0
  25. package/src/core/pipeline-runner.js +312 -217
  26. package/src/core/status-writer.js +84 -0
  27. package/src/llm/index.js +129 -9
  28. package/src/pages/Code.jsx +8 -1
  29. package/src/pages/PipelineDetail.jsx +84 -2
  30. package/src/pages/PipelineList.jsx +214 -0
  31. package/src/pages/PipelineTypeDetail.jsx +234 -0
  32. package/src/pages/PromptPipelineDashboard.jsx +10 -11
  33. package/src/providers/deepseek.js +76 -16
  34. package/src/providers/openai.js +61 -34
  35. package/src/task-analysis/enrichers/analysis-writer.js +62 -0
  36. package/src/task-analysis/enrichers/schema-deducer.js +145 -0
  37. package/src/task-analysis/enrichers/schema-writer.js +74 -0
  38. package/src/task-analysis/extractors/artifacts.js +137 -0
  39. package/src/task-analysis/extractors/llm-calls.js +176 -0
  40. package/src/task-analysis/extractors/stages.js +51 -0
  41. package/src/task-analysis/index.js +103 -0
  42. package/src/task-analysis/parser.js +28 -0
  43. package/src/task-analysis/utils/ast.js +43 -0
  44. package/src/ui/client/adapters/job-adapter.js +60 -0
  45. package/src/ui/client/api.js +233 -8
  46. package/src/ui/client/hooks/useAnalysisProgress.js +145 -0
  47. package/src/ui/client/hooks/useJobList.js +14 -1
  48. package/src/ui/client/index.css +64 -0
  49. package/src/ui/client/main.jsx +4 -0
  50. package/src/ui/client/sse-fetch.js +120 -0
  51. package/src/ui/dist/app.js +262 -0
  52. package/src/ui/dist/assets/index-cjHV9mYW.js +82578 -0
  53. package/src/ui/dist/assets/index-cjHV9mYW.js.map +1 -0
  54. package/src/ui/dist/assets/style-CoM9SoQF.css +180 -0
  55. package/src/ui/dist/favicon.svg +12 -0
  56. package/src/ui/dist/index.html +2 -2
  57. package/src/ui/endpoints/create-pipeline-endpoint.js +194 -0
  58. package/src/ui/endpoints/file-endpoints.js +330 -0
  59. package/src/ui/endpoints/job-control-endpoints.js +1001 -0
  60. package/src/ui/endpoints/job-endpoints.js +62 -0
  61. package/src/ui/endpoints/pipeline-analysis-endpoint.js +246 -0
  62. package/src/ui/endpoints/pipeline-type-detail-endpoint.js +181 -0
  63. package/src/ui/endpoints/pipelines-endpoint.js +133 -0
  64. package/src/ui/endpoints/schema-file-endpoint.js +105 -0
  65. package/src/ui/endpoints/sse-endpoints.js +223 -0
  66. package/src/ui/endpoints/state-endpoint.js +85 -0
  67. package/src/ui/endpoints/task-analysis-endpoint.js +104 -0
  68. package/src/ui/endpoints/task-creation-endpoint.js +114 -0
  69. package/src/ui/endpoints/task-save-endpoint.js +101 -0
  70. package/src/ui/endpoints/upload-endpoints.js +406 -0
  71. package/src/ui/express-app.js +227 -0
  72. package/src/ui/lib/analysis-lock.js +67 -0
  73. package/src/ui/lib/sse.js +30 -0
  74. package/src/ui/server.js +42 -1880
  75. package/src/ui/sse-broadcast.js +93 -0
  76. package/src/ui/utils/http-utils.js +139 -0
  77. package/src/ui/utils/mime-types.js +196 -0
  78. package/src/ui/utils/slug.js +31 -0
  79. package/src/ui/vite.config.js +22 -0
  80. package/src/ui/watcher.js +28 -2
  81. package/src/utils/jobs.js +39 -0
  82. package/src/ui/dist/assets/index-DeDzq-Kk.js +0 -23863
  83. package/src/ui/dist/assets/style-aBtD_Yrs.css +0 -62
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
- import { Box, Flex, Table, Text, Button } from "@radix-ui/themes";
2
+ import { Box, Flex, Table, Text } from "@radix-ui/themes";
3
3
  import { Progress } from "./ui/progress";
4
+ import { Button } from "./ui/button.jsx";
4
5
  import { TimerReset, ChevronRight } from "lucide-react";
5
6
  import { fmtDuration, jobCumulativeDurationMs } from "../utils/duration.js";
6
7
  import { countCompleted } from "../utils/jobs";
@@ -234,8 +235,8 @@ export default function JobTable({ jobs, pipeline, onOpenJob }) {
234
235
  <Table.Cell>
235
236
  <Button
236
237
  variant="ghost"
237
- size="1"
238
- className="opacity-0 group-hover:opacity-100 transition-opacity text-slate-500 hover:text-slate-700"
238
+ size="sm"
239
+ className="opacity-0 group-hover:opacity-100 transition-opacity"
239
240
  aria-label={`View details for ${jobTitle}`}
240
241
  >
241
242
  <ChevronRight className="h-4 w-4" />
@@ -1,12 +1,11 @@
1
1
  import React, { useState, useRef, useEffect } from "react";
2
2
  import { useNavigate, useLocation, Link } from "react-router-dom";
3
- import * as Tooltip from "@radix-ui/react-tooltip";
4
3
  import { Box, Flex, Text, Heading, Link as RadixLink } from "@radix-ui/themes";
5
4
  import { Button } from "./ui/button.jsx";
6
5
  import Logo from "./ui/Logo.jsx";
7
6
  import PageSubheader from "./PageSubheader.jsx";
8
7
  import UploadSeed from "./UploadSeed.jsx";
9
- import { ArrowLeft, Code2, Upload } from "lucide-react";
8
+ import { Code2, Upload, List } from "lucide-react";
10
9
  import "./ui/focus-styles.css";
11
10
 
12
11
  /**
@@ -85,153 +84,157 @@ export default function Layout({
85
84
  }, [isUploadOpen]);
86
85
 
87
86
  return (
88
- <Tooltip.Provider delayDuration={200}>
89
- <Box className="min-h-screen bg-gray-1">
90
- {/* Skip to main content link for accessibility */}
91
- <Box
92
- as="a"
93
- href="#main-content"
94
- className="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 z-50 bg-blue-600 text-white px-3 py-2 rounded-md text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
87
+ <Box className="min-h-screen bg-gray-1">
88
+ {/* Skip to main content link for accessibility */}
89
+ <Box
90
+ as="a"
91
+ href="#main-content"
92
+ className="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 z-50 bg-primary text-primary-foreground px-3 py-2 rounded-md text-sm font-medium focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 transition-colors"
93
+ >
94
+ Skip to main content
95
+ </Box>
96
+
97
+ {/* Header */}
98
+ <Box
99
+ role="banner"
100
+ className="sticky top-0 z-20 border-b border-gray-300 bg-gray-1/80 backdrop-blur supports-[backdrop-filter]:bg-gray-1/60"
101
+ >
102
+ <Flex
103
+ align="center"
104
+ justify="between"
105
+ className={`mx-auto w-full ${maxWidth} px-4 sm:px-6 lg:px-8 py-4`}
106
+ gap="4"
95
107
  >
96
- Skip to main content
97
- </Box>
108
+ {/* Left side: Navigation and title */}
109
+ <Flex align="center" className="min-w-0 flex-1">
110
+ {/* Logo */}
111
+ <Box
112
+ asChild
113
+ className="shrink-0"
114
+ style={{ width: "80px", height: "60px" }}
115
+ >
116
+ <Link
117
+ to="/"
118
+ aria-label="Go to homepage"
119
+ className="rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
120
+ >
121
+ <Logo />
122
+ </Link>
123
+ </Box>
124
+
125
+ {/* App title - clickable to navigate to dashboard */}
126
+ <Box
127
+ asChild
128
+ className="shrink-0 cursor-pointer hover:bg-gray-3 rounded p-1 -m-1 transition-colors"
129
+ onClick={() => navigate("/")}
130
+ >
131
+ <Heading
132
+ size="6"
133
+ weight="medium"
134
+ className="text-gray-12 truncate"
135
+ >
136
+ <>
137
+ Prompt
138
+ <br />
139
+ Pipeline
140
+ </>
141
+ </Heading>
142
+ </Box>
143
+ </Flex>
144
+
145
+ {/* Center: Navigation */}
146
+ <nav
147
+ role="navigation"
148
+ aria-label="Main navigation"
149
+ className="hidden md:flex"
150
+ >
151
+ <Flex align="center" gap="6">
152
+ <RadixLink
153
+ href="/pipelines"
154
+ className={`text-sm font-medium transition-colors hover:text-blue-600 ${
155
+ isActivePath("/pipelines")
156
+ ? "text-blue-600"
157
+ : "text-gray-11 hover:text-gray-12"
158
+ }`}
159
+ aria-current={isActivePath("/pipelines") ? "page" : undefined}
160
+ >
161
+ <Flex align="center" gap="2">
162
+ <List className="h-4 w-4" />
163
+ Pipelines
164
+ </Flex>
165
+ </RadixLink>
166
+ <RadixLink
167
+ href="/code"
168
+ className={`text-sm font-medium transition-colors hover:text-blue-600 ${
169
+ isActivePath("/code")
170
+ ? "text-blue-600"
171
+ : "text-gray-11 hover:text-gray-12"
172
+ }`}
173
+ aria-current={isActivePath("/code") ? "page" : undefined}
174
+ >
175
+ <Flex align="center" gap="2">
176
+ <Code2 className="h-4 w-4" />
177
+ Help
178
+ </Flex>
179
+ </RadixLink>
180
+ </Flex>
181
+ </nav>
182
+
183
+ {/* Right side: Actions */}
184
+ <Flex align="center" gap="3" className="shrink-0">
185
+ {actions}
186
+ <Button
187
+ size="md"
188
+ variant="solid"
189
+ onClick={toggleUploadPanel}
190
+ aria-controls="layout-upload-panel"
191
+ aria-expanded={isUploadOpen}
192
+ >
193
+ <Upload className="h-4 w-4" />
194
+ <Text size="2" className="ml-2">
195
+ Upload Seed
196
+ </Text>
197
+ </Button>
198
+ </Flex>
199
+ </Flex>
200
+ </Box>
98
201
 
99
- {/* Header */}
202
+ {/* Upload Panel */}
203
+ {isUploadOpen && (
100
204
  <Box
101
- role="banner"
102
- className="sticky top-0 z-20 border-b border-gray-300 bg-gray-1/80 backdrop-blur supports-[backdrop-filter]:bg-gray-1/60"
205
+ id="layout-upload-panel"
206
+ ref={uploadPanelRef}
207
+ role="region"
208
+ aria-label="Upload seed file"
209
+ className="bg-blue-50"
103
210
  >
104
211
  <Flex
105
- align="center"
106
- justify="between"
212
+ direction="column"
213
+ gap="3"
107
214
  className={`mx-auto w-full ${maxWidth} px-4 sm:px-6 lg:px-8 py-4`}
108
- gap="4"
109
215
  >
110
- {/* Left side: Navigation and title */}
111
- <Flex align="center" className="min-w-0 flex-1">
112
- {/* Logo */}
113
- <Box
114
- asChild
115
- className="shrink-0"
116
- style={{ width: "80px", height: "60px" }}
117
- >
118
- <Link
119
- to="/"
120
- aria-label="Go to homepage"
121
- className="rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
122
- >
123
- <Logo />
124
- </Link>
125
- </Box>
126
-
127
- {/* App title - clickable to navigate to dashboard */}
128
- <Box
129
- asChild
130
- className="shrink-0 cursor-pointer hover:bg-gray-3 rounded p-1 -m-1 transition-colors"
131
- onClick={() => navigate("/")}
132
- >
133
- <Heading
134
- size="6"
135
- weight="medium"
136
- className="text-gray-12 truncate"
137
- >
138
- <>
139
- Prompt
140
- <br />
141
- Pipeline
142
- </>
143
- </Heading>
216
+ {/* Success Message */}
217
+ {seedUploadSuccess && (
218
+ <Box className="rounded-md bg-green-50 p-3 border border-green-200">
219
+ <Text size="2" className="text-green-800">
220
+ Job <strong>{seedUploadSuccess}</strong> created successfully
221
+ </Text>
144
222
  </Box>
145
- </Flex>
223
+ )}
146
224
 
147
- {/* Center: Navigation */}
148
- <nav
149
- role="navigation"
150
- aria-label="Main navigation"
151
- className="hidden md:flex"
152
- >
153
- <Flex align="center" gap="6">
154
- <RadixLink
155
- href="/code"
156
- className={`text-sm font-medium transition-colors hover:text-blue-600 ${
157
- isActivePath("/code")
158
- ? "text-blue-600"
159
- : "text-gray-11 hover:text-gray-12"
160
- }`}
161
- aria-current={isActivePath("/code") ? "page" : undefined}
162
- >
163
- <Flex align="center" gap="2">
164
- <Code2 className="h-4 w-4" />
165
- Help
166
- </Flex>
167
- </RadixLink>
168
- </Flex>
169
- </nav>
170
-
171
- {/* Right side: Actions */}
172
- <Flex align="center" gap="3" className="shrink-0">
173
- {actions}
174
- <Tooltip.Root delayDuration={200}>
175
- <Tooltip.Trigger asChild>
176
- <Button
177
- size="sm"
178
- variant="default"
179
- onClick={toggleUploadPanel}
180
- aria-controls="layout-upload-panel"
181
- aria-expanded={isUploadOpen}
182
- >
183
- <Upload className="h-4 w-4" />
184
- <Text size="2" className="ml-2">
185
- Upload Seed
186
- </Text>
187
- </Button>
188
- </Tooltip.Trigger>
189
- <Tooltip.Content side="bottom" sideOffset={5}>
190
- <Text size="2">Upload seed file</Text>
191
- </Tooltip.Content>
192
- </Tooltip.Root>
193
- </Flex>
225
+ <UploadSeed onUploadSuccess={handleSeedUploadSuccess} />
194
226
  </Flex>
195
227
  </Box>
196
-
197
- {/* Upload Panel */}
198
- {isUploadOpen && (
199
- <Box
200
- id="layout-upload-panel"
201
- ref={uploadPanelRef}
202
- role="region"
203
- aria-label="Upload seed file"
204
- className="bg-blue-50"
205
- >
206
- <Flex
207
- direction="column"
208
- gap="3"
209
- className={`mx-auto w-full ${maxWidth} px-4 sm:px-6 lg:px-8 py-4`}
210
- >
211
- {/* Success Message */}
212
- {seedUploadSuccess && (
213
- <Box className="rounded-md bg-green-50 p-3 border border-green-200">
214
- <Text size="2" className="text-green-800">
215
- Job <strong>{seedUploadSuccess}</strong> created
216
- successfully
217
- </Text>
218
- </Box>
219
- )}
220
-
221
- <UploadSeed onUploadSuccess={handleSeedUploadSuccess} />
222
- </Flex>
223
- </Box>
224
- )}
225
-
226
- {/* Main content */}
227
- <main
228
- id="main-content"
229
- role="main"
230
- className={`mx-auto w-full ${maxWidth} px-4 sm:px-6 lg:px-8`}
231
- >
232
- {children}
233
- </main>
234
- </Box>
235
- </Tooltip.Provider>
228
+ )}
229
+
230
+ {/* Main content */}
231
+ <main
232
+ id="main-content"
233
+ role="main"
234
+ className={`mx-auto w-full ${maxWidth} px-4 sm:px-6 lg:px-8`}
235
+ >
236
+ {children}
237
+ </main>
238
+ </Box>
236
239
  );
237
240
  }
@@ -0,0 +1,149 @@
1
+ import React, { useState } from "react";
2
+ import ReactMarkdown from "react-markdown";
3
+ import remarkGfm from "remark-gfm";
4
+ import rehypeHighlight from "rehype-highlight";
5
+ import "highlight.js/styles/github-dark.css";
6
+
7
+ /**
8
+ * MarkdownRenderer component for rendering markdown content with syntax highlighting
9
+ * @param {Object} props - Component props
10
+ * @param {string} props.content - Markdown content to render
11
+ * @param {string} props.className - Additional CSS classes
12
+ */
13
+ export function MarkdownRenderer({ content, className = "" }) {
14
+ const [copiedCode, setCopiedCode] = useState(null);
15
+
16
+ // Handle code copy
17
+ const handleCopyCode = async (code) => {
18
+ try {
19
+ await navigator.clipboard.writeText(code);
20
+ setCopiedCode(code);
21
+ setTimeout(() => setCopiedCode(null), 2000);
22
+ } catch (err) {
23
+ console.error("Failed to copy code:", err);
24
+ }
25
+ };
26
+
27
+ // Custom code block component with copy button
28
+ const CodeBlock = ({ children, className: codeClassName }) => {
29
+ const language = codeClassName?.replace("language-", "") || "text";
30
+ const code = React.Children.toArray(children).join("");
31
+ const isCopied = copiedCode === code;
32
+
33
+ return (
34
+ <div className="relative group">
35
+ <pre className="!bg-muted !text-foreground rounded-lg p-4 overflow-x-auto mt-3 mb-3">
36
+ <code className={codeClassName}>{children}</code>
37
+ </pre>
38
+ <button
39
+ onClick={() => handleCopyCode(code)}
40
+ className="absolute top-2 right-2 bg-muted-foreground/80 text-background text-xs px-2 py-1 rounded hover:bg-muted-foreground transition-opacity opacity-0 group-hover:opacity-100"
41
+ aria-label="Copy code to clipboard"
42
+ >
43
+ {isCopied ? "Copied!" : "Copy"}
44
+ </button>
45
+ {language !== "text" && (
46
+ <span className="absolute top-2 left-2 text-xs text-muted-foreground">
47
+ {language}
48
+ </span>
49
+ )}
50
+ </div>
51
+ );
52
+ };
53
+
54
+ return (
55
+ <div
56
+ className={`!max-w-none prose prose-sm dark:prose-invert ${className}`}
57
+ >
58
+ <ReactMarkdown
59
+ remarkPlugins={[remarkGfm]}
60
+ rehypePlugins={[rehypeHighlight]}
61
+ components={{
62
+ code: CodeBlock,
63
+ h1: ({ children }) => (
64
+ <h1 className="text-xl font-bold mb-3 text-foreground">
65
+ {children}
66
+ </h1>
67
+ ),
68
+ h2: ({ children }) => (
69
+ <h2 className="text-lg font-semibold mb-2 text-foreground">
70
+ {children}
71
+ </h2>
72
+ ),
73
+ h3: ({ children }) => (
74
+ <h3 className="text-base font-medium mb-2 text-foreground">
75
+ {children}
76
+ </h3>
77
+ ),
78
+ p: ({ children }) => (
79
+ <p className="mb-3 text-foreground leading-relaxed">{children}</p>
80
+ ),
81
+ ul: ({ children }) => (
82
+ <ul className="list-disc pl-5 mb-3 space-y-1 text-foreground">
83
+ {children}
84
+ </ul>
85
+ ),
86
+ ol: ({ children }) => (
87
+ <ol className="list-decimal pl-5 mb-3 space-y-1 text-foreground">
88
+ {children}
89
+ </ol>
90
+ ),
91
+ li: ({ children }) => <li className="ml-2">{children}</li>,
92
+ a: ({ children, href }) => (
93
+ <a
94
+ href={href}
95
+ className="text-primary hover:text-primary/80 underline"
96
+ target="_blank"
97
+ rel="noopener noreferrer"
98
+ >
99
+ {children}
100
+ </a>
101
+ ),
102
+ blockquote: ({ children }) => (
103
+ <blockquote className="border-l-4 border-primary/50 pl-4 py-2 my-3 bg-muted/30 italic text-foreground/80">
104
+ {children}
105
+ </blockquote>
106
+ ),
107
+ table: ({ children }) => (
108
+ <div className="overflow-x-auto my-4">
109
+ <table className="min-w-full border-collapse border border-border text-foreground">
110
+ {children}
111
+ </table>
112
+ </div>
113
+ ),
114
+ thead: ({ children }) => (
115
+ <thead className="bg-muted/50">{children}</thead>
116
+ ),
117
+ tbody: ({ children }) => <tbody>{children}</tbody>,
118
+ tr: ({ children }) => (
119
+ <tr className="border-b border-border hover:bg-muted/20">
120
+ {children}
121
+ </tr>
122
+ ),
123
+ th: ({ children }) => (
124
+ <th className="border border-border px-4 py-2 text-left font-semibold">
125
+ {children}
126
+ </th>
127
+ ),
128
+ td: ({ children }) => (
129
+ <td className="border border-border px-4 py-2">{children}</td>
130
+ ),
131
+ hr: () => <hr className="my-4 border-border" />,
132
+ strong: ({ children }) => (
133
+ <strong className="font-bold text-foreground">{children}</strong>
134
+ ),
135
+ em: ({ children }) => (
136
+ <em className="italic text-foreground">{children}</em>
137
+ ),
138
+ del: ({ children }) => (
139
+ <del className="line-through text-muted-foreground">{children}</del>
140
+ ),
141
+ }}
142
+ >
143
+ {content}
144
+ </ReactMarkdown>
145
+ </div>
146
+ );
147
+ }
148
+
149
+ export default MarkdownRenderer;