@ryanfw/prompt-orchestration-pipeline 0.15.0 → 0.16.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 +4 -1
- package/src/components/Layout.jsx +4 -0
- package/src/components/TaskCreationSidebar.jsx +198 -47
- package/src/components/ui/CopyableCode.jsx +110 -0
- package/src/core/batch-runner.js +277 -0
- package/src/core/file-io.js +37 -1
- package/src/core/orchestrator.js +0 -18
- package/src/core/pipeline-runner.js +0 -37
- package/src/core/status-writer.js +0 -18
- package/src/core/symlink-utils.js +0 -12
- package/src/core/task-runner.js +0 -23
- package/src/pages/Code.jsx +538 -272
- package/src/pages/PromptPipelineDashboard.jsx +28 -13
- package/src/task-analysis/enrichers/analysis-writer.js +32 -0
- package/src/task-analysis/enrichers/artifact-resolver.js +98 -0
- package/src/task-analysis/extractors/artifacts.js +70 -26
- package/src/task-analysis/index.js +4 -2
- package/src/ui/dist/assets/{index-B5HMRkR9.js → index-DI_nRqVI.js} +3271 -397
- package/src/ui/dist/assets/index-DI_nRqVI.js.map +1 -0
- package/src/ui/dist/assets/style-CVd3RRU2.css +180 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/endpoints/pipeline-analysis-endpoint.js +59 -0
- package/src/ui/endpoints/pipeline-artifacts-endpoint.js +109 -0
- package/src/ui/express-app.js +4 -0
- package/src/ui/watcher.js +20 -10
- package/src/ui/dist/assets/index-B5HMRkR9.js.map +0 -1
- package/src/ui/dist/assets/style-CoM9SoQF.css +0 -180
package/src/pages/Code.jsx
CHANGED
|
@@ -1,358 +1,624 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Box, Heading,
|
|
2
|
+
import { Box, Flex, Heading, Text, Code, Table } from "@radix-ui/themes";
|
|
3
3
|
import Layout from "../components/Layout.jsx";
|
|
4
4
|
import PageSubheader from "../components/PageSubheader.jsx";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardHeader,
|
|
8
|
+
CardTitle,
|
|
9
|
+
CardContent,
|
|
10
|
+
} from "../components/ui/card.jsx";
|
|
11
|
+
import { CopyableCodeBlock } from "../components/ui/CopyableCode.jsx";
|
|
12
|
+
import {
|
|
13
|
+
ChevronDown,
|
|
14
|
+
ChevronRight,
|
|
15
|
+
FileText,
|
|
16
|
+
Database,
|
|
17
|
+
Cpu,
|
|
18
|
+
Shield,
|
|
19
|
+
Key,
|
|
20
|
+
Folder,
|
|
21
|
+
} from "lucide-react";
|
|
6
22
|
|
|
7
|
-
|
|
23
|
+
// Section navigation items
|
|
24
|
+
const sections = [
|
|
25
|
+
{ id: "environment", label: "Environment", icon: Key },
|
|
26
|
+
{ id: "getting-started", label: "Getting Started", icon: FileText },
|
|
27
|
+
{ id: "io-api", label: "IO API", icon: Database },
|
|
28
|
+
{ id: "llm-api", label: "LLM API", icon: Cpu },
|
|
29
|
+
{ id: "validation", label: "Validation", icon: Shield },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// IO Functions organized by category
|
|
33
|
+
const writeFunctions = [
|
|
8
34
|
{
|
|
9
35
|
name: "writeArtifact",
|
|
10
|
-
description: "
|
|
11
|
-
params:
|
|
12
|
-
'name: string, content: string, options?: { mode?: "replace"|"append"=replace }',
|
|
36
|
+
description: "Persist output files for downstream tasks",
|
|
37
|
+
params: 'name, content, { mode?: "replace"|"append" }',
|
|
13
38
|
returns: "Promise<string>",
|
|
14
|
-
|
|
39
|
+
path: "{workDir}/files/artifacts",
|
|
15
40
|
},
|
|
16
41
|
{
|
|
17
42
|
name: "writeLog",
|
|
18
|
-
description: "
|
|
19
|
-
params:
|
|
20
|
-
'name: string, content: string, options?: { mode?: "append"|"replace"=append }',
|
|
43
|
+
description: "Append debug or progress logs",
|
|
44
|
+
params: 'name, content, { mode?: "append"|"replace" }',
|
|
21
45
|
returns: "Promise<string>",
|
|
22
|
-
|
|
23
|
-
"Writes to {workDir}/files/logs; default append; updates tasks-status.json",
|
|
46
|
+
path: "{workDir}/files/logs",
|
|
24
47
|
},
|
|
25
48
|
{
|
|
26
49
|
name: "writeTmp",
|
|
27
|
-
description: "
|
|
28
|
-
params:
|
|
29
|
-
'name: string, content: string, options?: { mode?: "replace"|"append"=replace }',
|
|
50
|
+
description: "Store intermediate scratch data",
|
|
51
|
+
params: 'name, content, { mode?: "replace"|"append" }',
|
|
30
52
|
returns: "Promise<string>",
|
|
31
|
-
|
|
53
|
+
path: "{workDir}/files/tmp",
|
|
32
54
|
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const readFunctions = [
|
|
33
58
|
{
|
|
34
59
|
name: "readArtifact",
|
|
35
|
-
description: "
|
|
60
|
+
description: "Load artifacts from previous tasks",
|
|
36
61
|
params: "name: string",
|
|
37
62
|
returns: "Promise<string>",
|
|
38
|
-
|
|
63
|
+
path: "{workDir}/files/artifacts",
|
|
39
64
|
},
|
|
40
65
|
{
|
|
41
66
|
name: "readLog",
|
|
42
|
-
description: "Read
|
|
67
|
+
description: "Read log file contents",
|
|
43
68
|
params: "name: string",
|
|
44
69
|
returns: "Promise<string>",
|
|
45
|
-
|
|
70
|
+
path: "{workDir}/files/logs",
|
|
46
71
|
},
|
|
47
72
|
{
|
|
48
73
|
name: "readTmp",
|
|
49
|
-
description: "Read
|
|
74
|
+
description: "Read temporary file contents",
|
|
50
75
|
params: "name: string",
|
|
51
76
|
returns: "Promise<string>",
|
|
52
|
-
|
|
77
|
+
path: "{workDir}/files/tmp",
|
|
53
78
|
},
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
const utilityFunctions = [
|
|
54
82
|
{
|
|
55
83
|
name: "getTaskDir",
|
|
56
|
-
description: "Get the task directory path",
|
|
57
|
-
params: "",
|
|
84
|
+
description: "Get the current task's directory path",
|
|
85
|
+
params: "—",
|
|
58
86
|
returns: "string",
|
|
59
87
|
notes: "Returns {workDir}/tasks/{taskName}",
|
|
60
88
|
},
|
|
61
89
|
{
|
|
62
90
|
name: "getCurrentStage",
|
|
63
91
|
description: "Get the current stage name",
|
|
64
|
-
params: "",
|
|
92
|
+
params: "—",
|
|
65
93
|
returns: "string",
|
|
66
94
|
notes: "Calls injected getStage()",
|
|
67
95
|
},
|
|
96
|
+
{
|
|
97
|
+
name: "getDB",
|
|
98
|
+
description: "Get SQLite database for this job run",
|
|
99
|
+
params: "options?: better-sqlite3 Options",
|
|
100
|
+
returns: "Database",
|
|
101
|
+
notes: "WAL mode enabled; caller must close",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "runBatch",
|
|
105
|
+
description: "Execute batch jobs concurrently with retry support",
|
|
106
|
+
params: "{ jobs, processor, concurrency?, maxRetries?, batchId? }",
|
|
107
|
+
returns: "Promise<{ completed, failed }>",
|
|
108
|
+
notes: "Auto-retries failures; state persisted in SQLite",
|
|
109
|
+
},
|
|
68
110
|
];
|
|
69
111
|
|
|
70
112
|
const sampleSeed = {
|
|
71
|
-
name: "
|
|
113
|
+
name: "my-blog-post",
|
|
72
114
|
pipeline: "content-generation",
|
|
73
115
|
data: {
|
|
74
|
-
type: "some-type",
|
|
75
116
|
contentType: "blog-post",
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
length: "1500-2000 words",
|
|
79
|
-
outputFormat: "blog-post",
|
|
117
|
+
topic: "AI in Healthcare",
|
|
118
|
+
targetAudience: "developers",
|
|
80
119
|
},
|
|
81
120
|
};
|
|
82
121
|
|
|
122
|
+
const envVars = [
|
|
123
|
+
{ name: "OPENAI_API_KEY", provider: "OpenAI" },
|
|
124
|
+
{ name: "ANTHROPIC_API_KEY", provider: "Anthropic" },
|
|
125
|
+
{ name: "GEMINI_API_KEY", provider: "Google Gemini" },
|
|
126
|
+
{ name: "DEEPSEEK_API_KEY", provider: "DeepSeek" },
|
|
127
|
+
{ name: "ZHIPU_API_KEY", provider: "Zhipu" },
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
// Collapsible Section Component
|
|
131
|
+
function CollapsibleSection({
|
|
132
|
+
id,
|
|
133
|
+
title,
|
|
134
|
+
icon: Icon,
|
|
135
|
+
defaultOpen = false,
|
|
136
|
+
children,
|
|
137
|
+
}) {
|
|
138
|
+
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<section id={id} className="scroll-mt-24">
|
|
142
|
+
<Card className="mb-6">
|
|
143
|
+
<button
|
|
144
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
145
|
+
className="w-full text-left"
|
|
146
|
+
aria-expanded={isOpen}
|
|
147
|
+
aria-controls={`${id}-content`}
|
|
148
|
+
>
|
|
149
|
+
<CardHeader
|
|
150
|
+
className={`flex flex-row items-center justify-between cursor-pointer hover:bg-gray-50 rounded-t-xl transition-colors ${!isOpen ? "rounded-b-xl border-b-0" : ""}`}
|
|
151
|
+
>
|
|
152
|
+
<Flex align="center" gap="3">
|
|
153
|
+
{Icon && <Icon className="h-5 w-5 text-gray-500" />}
|
|
154
|
+
<CardTitle className="text-lg">{title}</CardTitle>
|
|
155
|
+
</Flex>
|
|
156
|
+
{isOpen ? (
|
|
157
|
+
<ChevronDown className="h-5 w-5 text-gray-400" />
|
|
158
|
+
) : (
|
|
159
|
+
<ChevronRight className="h-5 w-5 text-gray-400" />
|
|
160
|
+
)}
|
|
161
|
+
</CardHeader>
|
|
162
|
+
</button>
|
|
163
|
+
{isOpen && (
|
|
164
|
+
<CardContent id={`${id}-content`} className="pt-4">
|
|
165
|
+
{children}
|
|
166
|
+
</CardContent>
|
|
167
|
+
)}
|
|
168
|
+
</Card>
|
|
169
|
+
</section>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Function row component for compact display
|
|
174
|
+
function FunctionRow({ name, description, params, returns, path, notes }) {
|
|
175
|
+
return (
|
|
176
|
+
<div className="py-3 border-b border-gray-100 last:border-0">
|
|
177
|
+
<Flex justify="between" align="start" gap="4" wrap="wrap">
|
|
178
|
+
<div className="flex-1 min-w-[200px]">
|
|
179
|
+
<Code size="2" className="text-blue-600 font-medium">
|
|
180
|
+
io.{name}
|
|
181
|
+
</Code>
|
|
182
|
+
<Text as="p" size="2" className="text-gray-600 mt-1">
|
|
183
|
+
{description}
|
|
184
|
+
</Text>
|
|
185
|
+
</div>
|
|
186
|
+
<div className="text-right text-sm space-y-1">
|
|
187
|
+
<div>
|
|
188
|
+
<span className="text-gray-400">params: </span>
|
|
189
|
+
<Code size="1">{params}</Code>
|
|
190
|
+
</div>
|
|
191
|
+
<div>
|
|
192
|
+
<span className="text-gray-400">returns: </span>
|
|
193
|
+
<Code size="1">{returns}</Code>
|
|
194
|
+
</div>
|
|
195
|
+
{path && (
|
|
196
|
+
<div className="text-gray-400 text-xs flex items-center justify-end gap-1">
|
|
197
|
+
<Folder className="h-3 w-3" />
|
|
198
|
+
{path}
|
|
199
|
+
</div>
|
|
200
|
+
)}
|
|
201
|
+
{notes && <div className="text-gray-400 text-xs">{notes}</div>}
|
|
202
|
+
</div>
|
|
203
|
+
</Flex>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
83
208
|
export default function CodePage() {
|
|
84
209
|
const [llmFunctions, setLlmFunctions] = useState(null);
|
|
210
|
+
const [activeSection, setActiveSection] = useState("environment");
|
|
85
211
|
|
|
86
212
|
useEffect(() => {
|
|
87
213
|
fetch("/api/llm/functions")
|
|
88
214
|
.then((res) => res.json())
|
|
89
215
|
.then(({ ok, data }) => {
|
|
90
216
|
if (!ok || typeof data !== "object" || data === null) {
|
|
91
|
-
throw new Error(
|
|
92
|
-
"Invalid /api/llm/functions response: expected { ok:true, data:Object }"
|
|
93
|
-
);
|
|
217
|
+
throw new Error("Invalid /api/llm/functions response");
|
|
94
218
|
}
|
|
95
219
|
setLlmFunctions(data);
|
|
96
220
|
})
|
|
97
221
|
.catch(console.error);
|
|
98
222
|
}, []);
|
|
99
223
|
|
|
100
|
-
|
|
224
|
+
// Track active section on scroll
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
const observer = new IntersectionObserver(
|
|
227
|
+
(entries) => {
|
|
228
|
+
entries.forEach((entry) => {
|
|
229
|
+
if (entry.isIntersecting) {
|
|
230
|
+
setActiveSection(entry.target.id);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
{ rootMargin: "-100px 0px -66% 0px" }
|
|
235
|
+
);
|
|
101
236
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
237
|
+
sections.forEach(({ id }) => {
|
|
238
|
+
const el = document.getElementById(id);
|
|
239
|
+
if (el) observer.observe(el);
|
|
240
|
+
});
|
|
105
241
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
<PageSubheader breadcrumbs={breadcrumbs} />
|
|
109
|
-
<Box>
|
|
110
|
-
{/* Seed File Example Section */}
|
|
111
|
-
<Box mb="8">
|
|
112
|
-
<Heading size="6" mb="4">
|
|
113
|
-
Seed File Example
|
|
114
|
-
</Heading>
|
|
115
|
-
<Text as="p" mb="3" size="2">
|
|
116
|
-
A seed file is a JSON object used to start a new pipeline job. It
|
|
117
|
-
defines the job name, the pipeline to run, and any contextual data
|
|
118
|
-
the pipeline requires to begin.
|
|
119
|
-
</Text>
|
|
120
|
-
<Text as="p" mb="3" size="2" weight="bold">
|
|
121
|
-
Required fields:
|
|
122
|
-
</Text>
|
|
123
|
-
<ul className="list-disc list-inside mb-4 space-y-1">
|
|
124
|
-
<li className="text-sm text-gray-700">
|
|
125
|
-
<Text as="span" weight="bold">
|
|
126
|
-
name
|
|
127
|
-
</Text>{" "}
|
|
128
|
-
(string): Human-friendly title; non-empty, printable only, ≤120
|
|
129
|
-
chars; must be unique.
|
|
130
|
-
</li>
|
|
131
|
-
<li className="text-sm text-gray-700">
|
|
132
|
-
<Text as="span" weight="bold">
|
|
133
|
-
pipeline
|
|
134
|
-
</Text>{" "}
|
|
135
|
-
(string): Pipeline slug defined in your registry (e.g.,
|
|
136
|
-
content-generation).
|
|
137
|
-
</li>
|
|
138
|
-
<li className="text-sm text-gray-700">
|
|
139
|
-
<Text as="span" weight="bold">
|
|
140
|
-
data
|
|
141
|
-
</Text>{" "}
|
|
142
|
-
(object): Required but flexible; include any arbitrary keys your
|
|
143
|
-
pipeline tasks expect.
|
|
144
|
-
</li>
|
|
145
|
-
</ul>
|
|
146
|
-
<Box mb="3">
|
|
147
|
-
<Button
|
|
148
|
-
size="1"
|
|
149
|
-
onClick={handleCopySeed}
|
|
150
|
-
data-testid="copy-seed-example"
|
|
151
|
-
>
|
|
152
|
-
Copy
|
|
153
|
-
</Button>
|
|
154
|
-
</Box>
|
|
155
|
-
<pre className="text-xs bg-gray-50 p-3 rounded overflow-auto max-h-60 border border-gray-200">
|
|
156
|
-
{JSON.stringify(sampleSeed, null, 2)}
|
|
157
|
-
</pre>
|
|
158
|
-
</Box>
|
|
242
|
+
return () => observer.disconnect();
|
|
243
|
+
}, []);
|
|
159
244
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
<Table.Root>
|
|
165
|
-
<Table.Header>
|
|
166
|
-
<Table.Row>
|
|
167
|
-
<Table.ColumnHeaderCell>Function</Table.ColumnHeaderCell>
|
|
168
|
-
<Table.ColumnHeaderCell>Parameters</Table.ColumnHeaderCell>
|
|
169
|
-
<Table.ColumnHeaderCell>Returns</Table.ColumnHeaderCell>
|
|
170
|
-
<Table.ColumnHeaderCell>Notes</Table.ColumnHeaderCell>
|
|
171
|
-
</Table.Row>
|
|
172
|
-
</Table.Header>
|
|
173
|
-
<Table.Body>
|
|
174
|
-
{ioFunctions.map((fn) => (
|
|
175
|
-
<Table.Row key={fn.name}>
|
|
176
|
-
<Table.RowHeaderCell>
|
|
177
|
-
<Code size="3">io.{fn.name}</Code>
|
|
178
|
-
</Table.RowHeaderCell>
|
|
179
|
-
<Table.Cell>
|
|
180
|
-
<Code size="3">{fn.params || "—"}</Code>
|
|
181
|
-
</Table.Cell>
|
|
182
|
-
<Table.Cell>
|
|
183
|
-
<Code size="3">{fn.returns}</Code>
|
|
184
|
-
</Table.Cell>
|
|
185
|
-
<Table.Cell>
|
|
186
|
-
{fn.description}
|
|
187
|
-
<br />
|
|
188
|
-
{fn.notes}
|
|
189
|
-
</Table.Cell>
|
|
190
|
-
</Table.Row>
|
|
191
|
-
))}
|
|
192
|
-
</Table.Body>
|
|
193
|
-
</Table.Root>
|
|
194
|
-
</Box>
|
|
245
|
+
const breadcrumbs = [
|
|
246
|
+
{ label: "Home", href: "/" },
|
|
247
|
+
{ label: "API Reference" },
|
|
248
|
+
];
|
|
195
249
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
topP?: number,
|
|
211
|
-
frequencyPenalty?: number,
|
|
212
|
-
presencePenalty?: number,
|
|
213
|
-
seed?: number,
|
|
214
|
-
provider?: string,
|
|
215
|
-
model?: string,
|
|
216
|
-
metadata?: object,
|
|
217
|
-
maxRetries?: number
|
|
218
|
-
}`}
|
|
219
|
-
</Code>
|
|
220
|
-
<Heading size="4" mb="2">
|
|
221
|
-
Returns
|
|
222
|
-
</Heading>
|
|
223
|
-
<Code size="3">{`Promise<{ content: any, usage?: object, raw?: any }>`}</Code>
|
|
224
|
-
</Box>
|
|
225
|
-
|
|
226
|
-
{llmFunctions && (
|
|
227
|
-
<Box overflowX="auto">
|
|
228
|
-
<Table.Root>
|
|
229
|
-
<Table.Header>
|
|
230
|
-
<Table.Row>
|
|
231
|
-
<Table.ColumnHeaderCell>Function</Table.ColumnHeaderCell>
|
|
232
|
-
<Table.ColumnHeaderCell>Model</Table.ColumnHeaderCell>
|
|
233
|
-
</Table.Row>
|
|
234
|
-
</Table.Header>
|
|
235
|
-
<Table.Body>
|
|
236
|
-
{Object.entries(llmFunctions).map(([provider, functions]) =>
|
|
237
|
-
functions.map((fn) => (
|
|
238
|
-
<Table.Row key={fn.fullPath}>
|
|
239
|
-
<Table.RowHeaderCell>
|
|
240
|
-
<Code size="3">{fn.fullPath}</Code>
|
|
241
|
-
</Table.RowHeaderCell>
|
|
242
|
-
<Table.Cell>
|
|
243
|
-
<Code size="3">{fn.model}</Code>
|
|
244
|
-
</Table.Cell>
|
|
245
|
-
</Table.Row>
|
|
246
|
-
))
|
|
247
|
-
)}
|
|
248
|
-
</Table.Body>
|
|
249
|
-
</Table.Root>
|
|
250
|
-
</Box>
|
|
251
|
-
)}
|
|
250
|
+
const llmArgsCode = `{
|
|
251
|
+
messages: Array<{ role: "system"|"user"|"assistant", content: string }>,
|
|
252
|
+
temperature?: number, // 0-2, default varies by model
|
|
253
|
+
maxTokens?: number, // Max response tokens
|
|
254
|
+
responseFormat?: "json" | { type: "json_object" } | { type: "json_schema", ... },
|
|
255
|
+
stop?: string | string[], // Stop sequences
|
|
256
|
+
topP?: number, // Nucleus sampling
|
|
257
|
+
frequencyPenalty?: number, // -2 to 2
|
|
258
|
+
presencePenalty?: number, // -2 to 2
|
|
259
|
+
seed?: number, // For reproducibility
|
|
260
|
+
provider?: string, // Override default provider
|
|
261
|
+
model?: string, // Override default model
|
|
262
|
+
maxRetries?: number // Auto-retry on failure
|
|
263
|
+
}`;
|
|
252
264
|
|
|
253
|
-
|
|
254
|
-
Validation API
|
|
255
|
-
</Heading>
|
|
256
|
-
<Text as="p" mb="3" size="2">
|
|
257
|
-
Schema validation helper available to task stages via validators.
|
|
258
|
-
</Text>
|
|
259
|
-
<Box mb="4">
|
|
260
|
-
<Heading size="4" mb="2">
|
|
261
|
-
Function Signature
|
|
262
|
-
</Heading>
|
|
263
|
-
<Code size="3">
|
|
264
|
-
{`validateWithSchema(schema: object, data: object | string): { valid: true } | { valid: false, errors: AjvError[] }`}
|
|
265
|
-
</Code>
|
|
266
|
-
<Heading size="4" mb="2" mt="4">
|
|
267
|
-
Behavior
|
|
268
|
-
</Heading>
|
|
269
|
-
<ul className="list-disc list-inside mb-4 space-y-1">
|
|
270
|
-
<li className="text-sm text-gray-700">
|
|
271
|
-
<Text as="span">
|
|
272
|
-
Parses string data to JSON; on parse failure returns{" "}
|
|
273
|
-
{`{ valid:false, errors:[{ keyword:"type", message:"must be a valid JSON object (string parsing failed)"} ]`}
|
|
274
|
-
</Text>
|
|
275
|
-
</li>
|
|
276
|
-
<li className="text-sm text-gray-700">
|
|
277
|
-
<Text as="span">
|
|
278
|
-
Uses Ajv({`{ allErrors: true, strict: false }`}); compiles
|
|
279
|
-
provided schema
|
|
280
|
-
</Text>
|
|
281
|
-
</li>
|
|
282
|
-
<li className="text-sm text-gray-700">
|
|
283
|
-
<Text as="span">Returns AJV errors array when invalid</Text>
|
|
284
|
-
</li>
|
|
285
|
-
</ul>
|
|
286
|
-
<Heading size="4" mb="2">
|
|
287
|
-
Source
|
|
288
|
-
</Heading>
|
|
289
|
-
<Code size="3">src/api/validators/json.js</Code>
|
|
290
|
-
<Heading size="4" mb="2" mt="4">
|
|
291
|
-
Usage Example
|
|
292
|
-
</Heading>
|
|
293
|
-
<Code size="3">{`export const validateStructure = async ({
|
|
265
|
+
const validationExampleCode = `export const validateStructure = async ({
|
|
294
266
|
io,
|
|
295
267
|
flags,
|
|
296
268
|
validators: { validateWithSchema },
|
|
297
269
|
}) => {
|
|
298
270
|
const content = await io.readArtifact("research-output.json");
|
|
299
|
-
const result = validateWithSchema(
|
|
271
|
+
const result = validateWithSchema(mySchema, content);
|
|
300
272
|
|
|
301
273
|
if (!result.valid) {
|
|
302
|
-
console.warn("
|
|
274
|
+
console.warn("Validation failed", result.errors);
|
|
303
275
|
return { output: {}, flags: { ...flags, validationFailed: true } };
|
|
304
276
|
}
|
|
305
277
|
return { output: {}, flags };
|
|
306
|
-
}
|
|
307
|
-
|
|
278
|
+
};`;
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<Layout>
|
|
282
|
+
<PageSubheader breadcrumbs={breadcrumbs} />
|
|
283
|
+
|
|
284
|
+
<Flex gap="6" className="relative">
|
|
285
|
+
{/* Sticky Section Navigation */}
|
|
286
|
+
<nav
|
|
287
|
+
aria-label="Page sections"
|
|
288
|
+
className="hidden lg:block w-48 shrink-0"
|
|
289
|
+
>
|
|
290
|
+
<div className="sticky top-28 space-y-1">
|
|
291
|
+
<Text
|
|
292
|
+
size="1"
|
|
293
|
+
weight="medium"
|
|
294
|
+
className="text-gray-400 uppercase tracking-wide mb-3 block"
|
|
295
|
+
>
|
|
296
|
+
On this page
|
|
297
|
+
</Text>
|
|
298
|
+
{sections.map(({ id, label, icon: Icon }) => (
|
|
299
|
+
<a
|
|
300
|
+
key={id}
|
|
301
|
+
href={`#${id}`}
|
|
302
|
+
className={`flex items-center gap-2 px-3 py-2 text-sm rounded-md transition-colors ${
|
|
303
|
+
activeSection === id
|
|
304
|
+
? "bg-blue-50 text-blue-700 font-medium"
|
|
305
|
+
: "text-gray-600 hover:bg-gray-100 hover:text-gray-900"
|
|
306
|
+
}`}
|
|
307
|
+
>
|
|
308
|
+
<Icon className="h-4 w-4" />
|
|
309
|
+
{label}
|
|
310
|
+
</a>
|
|
311
|
+
))}
|
|
312
|
+
</div>
|
|
313
|
+
</nav>
|
|
308
314
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
<Heading size="4" mb="2">
|
|
314
|
-
Example .env Configuration
|
|
315
|
+
{/* Main Content */}
|
|
316
|
+
<Box className="flex-1 min-w-0 pb-16">
|
|
317
|
+
<Heading size="7" className="mb-2">
|
|
318
|
+
Pipeline API Reference
|
|
315
319
|
</Heading>
|
|
316
|
-
<
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
320
|
+
<Text as="p" size="3" className="text-gray-600" mb="3">
|
|
321
|
+
Everything you need to build pipeline tasks — from file I/O to LLM
|
|
322
|
+
calls.
|
|
323
|
+
</Text>
|
|
324
|
+
|
|
325
|
+
{/* Environment Section */}
|
|
326
|
+
<CollapsibleSection
|
|
327
|
+
id="environment"
|
|
328
|
+
title="Environment Setup"
|
|
329
|
+
icon={Key}
|
|
330
|
+
defaultOpen={true}
|
|
331
|
+
>
|
|
332
|
+
<Text as="p" size="3" className="text-gray-600 mb-4">
|
|
333
|
+
Configure API keys in your <Code size="2">.env</Code> file before
|
|
334
|
+
running pipelines. Only add keys for providers you plan to use.
|
|
335
|
+
</Text>
|
|
336
|
+
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
|
337
|
+
<div className="grid gap-2">
|
|
338
|
+
{envVars.map(({ name, provider }) => (
|
|
339
|
+
<Flex key={name} align="center" justify="between">
|
|
340
|
+
<Code size="2">{name}=</Code>
|
|
341
|
+
<Text size="1" className="text-gray-500">
|
|
342
|
+
{provider}
|
|
343
|
+
</Text>
|
|
344
|
+
</Flex>
|
|
345
|
+
))}
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
</CollapsibleSection>
|
|
349
|
+
|
|
350
|
+
{/* Getting Started Section */}
|
|
351
|
+
<CollapsibleSection
|
|
352
|
+
id="getting-started"
|
|
353
|
+
title="Getting Started: Seed Files"
|
|
354
|
+
icon={FileText}
|
|
355
|
+
defaultOpen={true}
|
|
356
|
+
>
|
|
357
|
+
<Text as="p" size="3" className="text-gray-600 mb-4">
|
|
358
|
+
A seed file initiates a pipeline job. Upload it via the UI or
|
|
359
|
+
place it in the <Code size="2">pending/</Code> directory.
|
|
360
|
+
</Text>
|
|
361
|
+
|
|
362
|
+
<div className="space-y-4">
|
|
363
|
+
<div>
|
|
364
|
+
<Text size="2" weight="medium" className="mb-2 block">
|
|
365
|
+
Required Fields
|
|
366
|
+
</Text>
|
|
367
|
+
<div className="space-y-2">
|
|
368
|
+
<Flex align="start" gap="3" className="text-base">
|
|
369
|
+
<Code size="2" className="shrink-0 mt-0.5">
|
|
370
|
+
name
|
|
371
|
+
</Code>
|
|
372
|
+
<Text className="text-gray-600">
|
|
373
|
+
Unique job identifier — printable characters only, max 120
|
|
374
|
+
chars
|
|
375
|
+
</Text>
|
|
376
|
+
</Flex>
|
|
377
|
+
<Flex align="start" gap="3" className="text-base">
|
|
378
|
+
<Code size="2" className="shrink-0 mt-0.5">
|
|
379
|
+
pipeline
|
|
380
|
+
</Code>
|
|
381
|
+
<Text className="text-gray-600">
|
|
382
|
+
Pipeline slug from your registry (e.g.,
|
|
383
|
+
content-generation)
|
|
384
|
+
</Text>
|
|
385
|
+
</Flex>
|
|
386
|
+
<Flex align="start" gap="3" className="text-base">
|
|
387
|
+
<Code size="2" className="shrink-0 mt-0.5">
|
|
388
|
+
data
|
|
389
|
+
</Code>
|
|
390
|
+
<Text className="text-gray-600">
|
|
391
|
+
Object containing any context your pipeline tasks expect
|
|
392
|
+
</Text>
|
|
393
|
+
</Flex>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
|
|
397
|
+
<div>
|
|
398
|
+
<Text size="2" weight="medium" className="mb-2 block">
|
|
399
|
+
Example
|
|
400
|
+
</Text>
|
|
401
|
+
<CopyableCodeBlock maxHeight="200px">
|
|
402
|
+
{JSON.stringify(sampleSeed, null, 2)}
|
|
403
|
+
</CopyableCodeBlock>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
</CollapsibleSection>
|
|
407
|
+
|
|
408
|
+
{/* IO API Section */}
|
|
409
|
+
<CollapsibleSection
|
|
410
|
+
id="io-api"
|
|
411
|
+
title="IO API"
|
|
412
|
+
icon={Database}
|
|
413
|
+
defaultOpen={true}
|
|
414
|
+
>
|
|
415
|
+
<Text as="p" size="3" className="text-gray-600 mb-6">
|
|
416
|
+
File operations for reading/writing artifacts, logs, and temporary
|
|
417
|
+
files. All functions are available on the <Code size="2">io</Code>{" "}
|
|
418
|
+
object passed to task stages.
|
|
419
|
+
</Text>
|
|
420
|
+
|
|
421
|
+
{/* Write Functions */}
|
|
422
|
+
<div className="mb-6">
|
|
423
|
+
<Flex align="center" gap="2" className="mb-3">
|
|
424
|
+
<div className="w-2 h-2 rounded-full bg-green-500" />
|
|
425
|
+
<Text size="2" weight="medium">
|
|
426
|
+
Write Functions
|
|
427
|
+
</Text>
|
|
428
|
+
</Flex>
|
|
429
|
+
<div className="border border-gray-200 rounded-lg px-4 bg-white">
|
|
430
|
+
{writeFunctions.map((fn) => (
|
|
431
|
+
<FunctionRow key={fn.name} {...fn} />
|
|
432
|
+
))}
|
|
433
|
+
</div>
|
|
434
|
+
</div>
|
|
435
|
+
|
|
436
|
+
{/* Read Functions */}
|
|
437
|
+
<div className="mb-6">
|
|
438
|
+
<Flex align="center" gap="2" className="mb-3">
|
|
439
|
+
<div className="w-2 h-2 rounded-full bg-blue-500" />
|
|
440
|
+
<Text size="2" weight="medium">
|
|
441
|
+
Read Functions
|
|
442
|
+
</Text>
|
|
443
|
+
</Flex>
|
|
444
|
+
<div className="border border-gray-200 rounded-lg px-4 bg-white">
|
|
445
|
+
{readFunctions.map((fn) => (
|
|
446
|
+
<FunctionRow key={fn.name} {...fn} />
|
|
447
|
+
))}
|
|
448
|
+
</div>
|
|
449
|
+
</div>
|
|
450
|
+
|
|
451
|
+
{/* Utility Functions */}
|
|
452
|
+
<div>
|
|
453
|
+
<Flex align="center" gap="2" className="mb-3">
|
|
454
|
+
<div className="w-2 h-2 rounded-full bg-purple-500" />
|
|
455
|
+
<Text size="2" weight="medium">
|
|
456
|
+
Utility Functions
|
|
457
|
+
</Text>
|
|
458
|
+
</Flex>
|
|
459
|
+
<div className="border border-gray-200 rounded-lg px-4 bg-white">
|
|
460
|
+
{utilityFunctions.map((fn) => (
|
|
461
|
+
<FunctionRow key={fn.name} {...fn} />
|
|
462
|
+
))}
|
|
463
|
+
</div>
|
|
464
|
+
</div>
|
|
465
|
+
</CollapsibleSection>
|
|
466
|
+
|
|
467
|
+
{/* LLM API Section */}
|
|
468
|
+
<CollapsibleSection
|
|
469
|
+
id="llm-api"
|
|
470
|
+
title="LLM API"
|
|
471
|
+
icon={Cpu}
|
|
472
|
+
defaultOpen={true}
|
|
473
|
+
>
|
|
474
|
+
<Text as="p" size="3" className="text-gray-600 mb-6">
|
|
475
|
+
Call language models from any provider using a unified interface.
|
|
476
|
+
Functions are available on the <Code size="2">llm</Code> object.
|
|
477
|
+
</Text>
|
|
478
|
+
|
|
479
|
+
<div className="space-y-6">
|
|
480
|
+
{/* Arguments */}
|
|
481
|
+
<div>
|
|
482
|
+
<Text size="2" weight="medium" className="mb-2 block">
|
|
483
|
+
Arguments
|
|
484
|
+
</Text>
|
|
485
|
+
<CopyableCodeBlock maxHeight="280px">
|
|
486
|
+
{llmArgsCode}
|
|
487
|
+
</CopyableCodeBlock>
|
|
488
|
+
</div>
|
|
489
|
+
|
|
490
|
+
{/* Returns */}
|
|
491
|
+
<div>
|
|
492
|
+
<Text size="2" weight="medium" className="mb-2 block">
|
|
493
|
+
Returns
|
|
494
|
+
</Text>
|
|
495
|
+
<div className="bg-gray-50 rounded-lg p-3 border border-gray-200">
|
|
496
|
+
<Code size="2">{`Promise<{ content: any, usage?: object, raw?: any }>`}</Code>
|
|
497
|
+
</div>
|
|
498
|
+
</div>
|
|
499
|
+
|
|
500
|
+
{/* Available Models */}
|
|
501
|
+
{llmFunctions && (
|
|
502
|
+
<div>
|
|
503
|
+
<Text size="2" weight="medium" className="mb-3 block">
|
|
504
|
+
Available Models
|
|
505
|
+
</Text>
|
|
506
|
+
<div className="border border-gray-200 rounded-lg overflow-hidden">
|
|
507
|
+
<Table.Root>
|
|
508
|
+
<Table.Header>
|
|
509
|
+
<Table.Row>
|
|
510
|
+
<Table.ColumnHeaderCell className="bg-gray-50">
|
|
511
|
+
Function
|
|
512
|
+
</Table.ColumnHeaderCell>
|
|
513
|
+
<Table.ColumnHeaderCell className="bg-gray-50">
|
|
514
|
+
Model
|
|
515
|
+
</Table.ColumnHeaderCell>
|
|
516
|
+
</Table.Row>
|
|
517
|
+
</Table.Header>
|
|
518
|
+
<Table.Body>
|
|
519
|
+
{Object.entries(llmFunctions).flatMap(
|
|
520
|
+
([_provider, functions]) =>
|
|
521
|
+
functions.map((fn) => (
|
|
522
|
+
<Table.Row key={fn.fullPath}>
|
|
523
|
+
<Table.RowHeaderCell>
|
|
524
|
+
<Code size="2">{fn.fullPath}</Code>
|
|
525
|
+
</Table.RowHeaderCell>
|
|
526
|
+
<Table.Cell>
|
|
527
|
+
<Code size="2" className="text-gray-600">
|
|
528
|
+
{fn.model}
|
|
529
|
+
</Code>
|
|
530
|
+
</Table.Cell>
|
|
531
|
+
</Table.Row>
|
|
532
|
+
))
|
|
533
|
+
)}
|
|
534
|
+
</Table.Body>
|
|
535
|
+
</Table.Root>
|
|
536
|
+
</div>
|
|
537
|
+
</div>
|
|
538
|
+
)}
|
|
539
|
+
</div>
|
|
540
|
+
</CollapsibleSection>
|
|
541
|
+
|
|
542
|
+
{/* Validation Section */}
|
|
543
|
+
<CollapsibleSection
|
|
544
|
+
id="validation"
|
|
545
|
+
title="Validation API"
|
|
546
|
+
icon={Shield}
|
|
547
|
+
defaultOpen={true}
|
|
548
|
+
>
|
|
549
|
+
<Text as="p" size="3" className="text-gray-600 mb-6">
|
|
550
|
+
Validate JSON data against schemas using{" "}
|
|
551
|
+
<Code size="2">validateWithSchema</Code>. Available via the{" "}
|
|
552
|
+
<Code size="2">validators</Code> object in task stages.
|
|
553
|
+
</Text>
|
|
554
|
+
|
|
555
|
+
<div className="space-y-6">
|
|
556
|
+
{/* Signature */}
|
|
557
|
+
<div>
|
|
558
|
+
<Text size="2" weight="medium" className="mb-2 block">
|
|
559
|
+
Function Signature
|
|
560
|
+
</Text>
|
|
561
|
+
<div className="bg-gray-50 rounded-lg p-3 border border-gray-200">
|
|
562
|
+
<Code size="2">
|
|
563
|
+
validateWithSchema(schema, data) → {"{"} valid: boolean,
|
|
564
|
+
errors?: AjvError[] {"}"}
|
|
565
|
+
</Code>
|
|
566
|
+
</div>
|
|
567
|
+
</div>
|
|
568
|
+
|
|
569
|
+
{/* Behavior */}
|
|
570
|
+
<div>
|
|
571
|
+
<Text size="2" weight="medium" className="mb-2 block">
|
|
572
|
+
Behavior
|
|
573
|
+
</Text>
|
|
574
|
+
<ul className="space-y-2 text-base text-gray-600">
|
|
575
|
+
<li className="flex items-start gap-2">
|
|
576
|
+
<span className="text-blue-500 mt-1">•</span>
|
|
577
|
+
<span>
|
|
578
|
+
Accepts string or object data — strings are parsed as JSON
|
|
579
|
+
first
|
|
580
|
+
</span>
|
|
581
|
+
</li>
|
|
582
|
+
<li className="flex items-start gap-2">
|
|
583
|
+
<span className="text-blue-500 mt-1">•</span>
|
|
584
|
+
<span>
|
|
585
|
+
Uses Ajv with{" "}
|
|
586
|
+
<Code size="2">
|
|
587
|
+
{"{ allErrors: true, strict: false }"}
|
|
588
|
+
</Code>
|
|
589
|
+
</span>
|
|
590
|
+
</li>
|
|
591
|
+
<li className="flex items-start gap-2">
|
|
592
|
+
<span className="text-blue-500 mt-1">•</span>
|
|
593
|
+
<span>
|
|
594
|
+
Returns <Code size="2">{"{ valid: true }"}</Code> on
|
|
595
|
+
success, or{" "}
|
|
596
|
+
<Code size="2">{"{ valid: false, errors: [...] }"}</Code>{" "}
|
|
597
|
+
on failure
|
|
598
|
+
</span>
|
|
599
|
+
</li>
|
|
600
|
+
</ul>
|
|
601
|
+
</div>
|
|
602
|
+
|
|
603
|
+
{/* Example */}
|
|
604
|
+
<div>
|
|
605
|
+
<Text size="2" weight="medium" className="mb-2 block">
|
|
606
|
+
Usage Example
|
|
607
|
+
</Text>
|
|
608
|
+
<CopyableCodeBlock maxHeight="240px">
|
|
609
|
+
{validationExampleCode}
|
|
610
|
+
</CopyableCodeBlock>
|
|
611
|
+
</div>
|
|
612
|
+
|
|
613
|
+
{/* Source */}
|
|
614
|
+
<div className="text-base text-gray-500">
|
|
615
|
+
<span>Source: </span>
|
|
616
|
+
<Code size="2">src/api/validators/json.js</Code>
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
</CollapsibleSection>
|
|
354
620
|
</Box>
|
|
355
|
-
</
|
|
621
|
+
</Flex>
|
|
356
622
|
</Layout>
|
|
357
623
|
);
|
|
358
624
|
}
|