ocpipe 0.2.1 → 0.3.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.
package/README.md CHANGED
@@ -1,60 +1,59 @@
1
- <div align="center">
2
- <h3>OpenCode Pipeline</h3>
3
- <p>SDK for LLM pipelines with <a href="https://opencode.ai">OpenCode</a> and <a href="https://zod.dev">Zod</a>.</p>
4
- </div>
1
+ <p align="center"><strong>ocpipe</strong></p>
2
+ <p align="center">Build LLM pipelines with <a href="https://github.com/sst/opencode">OpenCode</a> and <a href="https://zod.dev">Zod</a>.</p>
3
+ <p align="center">Inspired by <a href="https://github.com/stanfordnlp/dspy">DSPy</a>.</p>
4
+ <p align="center">
5
+ <a href="https://www.npmjs.com/package/ocpipe"><img alt="npm" src="https://img.shields.io/npm/v/ocpipe?style=flat-square" /></a>
6
+ <a href="https://github.com/s4wave/ocpipe/actions"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/s4wave/ocpipe/tests.yml?style=flat-square&branch=master" /></a>
7
+ </p>
5
8
 
6
- <div align="center">
9
+ ---
7
10
 
8
- ```
9
- Signature → Predict → Module → Pipeline
10
- │ │ │ │
11
- what execute compose orchestrate
12
- ```
11
+ - **Type-safe** Define inputs and outputs with Zod schemas
12
+ - **Modular** Compose modules into complex pipelines
13
+ - **Checkpoints** Resume from any step
14
+ - **Multi-model** Works with 75+ providers through OpenCode
15
+ - **Auto-correction** Fixes schema mismatches automatically
13
16
 
14
- </div>
17
+ ### Quick Start
18
+
19
+ ```bash
20
+ bun add ocpipe
21
+ ```
15
22
 
16
23
  ```typescript
17
24
  import { signature, field, module, Pipeline, createBaseState } from 'ocpipe'
18
25
 
19
- // Define a signature
20
26
  const Greet = signature({
21
27
  doc: 'Generate a friendly greeting for the given name.',
22
- inputs: {
23
- name: field.string('The name of the person to greet'),
24
- },
25
- outputs: {
26
- greeting: field.string('A friendly greeting message'),
27
- emoji: field.string('An appropriate emoji for the greeting'),
28
- },
28
+ inputs: { name: field.string('The name of the person to greet') },
29
+ outputs: { greeting: field.string('A friendly greeting message') },
29
30
  })
30
31
 
31
- // Run in a pipeline
32
- const pipeline = new Pipeline({
33
- name: 'hello-world',
34
- defaultModel: { providerID: 'anthropic', modelID: 'claude-haiku-4-5' },
35
- defaultAgent: 'code',
36
- checkpointDir: './ckpt',
37
- logDir: './logs',
38
- }, createBaseState)
32
+ const pipeline = new Pipeline(
33
+ {
34
+ name: 'hello-world',
35
+ defaultModel: { providerID: 'anthropic', modelID: 'claude-haiku-4-5' },
36
+ defaultAgent: 'code',
37
+ },
38
+ createBaseState,
39
+ )
39
40
 
40
41
  const result = await pipeline.run(module(Greet), { name: 'World' })
41
- console.log(result.data.greeting) // "Hello, World! It's wonderful to meet you!"
42
+ console.log(result.data.greeting)
42
43
  ```
43
44
 
44
- ## Install
45
-
46
- ```bash
47
- bun add ocpipe zod
48
- ```
45
+ OpenCode CLI is bundled — run `bun run opencode` or use your system `opencode` if installed.
49
46
 
50
- Requires [Bun](https://bun.sh) and [OpenCode](https://opencode.ai) CLI.
51
-
52
- ## Documentation
47
+ ### Documentation
53
48
 
54
49
  - [Getting Started](./GETTING_STARTED.md) - Tutorial with examples
55
50
  - [Design](./DESIGN.md) - Architecture and concepts
56
51
  - [Contributing](./CONTRIBUTING.md) - Development setup
57
52
 
58
- ## License
53
+ <!-- This code has been tested on animals. They didn't understand it either. -->
54
+
55
+ ---
56
+
57
+ [Discord](https://discord.gg/opencode) · [OpenCode](https://github.com/sst/opencode)
59
58
 
60
- [MIT](./LICENSE)
59
+ <sub>An [Aperture Robotics](https://github.com/aperturerobotics) project.</sub>
@@ -0,0 +1,27 @@
1
+ {
2
+ "sessionId": "20251227_044217",
3
+ "startedAt": "2025-12-27T04:42:17.756Z",
4
+ "phase": "init",
5
+ "steps": [
6
+ {
7
+ "stepName": "Greeter",
8
+ "timestamp": "2025-12-27T04:42:22.145Z",
9
+ "result": {
10
+ "data": {
11
+ "greeting": "Hello, World! Welcome!",
12
+ "emoji": "👋"
13
+ },
14
+ "stepName": "Greeter",
15
+ "duration": 4389,
16
+ "sessionId": "ses_4a1e2aaceffedog5Q2374azn79",
17
+ "model": {
18
+ "providerID": "anthropic",
19
+ "modelID": "claude-haiku-4-5"
20
+ },
21
+ "attempt": 1
22
+ }
23
+ }
24
+ ],
25
+ "subPipelines": [],
26
+ "opencodeSessionId": "ses_4a1e2aaceffedog5Q2374azn79"
27
+ }
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Auto-correction example.
3
3
  *
4
- * Demonstrates DSTS's automatic schema correction.
4
+ * Demonstrates ocpipe's automatic schema correction.
5
5
  * This example uses a schema with specific field names that LLMs
6
6
  * sometimes get wrong (e.g., "type" instead of "issue_type").
7
7
  *
8
- * DSTS supports two correction methods:
8
+ * ocpipe supports two correction methods:
9
9
  * - 'json-patch' (default): RFC 6902 JSON Patch, no external dependencies
10
10
  * - 'jq': jq-style expressions, requires jq binary installed
11
11
  *
@@ -15,7 +15,13 @@
15
15
  */
16
16
 
17
17
  import { z } from 'zod/v4'
18
- import { Pipeline, createBaseState, signature, field, SignatureModule } from '../src/index.js'
18
+ import {
19
+ Pipeline,
20
+ createBaseState,
21
+ signature,
22
+ field,
23
+ SignatureModule,
24
+ } from '../src/index.js'
19
25
  import type { CorrectionMethod, ExecutionContext } from '../src/types.js'
20
26
 
21
27
  // A signature with field names that LLMs often get wrong
@@ -28,9 +34,15 @@ IMPORTANT: Use the EXACT field names specified in the schema.`,
28
34
  },
29
35
  outputs: {
30
36
  // LLMs often return "type" instead of "issue_type"
31
- issue_type: field.enum(['bug', 'feature', 'refactor', 'docs'] as const, 'Category of the issue'),
37
+ issue_type: field.enum(
38
+ ['bug', 'feature', 'refactor', 'docs'] as const,
39
+ 'Category of the issue',
40
+ ),
32
41
  // LLMs often return "priority" instead of "severity"
33
- severity: field.enum(['low', 'medium', 'high', 'critical'] as const, 'How severe is the issue'),
42
+ severity: field.enum(
43
+ ['low', 'medium', 'high', 'critical'] as const,
44
+ 'How severe is the issue',
45
+ ),
34
46
  // LLMs often return "description" or "reason" instead of "explanation"
35
47
  explanation: field.string('Detailed explanation of the issue'),
36
48
  // LLMs often return just "tags" or "labels"
@@ -53,7 +65,8 @@ class IssueAnalyzer extends SignatureModule<typeof AnalyzeIssue> {
53
65
 
54
66
  async function main() {
55
67
  // Check for --jq flag
56
- const method: CorrectionMethod = process.argv.includes('--jq') ? 'jq' : 'json-patch'
68
+ const method: CorrectionMethod =
69
+ process.argv.includes('--jq') ? 'jq' : 'json-patch'
57
70
 
58
71
  const pipeline = new Pipeline(
59
72
  {
@@ -72,7 +85,8 @@ async function main() {
72
85
  console.log('Watch the correction rounds fix schema mismatches.\n')
73
86
 
74
87
  const result = await pipeline.run(new IssueAnalyzer(method), {
75
- description: 'The login button does not respond when clicked on mobile devices',
88
+ description:
89
+ 'The login button does not respond when clicked on mobile devices',
76
90
  })
77
91
 
78
92
  console.log('\n=== Final Result ===')
package/example/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Hello World example runner.
3
3
  *
4
- * Demonstrates running a DSTS module in a pipeline.
4
+ * Demonstrates running an ocpipe module in a pipeline.
5
5
  */
6
6
 
7
7
  import { Pipeline, createBaseState } from '../src/index.js'
package/llms.txt ADDED
@@ -0,0 +1,200 @@
1
+ # ocpipe
2
+
3
+ > ocpipe is a TypeScript library for building type-safe LLM pipelines with OpenCode and Zod. Inspired by DSPy, it provides modular, checkpointable workflows with automatic schema correction.
4
+
5
+ Important notes:
6
+
7
+ - Requires Bun runtime and OpenCode CLI installed and configured
8
+ - Uses Zod v4 for schema validation
9
+ - Unlike DSPy, does NOT provide ChainOfThought or ReAct - OpenCode agents already handle reasoning and tool access
10
+ - Session continuity is maintained by default across pipeline steps
11
+
12
+ ## Core Concepts
13
+
14
+ ocpipe separates concerns into three layers:
15
+ - **Signatures**: Declare input/output contracts (the "what")
16
+ - **Modules**: Compose predictors with execution logic (the "how")
17
+ - **Pipelines**: Orchestrate execution with checkpointing and retries (the "when")
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ bun add ocpipe zod
23
+ ```
24
+
25
+ ```typescript
26
+ import { signature, field, module, Pipeline, createBaseState } from 'ocpipe'
27
+
28
+ const Greet = signature({
29
+ doc: 'Generate a friendly greeting for the given name.',
30
+ inputs: { name: field.string('The name of the person to greet') },
31
+ outputs: { greeting: field.string('A friendly greeting message') },
32
+ })
33
+
34
+ const pipeline = new Pipeline(
35
+ {
36
+ name: 'hello-world',
37
+ defaultModel: { providerID: 'anthropic', modelID: 'claude-haiku-4-5' },
38
+ defaultAgent: 'code',
39
+ checkpointDir: './ckpt',
40
+ logDir: './logs',
41
+ },
42
+ createBaseState,
43
+ )
44
+
45
+ const result = await pipeline.run(module(Greet), { name: 'World' })
46
+ console.log(result.data.greeting)
47
+ ```
48
+
49
+ ## Field Helpers
50
+
51
+ - `field.string(desc?)` - String field
52
+ - `field.number(desc?)` - Number field
53
+ - `field.boolean(desc?)` - Boolean field
54
+ - `field.array(itemType, desc?)` - Array with Zod item type
55
+ - `field.object(shape, desc?)` - Object with Zod shape
56
+ - `field.enum(values, desc?)` - Enum from const array
57
+ - `field.optional(field)` - Optional wrapper
58
+ - `field.nullable(field)` - Nullable wrapper
59
+ - `field.custom(zodType, desc?)` - Custom Zod type
60
+
61
+ ## Docs
62
+
63
+ - [Getting Started](https://github.com/s4wave/ocpipe/blob/master/GETTING_STARTED.md): Tutorial with examples for building hello world and extending with multiple modules
64
+ - [Design](https://github.com/s4wave/ocpipe/blob/master/DESIGN.md): Architecture, core concepts, and advanced patterns
65
+ - [README](https://github.com/s4wave/ocpipe/blob/master/README.md): Quick start and feature overview
66
+
67
+ ## Source Files
68
+
69
+ - [src/index.ts](https://github.com/s4wave/ocpipe/blob/master/src/index.ts): Main exports - signature, field, module, Pipeline, Predict, state helpers, parsing utilities
70
+ - [src/signature.ts](https://github.com/s4wave/ocpipe/blob/master/src/signature.ts): Signature definition and field helpers using Zod
71
+ - [src/module.ts](https://github.com/s4wave/ocpipe/blob/master/src/module.ts): Module, SignatureModule, and simple module() factory
72
+ - [src/pipeline.ts](https://github.com/s4wave/ocpipe/blob/master/src/pipeline.ts): Pipeline orchestrator with checkpointing, retries, and sub-pipelines
73
+ - [src/predict.ts](https://github.com/s4wave/ocpipe/blob/master/src/predict.ts): Predict class - prompt generation, LLM execution, response parsing, auto-correction
74
+ - [src/types.ts](https://github.com/s4wave/ocpipe/blob/master/src/types.ts): TypeScript interfaces - ModelConfig, ExecutionContext, SignatureDef, PipelineConfig, etc.
75
+ - [src/agent.ts](https://github.com/s4wave/ocpipe/blob/master/src/agent.ts): OpenCode CLI integration - runAgent(), session management
76
+ - [src/parsing.ts](https://github.com/s4wave/ocpipe/blob/master/src/parsing.ts): JSON extraction, Zod validation, JSON Patch and jq-style correction
77
+ - [src/state.ts](https://github.com/s4wave/ocpipe/blob/master/src/state.ts): State management - createBaseState(), createSessionId(), extendBaseState()
78
+ - [src/testing.ts](https://github.com/s4wave/ocpipe/blob/master/src/testing.ts): MockAgentBackend, createMockContext(), generateMockOutputs() for unit testing
79
+
80
+ ## Examples
81
+
82
+ - [example/index.ts](https://github.com/s4wave/ocpipe/blob/master/example/index.ts): Hello world pipeline runner
83
+ - [example/signature.ts](https://github.com/s4wave/ocpipe/blob/master/example/signature.ts): Greet signature definition
84
+ - [example/module.ts](https://github.com/s4wave/ocpipe/blob/master/example/module.ts): Greeter SignatureModule implementation
85
+ - [example/correction.ts](https://github.com/s4wave/ocpipe/blob/master/example/correction.ts): Auto-correction demonstration
86
+
87
+ ## Key Types
88
+
89
+ ```typescript
90
+ interface PipelineConfig {
91
+ name: string
92
+ defaultModel: { providerID: string; modelID: string }
93
+ defaultAgent: string
94
+ checkpointDir: string
95
+ logDir: string
96
+ retry?: { maxAttempts: number; onParseError?: boolean }
97
+ timeoutSec?: number
98
+ }
99
+
100
+ interface SignatureDef<I, O> {
101
+ doc: string // Instructions for the LLM
102
+ inputs: I // Record<string, FieldConfig>
103
+ outputs: O // Record<string, FieldConfig>
104
+ }
105
+
106
+ interface FieldConfig<T extends z.ZodType = z.ZodType> {
107
+ type: T // Zod type for validation
108
+ desc?: string // Description for prompt generation
109
+ }
110
+ ```
111
+
112
+ ## Patterns
113
+
114
+ ### Simple Module (one signature)
115
+ ```typescript
116
+ const result = await pipeline.run(module(MySignature), inputs)
117
+ ```
118
+
119
+ ### Custom Module Class
120
+ ```typescript
121
+ class MyModule extends SignatureModule<typeof MySignature> {
122
+ constructor() { super(MySignature) }
123
+ async forward(input, ctx) {
124
+ const result = await this.predictor.execute(input, ctx)
125
+ return result.data
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Multi-Predictor Module
131
+ ```typescript
132
+ class ComplexModule extends Module<InputType, OutputType> {
133
+ private step1 = this.predict(Signature1)
134
+ private step2 = this.predict(Signature2, { agent: 'specialist' })
135
+
136
+ async forward(input, ctx) {
137
+ const r1 = await this.step1.execute(input, ctx)
138
+ const r2 = await this.step2.execute({ data: r1.data }, ctx)
139
+ return { result: r2.data.output, metadata: r1.data }
140
+ }
141
+ }
142
+ ```
143
+
144
+ ### Session Control
145
+ ```typescript
146
+ // New session for this step
147
+ await pipeline.run(module, input, { newSession: true })
148
+
149
+ // Model override for this step
150
+ await pipeline.run(module, input, {
151
+ model: { providerID: 'anthropic', modelID: 'claude-opus-4-5' }
152
+ })
153
+ ```
154
+
155
+ ### Checkpointing
156
+ ```typescript
157
+ // Resume from checkpoint
158
+ const resumed = await Pipeline.loadCheckpoint(config, sessionId)
159
+
160
+ // List available checkpoints
161
+ const files = await Pipeline.listCheckpoints(config)
162
+ ```
163
+
164
+ ### Auto-Correction Configuration
165
+ ```typescript
166
+ // Default: json-patch with 3 rounds
167
+ super(MySignature)
168
+
169
+ // Custom configuration
170
+ super(MySignature, {
171
+ correction: {
172
+ method: 'json-patch', // or 'jq' (requires jq binary)
173
+ maxFields: 5,
174
+ maxRounds: 3,
175
+ }
176
+ })
177
+
178
+ // Disable correction
179
+ super(MySignature, { correction: false })
180
+ ```
181
+
182
+ ### Testing Without LLM
183
+ ```typescript
184
+ import { MockAgentBackend, createMockContext } from 'ocpipe'
185
+ import { vi } from 'vitest'
186
+
187
+ const mock = new MockAgentBackend()
188
+ mock.addJsonResponse({ greeting: 'Hello!', emoji: ':wave:' })
189
+
190
+ vi.mock('./agent.js', () => ({ runAgent: mock.createRunner() }))
191
+
192
+ const ctx = createMockContext()
193
+ const result = await predictor.execute({ name: 'Test' }, ctx)
194
+ ```
195
+
196
+ ## Optional
197
+
198
+ - [CONTRIBUTING.md](https://github.com/s4wave/ocpipe/blob/master/CONTRIBUTING.md): Development setup and contribution guidelines
199
+ - [src/integration.test.ts](https://github.com/s4wave/ocpipe/blob/master/src/integration.test.ts): Integration tests showing real usage patterns
200
+ - [src/parsing.test.ts](https://github.com/s4wave/ocpipe/blob/master/src/parsing.test.ts): Tests for JSON parsing and correction logic
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ocpipe",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "SDK for LLM pipelines with OpenCode and Zod",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -28,15 +28,26 @@
28
28
  "engines": {
29
29
  "bun": ">=1.0.0"
30
30
  },
31
+ "dependencies": {
32
+ "opencode-ai": "latest"
33
+ },
31
34
  "peerDependencies": {
32
35
  "zod": "^4.0.0"
33
36
  },
34
37
  "devDependencies": {
38
+ "@eslint/js": "^9.39.2",
35
39
  "bun-types": "^1.3.5",
40
+ "eslint": "^9.39.2",
41
+ "globals": "^16.5.0",
42
+ "jiti": "^2.6.1",
43
+ "prettier": "^3.7.4",
36
44
  "typescript": "^5.0.0",
45
+ "typescript-eslint": "^8.50.1",
37
46
  "vitest": "^4.0.0"
38
47
  },
39
48
  "scripts": {
49
+ "lint": "eslint .",
50
+ "format": "bun run prettier --write .",
40
51
  "typecheck": "tsc --noEmit",
41
52
  "test": "vitest run",
42
53
  "test:watch": "vitest",
@@ -50,6 +61,11 @@
50
61
  "files": [
51
62
  "src/",
52
63
  "!src/*.test.ts",
53
- "example/"
64
+ "example/",
65
+ "llms.txt",
66
+ "README.md",
67
+ "LICENSE",
68
+ "GETTING_STARTED.md",
69
+ "DESIGN.md"
54
70
  ]
55
71
  }
package/src/agent.ts CHANGED
@@ -1,14 +1,33 @@
1
1
  /**
2
- * DSTS SDK OpenCode agent integration.
2
+ * ocpipe OpenCode agent integration.
3
3
  *
4
4
  * Wraps the OpenCode CLI for running LLM agents with session management.
5
5
  */
6
6
 
7
- import { spawn } from 'child_process'
7
+ import { spawn, execSync } from 'child_process'
8
8
  import { mkdir } from 'fs/promises'
9
9
  import { PROJECT_ROOT, TMP_DIR } from './paths.js'
10
10
  import type { RunAgentOptions, RunAgentResult } from './types.js'
11
11
 
12
+ /** Check if opencode is available in system PATH */
13
+ function hasSystemOpencode(): boolean {
14
+ try {
15
+ execSync('which opencode', { stdio: 'ignore' })
16
+ return true
17
+ } catch {
18
+ return false
19
+ }
20
+ }
21
+
22
+ /** Get command and args to invoke opencode */
23
+ function getOpencodeCommand(args: string[]): { cmd: string; args: string[] } {
24
+ if (hasSystemOpencode()) {
25
+ return { cmd: 'opencode', args }
26
+ }
27
+ // Fallback to bunx with ocpipe package (which has opencode-ai as dependency)
28
+ return { cmd: 'bunx', args: ['-p', 'ocpipe', 'opencode', ...args] }
29
+ }
30
+
12
31
  /** runAgent executes an OpenCode agent with a prompt, streaming output in real-time. */
13
32
  export async function runAgent(
14
33
  options: RunAgentOptions,
@@ -23,14 +42,23 @@ export async function runAgent(
23
42
  `\n>>> OpenCode [${agent}] [${modelStr}] ${sessionInfo}: ${promptPreview}...`,
24
43
  )
25
44
 
26
- const args = ['run', '--format', 'default', '--agent', agent, '--model', modelStr]
45
+ const args = [
46
+ 'run',
47
+ '--format',
48
+ 'default',
49
+ '--agent',
50
+ agent,
51
+ '--model',
52
+ modelStr,
53
+ ]
27
54
 
28
55
  if (sessionId) {
29
56
  args.push('--session', sessionId)
30
57
  }
31
58
 
32
59
  return new Promise((resolve, reject) => {
33
- const proc = spawn('opencode', args, {
60
+ const opencodeCmd = getOpencodeCommand(args)
61
+ const proc = spawn(opencodeCmd.cmd, opencodeCmd.args, {
34
62
  cwd: PROJECT_ROOT,
35
63
  stdio: ['pipe', 'pipe', 'pipe'],
36
64
  })
@@ -116,23 +144,20 @@ async function exportSession(sessionId: string): Promise<string | null> {
116
144
 
117
145
  try {
118
146
  await mkdir(TMP_DIR, { recursive: true })
119
- const proc = Bun.spawn(
120
- [
121
- 'opencode',
122
- 'session',
123
- 'export',
124
- sessionId,
125
- '--format',
126
- 'json',
127
- '-o',
128
- tmpPath,
129
- ],
130
- {
131
- cwd: PROJECT_ROOT,
132
- stdout: 'pipe',
133
- stderr: 'pipe',
134
- },
135
- )
147
+ const opencodeCmd = getOpencodeCommand([
148
+ 'session',
149
+ 'export',
150
+ sessionId,
151
+ '--format',
152
+ 'json',
153
+ '-o',
154
+ tmpPath,
155
+ ])
156
+ const proc = Bun.spawn([opencodeCmd.cmd, ...opencodeCmd.args], {
157
+ cwd: PROJECT_ROOT,
158
+ stdout: 'pipe',
159
+ stderr: 'pipe',
160
+ })
136
161
 
137
162
  await proc.exited
138
163
 
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * DSTS: Declarative Self-Improving TypeScript
2
+ * ocpipe: LLM pipelines with OpenCode and Zod.
3
3
  *
4
- * A DSPy-inspired SDK for building LLM workflow pipelines with OpenCode.
4
+ * Inspired by DSPy.
5
5
  *
6
6
  * @example
7
7
  * ```typescript
package/src/module.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  /**
2
- * DSTS SDK Module base class.
2
+ * ocpipe Module base class.
3
3
  *
4
4
  * Modules encapsulate logical units of work that use one or more Predictors.
5
5
  */
6
6
 
7
7
  import type {
8
8
  ExecutionContext,
9
+ FieldConfig,
9
10
  InferInputs,
10
11
  InferOutputs,
11
12
  SignatureDef,
@@ -13,17 +14,22 @@ import type {
13
14
  export type { ExecutionContext } from './types.js'
14
15
  import { Predict, type PredictConfig } from './predict.js'
15
16
 
17
+ type AnySignature = SignatureDef<
18
+ Record<string, FieldConfig>,
19
+ Record<string, FieldConfig>
20
+ >
21
+
16
22
  /** Module is the abstract base class for composable workflow units. */
17
23
  export abstract class Module<I, O> {
18
- private predictors: Predict<any>[] = []
24
+ private predictors: Predict<AnySignature>[] = []
19
25
 
20
26
  /** predict creates and registers a Predict instance for a signature. */
21
- protected predict<S extends SignatureDef<any, any>>(
27
+ protected predict<S extends AnySignature>(
22
28
  sig: S,
23
29
  config?: PredictConfig,
24
30
  ): Predict<S> {
25
31
  const p = new Predict(sig, config)
26
- this.predictors.push(p)
32
+ this.predictors.push(p as Predict<AnySignature>)
27
33
  return p
28
34
  }
29
35
 
@@ -31,15 +37,16 @@ export abstract class Module<I, O> {
31
37
  abstract forward(input: I, ctx: ExecutionContext): Promise<O>
32
38
 
33
39
  /** getPredictors returns all registered predictors (for future optimization). */
34
- getPredictors(): Predict<any>[] {
40
+ getPredictors(): Predict<AnySignature>[] {
35
41
  return this.predictors
36
42
  }
37
43
  }
38
44
 
39
45
  /** SignatureModule is a Module whose types are derived from a Signature. */
40
- export abstract class SignatureModule<
41
- S extends SignatureDef<any, any>,
42
- > extends Module<InferInputs<S>, InferOutputs<S>> {
46
+ export abstract class SignatureModule<S extends AnySignature> extends Module<
47
+ InferInputs<S>,
48
+ InferOutputs<S>
49
+ > {
43
50
  protected readonly sig: S
44
51
  protected readonly predictor: Predict<S>
45
52
 
@@ -51,18 +58,21 @@ export abstract class SignatureModule<
51
58
  }
52
59
 
53
60
  /** SimpleModule is a SignatureModule that just executes the predictor. */
54
- class SimpleModule<S extends SignatureDef<any, any>> extends SignatureModule<S> {
61
+ class SimpleModule<S extends AnySignature> extends SignatureModule<S> {
55
62
  constructor(sig: S, config?: PredictConfig) {
56
63
  super(sig, config)
57
64
  }
58
65
 
59
- async forward(input: InferInputs<S>, ctx: ExecutionContext): Promise<InferOutputs<S>> {
66
+ async forward(
67
+ input: InferInputs<S>,
68
+ ctx: ExecutionContext,
69
+ ): Promise<InferOutputs<S>> {
60
70
  return (await this.predictor.execute(input, ctx)).data
61
71
  }
62
72
  }
63
73
 
64
74
  /** module creates a simple Module from a Signature (syntactic sugar). */
65
- export function module<S extends SignatureDef<any, any>>(
75
+ export function module<S extends AnySignature>(
66
76
  sig: S,
67
77
  config?: PredictConfig,
68
78
  ): Module<InferInputs<S>, InferOutputs<S>> {