@ryanfw/prompt-orchestration-pipeline 0.15.1 → 0.16.1

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.
@@ -1,358 +1,624 @@
1
1
  import React, { useState, useEffect } from "react";
2
- import { Box, Heading, Table, Code, Text } from "@radix-ui/themes";
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 { Button } from "../components/ui/button.jsx";
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
- const ioFunctions = [
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: "Write an artifact file",
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
- notes: "Writes to {workDir}/files/artifacts; updates tasks-status.json",
39
+ path: "{workDir}/files/artifacts",
15
40
  },
16
41
  {
17
42
  name: "writeLog",
18
- description: "Write a log file",
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
- notes:
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: "Write a temporary file",
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
- notes: "Writes to {workDir}/files/tmp; updates tasks-status.json",
53
+ path: "{workDir}/files/tmp",
32
54
  },
55
+ ];
56
+
57
+ const readFunctions = [
33
58
  {
34
59
  name: "readArtifact",
35
- description: "Read an artifact file",
60
+ description: "Load artifacts from previous tasks",
36
61
  params: "name: string",
37
62
  returns: "Promise<string>",
38
- notes: "Reads from {workDir}/files/artifacts",
63
+ path: "{workDir}/files/artifacts",
39
64
  },
40
65
  {
41
66
  name: "readLog",
42
- description: "Read a log file",
67
+ description: "Read log file contents",
43
68
  params: "name: string",
44
69
  returns: "Promise<string>",
45
- notes: "Reads from {workDir}/files/logs",
70
+ path: "{workDir}/files/logs",
46
71
  },
47
72
  {
48
73
  name: "readTmp",
49
- description: "Read a temporary file",
74
+ description: "Read temporary file contents",
50
75
  params: "name: string",
51
76
  returns: "Promise<string>",
52
- notes: "Reads from {workDir}/files/tmp",
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: "some-name",
113
+ name: "my-blog-post",
72
114
  pipeline: "content-generation",
73
115
  data: {
74
- type: "some-type",
75
116
  contentType: "blog-post",
76
- targetAudience: "software-developers",
77
- tone: "professional-yet-accessible",
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
- const breadcrumbs = [{ label: "Home", href: "/" }, { label: "Code" }];
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
- const handleCopySeed = () => {
103
- navigator.clipboard.writeText(JSON.stringify(sampleSeed, null, 2));
104
- };
237
+ sections.forEach(({ id }) => {
238
+ const el = document.getElementById(id);
239
+ if (el) observer.observe(el);
240
+ });
105
241
 
106
- return (
107
- <Layout>
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
- <Heading size="6" mb="4">
161
- Pipeline Task IO API
162
- </Heading>
163
- <Box overflowX="auto">
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
- <Heading size="6" mt="8" mb="4">
197
- Pipeline Task LLM API
198
- </Heading>
199
- <Box mb="4">
200
- <Heading size="4" mb="2">
201
- Arguments
202
- </Heading>
203
- <Code size="3" mb="4">
204
- {`{
205
- messages: Array<{role: "system"|"user"|"assistant", content: string }>,
206
- temperature?: number,
207
- maxTokens?: number,
208
- responseFormat?: "json" | { type: "json_object" | { type: "json_schema", name: string, json_schema: object } },
209
- stop?: string | string[],
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
- <Heading size="6" mt="8" mb="4">
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(researchJsonSchema, content);
271
+ const result = validateWithSchema(mySchema, content);
300
272
 
301
273
  if (!result.valid) {
302
- console.warn("[Research:validateStructure] Validation failed", result.errors);
274
+ console.warn("Validation failed", result.errors);
303
275
  return { output: {}, flags: { ...flags, validationFailed: true } };
304
276
  }
305
277
  return { output: {}, flags };
306
- };`}</Code>
307
- </Box>
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
- <Heading size="6" mt="8" mb="4">
310
- Environment Configuration
311
- </Heading>
312
- <Box mb="4">
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
- <Box overflowX="auto">
317
- <Table.Root>
318
- <Table.Header>
319
- <Table.Row>
320
- <Table.ColumnHeaderCell>
321
- Environment Variable
322
- </Table.ColumnHeaderCell>
323
- </Table.Row>
324
- </Table.Header>
325
- <Table.Body>
326
- <Table.Row>
327
- <Table.RowHeaderCell>
328
- <Code size="3">OPENAI_API_KEY=</Code>
329
- </Table.RowHeaderCell>
330
- </Table.Row>
331
- <Table.Row>
332
- <Table.RowHeaderCell>
333
- <Code size="3">DEEPSEEK_API_KEY=</Code>
334
- </Table.RowHeaderCell>
335
- </Table.Row>
336
- <Table.Row>
337
- <Table.RowHeaderCell>
338
- <Code size="3">GEMINI_API_KEY=</Code>
339
- </Table.RowHeaderCell>
340
- </Table.Row>
341
- <Table.Row>
342
- <Table.RowHeaderCell>
343
- <Code size="3">ANTHROPIC_API_KEY=</Code>
344
- </Table.RowHeaderCell>
345
- </Table.Row>
346
- <Table.Row>
347
- <Table.RowHeaderCell>
348
- <Code size="3">ZHIPU_API_KEY=</Code>
349
- </Table.RowHeaderCell>
350
- </Table.Row>
351
- </Table.Body>
352
- </Table.Root>
353
- </Box>
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
- </Box>
621
+ </Flex>
356
622
  </Layout>
357
623
  );
358
624
  }