@n1k1t/pipelain 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +575 -0
- package/package.json +113 -0
package/README.md
ADDED
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
<div align='center'>
|
|
2
|
+
<h1>PipelAIn</h1>
|
|
3
|
+
<p>Type-safe AI-powered execution pipelines</p>
|
|
4
|
+
|
|
5
|
+
<img src="https://raw.githubusercontent.com/n1k1t/pipelain/refs/heads/master/images/preview.png?raw=true" />
|
|
6
|
+
|
|
7
|
+
<br />
|
|
8
|
+
<br />
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+

|
|
12
|
+
%20div%201000&label=coverage)
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
Powerful utility to build and execute type-safe AI pipelines with structured outputs and tool integration.
|
|
16
|
+
|
|
17
|
+
- [Features](#features)
|
|
18
|
+
- [Installation](#installation)
|
|
19
|
+
- [First Steps](#first-steps)
|
|
20
|
+
- [Simple Example](#simple-example)
|
|
21
|
+
- [Environment Variables](#environment-variables)
|
|
22
|
+
- [Pipeline Step Utilities](#pipeline-step-utilities)
|
|
23
|
+
- [factory](#factory)
|
|
24
|
+
- [utils](#utils)
|
|
25
|
+
- [context](#context)
|
|
26
|
+
- [utils.content (ContentFactory)](#utilscontent-contentfactory)
|
|
27
|
+
- [factory.tools (LlmToolsFactory)](#factorytools-llmtoolsfactory)
|
|
28
|
+
- [factory.skills (LlmSkillsFactory)](#factoryskills-llmskillsfactory)
|
|
29
|
+
- [Usage](#usage)
|
|
30
|
+
- [Logging and Events](#logging-and-events)
|
|
31
|
+
- [Structured Prompt Content](#structured-prompt-content)
|
|
32
|
+
- [AI Step with LLM Configuration](#ai-step-with-llm-configuration)
|
|
33
|
+
- [Debugging AI Steps](#debugging-ai-steps)
|
|
34
|
+
- [Combining self and ai Steps](#combining-self-and-ai-steps)
|
|
35
|
+
- [Parallel Execution with swarm](#parallel-execution-with-swarm)
|
|
36
|
+
- [Iterative Execution with loop](#iterative-execution-with-loop)
|
|
37
|
+
- [Multi-Provider Pipelines](#multi-provider-pipelines)
|
|
38
|
+
- [MCP (Model Context Protocol) Integration](#mcp-model-context-protocol-integration)
|
|
39
|
+
- [Using Skills](#using-skills)
|
|
40
|
+
- [Extensions](#extensions)
|
|
41
|
+
- [Custom LLM Tools](#custom-llm-tools)
|
|
42
|
+
- [Custom LLM Skills](#custom-llm-skills)
|
|
43
|
+
- [Registering Skills from Markdown](#registering-skills-from-markdown)
|
|
44
|
+
- [License](#license)
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- **Type-safe**: Built with TypeScript and Zod for full type safety of inputs, outputs, and intermediate states.
|
|
49
|
+
- **Structured Outputs**: Enforce AI responses to match specific Zod schemas.
|
|
50
|
+
- **Tool Integration**: Easily provide tools (web search, file system, etc.) to the AI.
|
|
51
|
+
- **Step-by-step Execution**: Define complex workflows as a series of steps.
|
|
52
|
+
- **Context-aware**: Shared context and state across all pipeline steps.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm install @n1k1t/pipelain zod
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## First Steps
|
|
61
|
+
|
|
62
|
+
To use the library, you need to provide an API key for the LLM provider. Create a `.env` file in your project root and add your key:
|
|
63
|
+
|
|
64
|
+
```env
|
|
65
|
+
PIPELAIN_API_KEY=your_api_key_here
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Simple Example
|
|
69
|
+
|
|
70
|
+
A basic pipeline that translates text and returns a structured response:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import z from 'zod/v3';
|
|
74
|
+
import { PipelineCompiler } from '@n1k1t/pipelain';
|
|
75
|
+
|
|
76
|
+
const translate = PipelineCompiler
|
|
77
|
+
.build('Translator')
|
|
78
|
+
.input(z.string())
|
|
79
|
+
.step('translated', ({ factory, context }) => factory
|
|
80
|
+
.ai('Translating')
|
|
81
|
+
.schema(z.object({
|
|
82
|
+
ru: z.string().describe('Russian translation'),
|
|
83
|
+
es: z.string().describe('Spanish translation'),
|
|
84
|
+
en: z.string().describe('English translation'),
|
|
85
|
+
}))
|
|
86
|
+
.prompt([
|
|
87
|
+
`Translate this text to Russian, Spanish and English: ${context.input}`,
|
|
88
|
+
])
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
(async () => {
|
|
92
|
+
const compiled = await translate.compile();
|
|
93
|
+
const result = await compiled.run('Hello');
|
|
94
|
+
|
|
95
|
+
console.log(result.translated.es); // Hola
|
|
96
|
+
})();
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Environment Variables
|
|
100
|
+
|
|
101
|
+
The following environment variables are supported:
|
|
102
|
+
|
|
103
|
+
| Variable | Description | Default |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| `PIPELAIN_API_KEY` | API key for the LLM provider. | - |
|
|
106
|
+
| `PIPELAIN_API_URL` | Custom API URL for the LLM provider. | - |
|
|
107
|
+
| `PIPELAIN_SKILLS_DIR` | Directory path where LLM skills are stored. | `~/.agents/skills` |
|
|
108
|
+
| `PIPELAIN_MODEL` | Default LLM model to use. | `gemini-flash-latest` |
|
|
109
|
+
| `PIPELAIN_PROVIDER` | LLM provider name (e.g., `google`, `openai`). | - |
|
|
110
|
+
|
|
111
|
+
## Pipeline Step Utilities
|
|
112
|
+
|
|
113
|
+
Each pipeline step receives a set of utilities to interact with the environment, AI, and context.
|
|
114
|
+
|
|
115
|
+
### `factory`
|
|
116
|
+
Used to create different types of steps:
|
|
117
|
+
- `ai(description?)`: Creates a step that executes on the AI side.
|
|
118
|
+
- `self(description?)`: Creates a step that executes on the local machine.
|
|
119
|
+
- `swarm(description?)`: Executes multiple steps or pipelines in parallel.
|
|
120
|
+
- `loop(description?)`: Creates a loop of steps.
|
|
121
|
+
- `tools`: Access to LLM tools factory (e.g., `factory.tools.web()`).
|
|
122
|
+
- `skills`: Access to LLM skills factory (e.g., `factory.skills.all()`).
|
|
123
|
+
|
|
124
|
+
### `utils`
|
|
125
|
+
General-purpose utilities:
|
|
126
|
+
- `content`: `ContentFactory` instance to create structured prompt content (articles, tasks, rules, attachments).
|
|
127
|
+
- `bash`: Execute shell commands on the local machine.
|
|
128
|
+
- `log`: Emit log events for the current pipeline session.
|
|
129
|
+
|
|
130
|
+
### `context`
|
|
131
|
+
Shared state and configuration:
|
|
132
|
+
- `input`: The input data provided to the pipeline.
|
|
133
|
+
- `state`: Shared state across all steps.
|
|
134
|
+
- `llm`: Current LLM provider configuration.
|
|
135
|
+
- `project`: Information about the current project.
|
|
136
|
+
|
|
137
|
+
### `utils.content` (ContentFactory)
|
|
138
|
+
Used to create structured prompt content. These methods are available via `utils.content` in pipeline steps.
|
|
139
|
+
|
|
140
|
+
| Method | Description | Example |
|
|
141
|
+
| --- | --- | --- |
|
|
142
|
+
| `article(title, content)` | Creates a `## Title` section with nested markdown content. | `utils.content.article('Context', [{ p: 'Some text' }])` |
|
|
143
|
+
| `rules(list)` | Creates a bulleted list of rules/constraints. | `utils.content.rules(['Use JSON', 'Be concise'])` |
|
|
144
|
+
| `tasks(list)` | Creates a numbered list of tasks for the AI. | `utils.content.tasks(['Analyze data', 'Write summary'])` |
|
|
145
|
+
| `sources(list)` | Creates a list of source links. | `utils.content.sources(['https://google.com'])` |
|
|
146
|
+
| `attachment(title, payload)` | Attaches data (object, string) as a file-like block. | `utils.content.attachment('Data', { content: { id: 1 } })` |
|
|
147
|
+
| `file(title, path)` | Reads a local file and attaches it as context. | `await utils.content.file('Config', 'package.json')` |
|
|
148
|
+
| `glob(title, pattern)` | Reads multiple files by pattern and attaches them. | `await utils.content.glob('Source', 'src/**/*.ts')` |
|
|
149
|
+
| `plain(text)` | Adds raw markdown text. | `utils.content.plain('### Subtitle\nText')` |
|
|
150
|
+
|
|
151
|
+
### `factory.tools` (LlmToolsFactory)
|
|
152
|
+
Used to provide tools to the AI. These methods are available via `factory.tools` in pipeline steps.
|
|
153
|
+
|
|
154
|
+
| Method | Description | Included Tools |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| `web()` | Tools for web interaction. | `search`, `fetch` |
|
|
157
|
+
| `files('read')` | Read-only file system tools. | `read`, `grep`, `glob`, `ls` |
|
|
158
|
+
| `files('read-write')` | Read and write file system tools. | `read`, `grep`, `glob`, `ls`, `mkdir`, `write`, `edit`, `rm` |
|
|
159
|
+
| `commands(['npm', 'npx'])` | Package manager tools. | `npm`, `npx` |
|
|
160
|
+
| `all()` | Includes all available tools. | All of the above |
|
|
161
|
+
| `custom(tools)` | Includes custom tool implementations. | - |
|
|
162
|
+
| `all()` | Includes all available tools. | All of the above |
|
|
163
|
+
|
|
164
|
+
### `factory.skills` (LlmSkillsFactory)
|
|
165
|
+
Used to provide domain-specific instructions and workflows to the AI. These methods are available via `factory.skills` in pipeline steps.
|
|
166
|
+
|
|
167
|
+
| Method | Description |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| `all()` | Includes all registered skills. |
|
|
170
|
+
| `match(pattern)` | Includes skills matching a minimatch pattern. |
|
|
171
|
+
| `register(raw)` | Registers a new skill from raw markdown content (with frontmatter). |
|
|
172
|
+
| `custom(skills)` | Includes custom skill objects. |
|
|
173
|
+
| `provide()` | Returns the list of included skills for LLM configuration. |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Usage
|
|
178
|
+
|
|
179
|
+
### Logging and Events
|
|
180
|
+
|
|
181
|
+
You can use `stdout.console` to output pipeline progress and logs to `stdout`:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { stdout } from '@n1k1t/pipelain';
|
|
185
|
+
|
|
186
|
+
(async () => {
|
|
187
|
+
const compiled = await translate.compile({ stdout: stdout.console });
|
|
188
|
+
await compiled.run('Hello');
|
|
189
|
+
})();
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
To use a custom logger or override specific event handlers, use `PipelineStdout`:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
import { PipelineStdout } from '@n1k1t/pipelain';
|
|
196
|
+
|
|
197
|
+
const customStdout = PipelineStdout
|
|
198
|
+
.build(console) // Pass any logger with .info and .warn methods
|
|
199
|
+
.override('log', (event) => {
|
|
200
|
+
console.log(`[CUSTOM LOG] ${event.message}`);
|
|
201
|
+
})
|
|
202
|
+
.override('step:run', (event) => {
|
|
203
|
+
if (event.meta.state === 'SUCCESS') {
|
|
204
|
+
console.log(`Step ${event.step.title} finished in ${event.meta.spent}ms`);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
(async () => {
|
|
209
|
+
const compiled = await translate.compile({ stdout: customStdout });
|
|
210
|
+
await compiled.run('Hello');
|
|
211
|
+
})();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Structured Prompt Content
|
|
215
|
+
|
|
216
|
+
You can combine multiple content types to create a rich, structured prompt for the AI:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
.step('complex_task', async ({ factory, utils, context }) => factory
|
|
220
|
+
.ai('Complex Processing')
|
|
221
|
+
.prompt([
|
|
222
|
+
// 1. Global rules for the AI
|
|
223
|
+
utils.content.rules([
|
|
224
|
+
'Use professional tone',
|
|
225
|
+
'Output must be valid JSON'
|
|
226
|
+
]),
|
|
227
|
+
|
|
228
|
+
// 2. Structured article with nested content
|
|
229
|
+
utils.content.article('Project Overview', [
|
|
230
|
+
{ p: 'This project is a type-safe pipeline builder.' },
|
|
231
|
+
{ h2: 'Key Goals' },
|
|
232
|
+
{ ul: ['Safety', 'Performance', 'Flexibility'] }
|
|
233
|
+
]),
|
|
234
|
+
|
|
235
|
+
// 3. Attachments (data or files)
|
|
236
|
+
utils.content.attachment('User Data', {
|
|
237
|
+
content: { id: 1, name: 'John Doe' }
|
|
238
|
+
}),
|
|
239
|
+
|
|
240
|
+
// 4. Local files and glob patterns
|
|
241
|
+
await utils.content.file('Package Info', 'package.json'),
|
|
242
|
+
await utils.content.glob('Source Code', 'src/utils/*.ts'),
|
|
243
|
+
|
|
244
|
+
// 5. External sources
|
|
245
|
+
utils.content.sources(['https://github.com/n1k1t/pipelain']),
|
|
246
|
+
|
|
247
|
+
// 6. Raw markdown
|
|
248
|
+
utils.content.plain('> Note: This is a critical task.'),
|
|
249
|
+
|
|
250
|
+
// 7. Specific tasks for the AI to complete
|
|
251
|
+
utils.content.tasks([
|
|
252
|
+
'Review the `Project Overview` article',
|
|
253
|
+
'Analyze `User Data` and `Source Code`',
|
|
254
|
+
'Generate a summary based on the rules'
|
|
255
|
+
])
|
|
256
|
+
])
|
|
257
|
+
)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### AI Step with LLM Configuration
|
|
261
|
+
|
|
262
|
+
You can customize the LLM behavior for a specific step, such as changing the temperature or providing tools. You can also restrict file tools to specific paths using `allowed` patterns:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
.step('research', ({ factory, context, utils }) => factory
|
|
266
|
+
.ai('Researching')
|
|
267
|
+
.llm(({ context }) => context.llm.assign({
|
|
268
|
+
temperature: 0.7,
|
|
269
|
+
limit: 10, // Max tool execution attempts
|
|
270
|
+
tools: factory.tools
|
|
271
|
+
.web()
|
|
272
|
+
.files('read-write', {
|
|
273
|
+
allowed: {
|
|
274
|
+
// Restrict 'write' tool to specific directory
|
|
275
|
+
write: ['src/generated/*.ts'],
|
|
276
|
+
// Restrict 'edit' tool to all TypeScript files
|
|
277
|
+
edit: ['src/**/*.ts'],
|
|
278
|
+
// Restrict 'rm' tool to temporary files
|
|
279
|
+
rm: ['temp/**/*']
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
.provide(),
|
|
283
|
+
}))
|
|
284
|
+
.schema(z.object({
|
|
285
|
+
summary: z.string(),
|
|
286
|
+
links: z.array(z.string())
|
|
287
|
+
}))
|
|
288
|
+
.prompt([
|
|
289
|
+
utils.content.tasks([
|
|
290
|
+
`Find information about: ${context.input}`,
|
|
291
|
+
'Summarize findings and provide source links'
|
|
292
|
+
])
|
|
293
|
+
])
|
|
294
|
+
)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Debugging AI Steps
|
|
298
|
+
|
|
299
|
+
You can use the `.debug()` method to inspect the prompts sent to the AI. When debug mode is enabled, the step will mock the AI response and save the generated prompt into a markdown file in the `.pipelain` directory:
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
.step('debug_example', ({ factory }) => factory
|
|
303
|
+
.ai('Debugging Step')
|
|
304
|
+
.debug() // Enables debug mode for this step
|
|
305
|
+
.schema(z.object({ result: z.string() }))
|
|
306
|
+
.prompt(['Analyze this complex data...'])
|
|
307
|
+
)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
The prompt will be saved to: `.pipelain/${timestamp}-${session-id}/${step-title}.md`.
|
|
311
|
+
|
|
312
|
+
### Combining `self` and `ai` Steps
|
|
313
|
+
|
|
314
|
+
You can use `self` steps to perform local computations, logging, or data transformation between AI steps:
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
const pipeline = PipelineCompiler
|
|
318
|
+
.build('Data Processor')
|
|
319
|
+
.input(z.string())
|
|
320
|
+
.step('extracted', ({ factory, context }) => factory
|
|
321
|
+
.ai('Extracting data')
|
|
322
|
+
.schema(z.object({
|
|
323
|
+
items: z.array(z.string())
|
|
324
|
+
}))
|
|
325
|
+
.prompt([`Extract items from: ${context.input}`])
|
|
326
|
+
)
|
|
327
|
+
// Local step to process data or log progress
|
|
328
|
+
.step(({ context, utils }) => {
|
|
329
|
+
utils.log(`Extracted ${context.state.extracted.items.length} items`);
|
|
330
|
+
|
|
331
|
+
// You can also modify the state or context here
|
|
332
|
+
context.merge({
|
|
333
|
+
state: {
|
|
334
|
+
processedCount: context.state.extracted.items.length
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
})
|
|
338
|
+
.step('summary', ({ factory, context }) => factory
|
|
339
|
+
.ai('Summarizing')
|
|
340
|
+
.schema(z.object({ text: z.string() }))
|
|
341
|
+
.prompt([
|
|
342
|
+
`Summarize these ${context.state.processedCount} items:`,
|
|
343
|
+
context.state.extracted.items.join(', ')
|
|
344
|
+
])
|
|
345
|
+
);
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Parallel Execution with `swarm`
|
|
349
|
+
|
|
350
|
+
Use `swarm` to execute multiple AI tasks in parallel. You can define subtasks using the `subtasks` method:
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
.step('analysis', ({ factory }) => factory
|
|
354
|
+
.swarm('Parallel Analysis')
|
|
355
|
+
.subtasks([
|
|
356
|
+
factory
|
|
357
|
+
.ai('Sentiment Analysis')
|
|
358
|
+
.schema(z.object({ score: z.number() }))
|
|
359
|
+
.prompt(({ context }) => [`Analyze sentiment of: ${context.input}`]),
|
|
360
|
+
|
|
361
|
+
factory
|
|
362
|
+
.ai('Keyword Extraction')
|
|
363
|
+
.schema(z.object({ tags: z.array(z.string()) }))
|
|
364
|
+
.prompt(({ context }) => [`Extract keywords from: ${context.input}`]),
|
|
365
|
+
])
|
|
366
|
+
.limit(2) // Optional: limit parallel executions
|
|
367
|
+
)
|
|
368
|
+
// Results will be available in context.state.analysis as an array of PromiseSettledResult
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Iterative Execution with `loop`
|
|
372
|
+
|
|
373
|
+
Use `loop` for tasks that require multiple iterations or validation, such as self-correction:
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
.step('refined_answer', ({ factory }) => factory
|
|
377
|
+
.loop('Self-Correction Loop')
|
|
378
|
+
.limit(3) // Max 3 attempts
|
|
379
|
+
.action(({ factory, context, verdict }) => factory
|
|
380
|
+
.ai('Answering')
|
|
381
|
+
.schema(z.object({
|
|
382
|
+
answer: z.string(),
|
|
383
|
+
isCorrect: z.boolean().describe('Self-check result')
|
|
384
|
+
}))
|
|
385
|
+
.prompt([
|
|
386
|
+
`Question: ${context.input}`,
|
|
387
|
+
// If this is a retry, include previous feedback
|
|
388
|
+
verdict.status === 'pending' ? verdict.content : ''
|
|
389
|
+
])
|
|
390
|
+
)
|
|
391
|
+
.condition(({ result }) => {
|
|
392
|
+
if (result.isCorrect) {
|
|
393
|
+
return { status: 'fulfilled' };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
status: 'pending',
|
|
398
|
+
content: 'Your previous answer was incorrect. Please try again and be more specific.'
|
|
399
|
+
};
|
|
400
|
+
})
|
|
401
|
+
)
|
|
402
|
+
// Result will contain { status: 'fulfilled' | 'voided', value: { answer, isCorrect } }
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Multi-Provider Pipelines
|
|
406
|
+
|
|
407
|
+
You can use different LLM providers for different steps in the same pipeline:
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
import { PipelineCompiler, llm } from '@n1k1t/pipelain';
|
|
411
|
+
|
|
412
|
+
const pipeline = PipelineCompiler
|
|
413
|
+
.build('Multi-Provider')
|
|
414
|
+
.input(z.string())
|
|
415
|
+
.step('initial_analysis', ({ factory }) => factory
|
|
416
|
+
.ai('Google Analysis')
|
|
417
|
+
.llm(() => llm.providers.LlmGoogleProvider.build('gemini-1.5-flash', {
|
|
418
|
+
connection: { key: process.env.GOOGLE_API_KEY! }
|
|
419
|
+
}))
|
|
420
|
+
.prompt(({ context }) => [`Analyze this: ${context.input}`])
|
|
421
|
+
)
|
|
422
|
+
.step('final_summary', ({ factory }) => factory
|
|
423
|
+
.ai('Anthropic Summary')
|
|
424
|
+
.llm(() => llm.providers.LlmAnthropicProvider.build('claude-3-5-sonnet-20240620', {
|
|
425
|
+
connection: { key: process.env.ANTHROPIC_API_KEY! }
|
|
426
|
+
}))
|
|
427
|
+
.prompt(({ context }) => [
|
|
428
|
+
`Summarize the analysis: ${context.state.initial_analysis}`
|
|
429
|
+
])
|
|
430
|
+
);
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### MCP (Model Context Protocol) Integration
|
|
434
|
+
|
|
435
|
+
You can integrate MCP servers into your pipeline steps. This allows the AI to use tools provided by external MCP servers. You can also filter which tools are enabled:
|
|
436
|
+
|
|
437
|
+
```ts
|
|
438
|
+
import { LlmMcp } from '@n1k1t/pipelain';
|
|
439
|
+
|
|
440
|
+
pipeline.step('mcp_research', ({ factory, context }) => factory
|
|
441
|
+
.ai('Researching with MCP')
|
|
442
|
+
.llm(({ context }) => context.llm.assign({
|
|
443
|
+
mcp: [
|
|
444
|
+
LlmMcp.build({
|
|
445
|
+
transport: {
|
|
446
|
+
type: 'stdio',
|
|
447
|
+
command: 'npx',
|
|
448
|
+
args: ['-y', '@modelcontextprotocol/server-gdrive'],
|
|
449
|
+
},
|
|
450
|
+
tools: {
|
|
451
|
+
// Enable only specific tools using names or minimatch patterns
|
|
452
|
+
enabled: ['list-files', 'read-file'],
|
|
453
|
+
// Or disable specific tools
|
|
454
|
+
// disabled: ['delete-file'],
|
|
455
|
+
},
|
|
456
|
+
}),
|
|
457
|
+
],
|
|
458
|
+
}))
|
|
459
|
+
.prompt(['List my recent files in Google Drive and summarize them.'])
|
|
460
|
+
);
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Using Skills
|
|
464
|
+
|
|
465
|
+
Skills allow you to inject reusable domain-specific instructions or workflows into your AI steps. You can manage them via `factory.skills`:
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
.step('specialized_task', ({ factory, context }) => factory
|
|
469
|
+
.ai('Executing with skills')
|
|
470
|
+
.llm(({ context }) => context.llm.assign({
|
|
471
|
+
// Include all registered skills from PIPELAIN_SKILLS_DIR
|
|
472
|
+
skills: factory.skills.all().provide(),
|
|
473
|
+
|
|
474
|
+
// OR: Include only specific skills by pattern
|
|
475
|
+
// skills: factory.skills.match('coding-*').provide(),
|
|
476
|
+
}))
|
|
477
|
+
.prompt([
|
|
478
|
+
`Perform this task using available skills: ${context.input}`
|
|
479
|
+
])
|
|
480
|
+
)
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## Extensions
|
|
484
|
+
|
|
485
|
+
### Custom LLM Tools
|
|
486
|
+
|
|
487
|
+
You can create custom tools for the AI using `LlmToolCompiler`. This allows the AI to interact with your own services or perform specialized tasks:
|
|
488
|
+
|
|
489
|
+
```ts
|
|
490
|
+
import z from 'zod/v3';
|
|
491
|
+
import { LlmToolCompiler } from '@n1k1t/pipelain';
|
|
492
|
+
|
|
493
|
+
// 1. Define the tool
|
|
494
|
+
const weatherTool = LlmToolCompiler
|
|
495
|
+
.build('Get current weather for a location')
|
|
496
|
+
.input(z.object({
|
|
497
|
+
city: z.string().describe('The city name')
|
|
498
|
+
}))
|
|
499
|
+
.output(z.object({
|
|
500
|
+
temperature: z.number(),
|
|
501
|
+
condition: z.string()
|
|
502
|
+
}))
|
|
503
|
+
.execute(() => async ({ city }) => {
|
|
504
|
+
// Your implementation here
|
|
505
|
+
return { temperature: 22, condition: 'Sunny' };
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// 2. Use it in a step
|
|
509
|
+
pipeline.step('weather_report', ({ factory, context }) => factory
|
|
510
|
+
.ai('Checking weather')
|
|
511
|
+
.llm(({ context }) => context.llm.assign({
|
|
512
|
+
tools: factory.tools
|
|
513
|
+
.web() // Include standard web tools
|
|
514
|
+
.custom({ weather: weatherTool }) // Add your custom tool
|
|
515
|
+
.provide()
|
|
516
|
+
}))
|
|
517
|
+
.prompt([`Check weather in ${context.input}`])
|
|
518
|
+
);
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Custom LLM Skills
|
|
522
|
+
|
|
523
|
+
You can also provide custom skills programmatically:
|
|
524
|
+
|
|
525
|
+
```ts
|
|
526
|
+
pipeline.step('custom_skills', ({ factory, context }) => factory
|
|
527
|
+
.ai('Using custom skills')
|
|
528
|
+
.llm(({ context }) => context.llm.assign({
|
|
529
|
+
skills: factory.skills
|
|
530
|
+
// Provide custom skill objects
|
|
531
|
+
.custom([{
|
|
532
|
+
name: 'manual-skill',
|
|
533
|
+
description: 'Manually defined skill',
|
|
534
|
+
content: 'Manual skill content'
|
|
535
|
+
}])
|
|
536
|
+
.provide()
|
|
537
|
+
}))
|
|
538
|
+
.prompt(['...'])
|
|
539
|
+
);
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Registering Skills from Markdown
|
|
543
|
+
|
|
544
|
+
You can register new skills from raw markdown content (with frontmatter) using `LlmSkillsFactory`:
|
|
545
|
+
|
|
546
|
+
```ts
|
|
547
|
+
import { LlmSkillsFactory } from '@n1k1t/pipelain';
|
|
548
|
+
|
|
549
|
+
const rawSkill = `---
|
|
550
|
+
name: custom-skill
|
|
551
|
+
description: A custom skill description
|
|
552
|
+
---
|
|
553
|
+
Skill content goes here.`;
|
|
554
|
+
|
|
555
|
+
// 1. Create a skills factory (optionally with existing skills)
|
|
556
|
+
const skills = LlmSkillsFactory.build();
|
|
557
|
+
|
|
558
|
+
// 2. Register new skills from raw markdown
|
|
559
|
+
skills.register(rawSkill);
|
|
560
|
+
|
|
561
|
+
// 3. Use in a pipeline step
|
|
562
|
+
pipeline.step('specialized_task', ({ factory, context }) => factory
|
|
563
|
+
.ai('Executing with registered skill')
|
|
564
|
+
.llm(({ context }) => context.llm.assign({
|
|
565
|
+
skills: skills.all().provide(),
|
|
566
|
+
}))
|
|
567
|
+
.prompt(['...'])
|
|
568
|
+
);
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
## License
|
|
574
|
+
|
|
575
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@n1k1t/pipelain",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Powerful utility to build and execute type-safe AI pipelines with structured outputs and tool integration",
|
|
5
|
+
"main": "lib/src/index.js",
|
|
6
|
+
"types": "lib/src/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "NODE_ENV=test jest --silent",
|
|
9
|
+
"test:coverage": "NODE_ENV=test jest --coverage",
|
|
10
|
+
"start:dev:main": "npx ts-node -r ./src/opentelemetry.ts test/index.ts",
|
|
11
|
+
"start:dev:skill": "npx ts-node -r ./src/opentelemetry.ts test/skill.ts",
|
|
12
|
+
"build": "rm -rf lib && npx tsc",
|
|
13
|
+
"build:check": "npx tsc --noEmit",
|
|
14
|
+
"prepare": "husky",
|
|
15
|
+
"preversion": "npm run build:check && npm test",
|
|
16
|
+
"version": "git add -A .",
|
|
17
|
+
"postversion": "npm run build && git push && git push --tags"
|
|
18
|
+
},
|
|
19
|
+
"jest": {
|
|
20
|
+
"preset": "ts-jest",
|
|
21
|
+
"passWithNoTests": true,
|
|
22
|
+
"testEnvironment": "node",
|
|
23
|
+
"testMatch": [
|
|
24
|
+
"<rootDir>/src/**/*.spec.ts"
|
|
25
|
+
],
|
|
26
|
+
"coverageReporters": [
|
|
27
|
+
"cobertura",
|
|
28
|
+
"text"
|
|
29
|
+
],
|
|
30
|
+
"collectCoverageFrom": [
|
|
31
|
+
"<rootDir>/src/**/*.ts"
|
|
32
|
+
],
|
|
33
|
+
"setupFilesAfterEnv": [
|
|
34
|
+
"jest-extended/all"
|
|
35
|
+
],
|
|
36
|
+
"setupFiles": [],
|
|
37
|
+
"transform": {
|
|
38
|
+
".+\\.ts?$": [
|
|
39
|
+
"ts-jest",
|
|
40
|
+
{
|
|
41
|
+
"isolatedModules": true
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=20.16.0"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"ai",
|
|
51
|
+
"pipeline",
|
|
52
|
+
"llm",
|
|
53
|
+
"type-safe",
|
|
54
|
+
"zod",
|
|
55
|
+
"structured-outputs",
|
|
56
|
+
"mcp",
|
|
57
|
+
"agents",
|
|
58
|
+
"automation",
|
|
59
|
+
"typescript"
|
|
60
|
+
],
|
|
61
|
+
"homepage": "https://github.com/n1k1t/pipelain",
|
|
62
|
+
"author": "n1k1t",
|
|
63
|
+
"license": "MIT",
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@langfuse/otel": "4.6.1",
|
|
66
|
+
"@n1k1t/unit-generator": "1.1.3",
|
|
67
|
+
"@opentelemetry/sdk-node": "0.213.0",
|
|
68
|
+
"@types/cheerio": "0.22.35",
|
|
69
|
+
"@types/commander": "2.12.2",
|
|
70
|
+
"@types/jest": "29.5.11",
|
|
71
|
+
"@types/jsdom": "28.0.1",
|
|
72
|
+
"@types/json2md": "1.5.4",
|
|
73
|
+
"@types/lodash": "4.14.184",
|
|
74
|
+
"@types/minimatch": "5.1.2",
|
|
75
|
+
"@types/node": "22.13.14",
|
|
76
|
+
"@types/proper-lockfile": "4.1.4",
|
|
77
|
+
"@types/turndown": "5.0.6",
|
|
78
|
+
"@types/uuid": "8.3.4",
|
|
79
|
+
"husky": "9.1.7",
|
|
80
|
+
"jest": "29.7.0",
|
|
81
|
+
"jest-extended": "4.0.2",
|
|
82
|
+
"ts-jest": "29.1.1",
|
|
83
|
+
"ts-node": "10.9.2",
|
|
84
|
+
"typescript": "5.7.2"
|
|
85
|
+
},
|
|
86
|
+
"dependencies": {
|
|
87
|
+
"@ai-sdk/anthropic": "3.0.48",
|
|
88
|
+
"@ai-sdk/google": "3.0.33",
|
|
89
|
+
"@ai-sdk/mcp": "1.0.43",
|
|
90
|
+
"@ai-sdk/mistral": "3.0.20",
|
|
91
|
+
"@ai-sdk/openai": "3.0.36",
|
|
92
|
+
"@ai-sdk/openai-compatible": "2.0.47",
|
|
93
|
+
"@ast-grep/napi": "0.42.0",
|
|
94
|
+
"ai": "6.0.103",
|
|
95
|
+
"colors": "1.4.0",
|
|
96
|
+
"commander": "9.4.0",
|
|
97
|
+
"dotenv": "16.4.7",
|
|
98
|
+
"fast-glob": "3.3.3",
|
|
99
|
+
"fast-xml-parser": "4.5.0",
|
|
100
|
+
"gray-matter": "4.0.3",
|
|
101
|
+
"json2md": "2.0.3",
|
|
102
|
+
"lodash": "4.17.21",
|
|
103
|
+
"minimatch": "5.1.0",
|
|
104
|
+
"proper-lockfile": "4.1.2",
|
|
105
|
+
"reflect-metadata": "0.2.1",
|
|
106
|
+
"string-argv": "0.3.2",
|
|
107
|
+
"turndown": "7.2.2",
|
|
108
|
+
"uuid": "9.0.0",
|
|
109
|
+
"yaml": "2.8.0",
|
|
110
|
+
"zocker": "3.0.0",
|
|
111
|
+
"zod": "4.3.6"
|
|
112
|
+
}
|
|
113
|
+
}
|