@positronic/template-new-project 0.0.2
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/.claude/settings.local.json +8 -0
- package/CLAUDE.md +215 -0
- package/index.js +180 -0
- package/package.json +18 -0
- package/template/.positronic/package.json +17 -0
- package/template/.positronic/src/index.ts +35 -0
- package/template/.positronic/tsconfig.json +19 -0
- package/template/.positronic/wrangler.jsonc +53 -0
- package/template/CLAUDE.md +120 -0
- package/template/_env +32 -0
- package/template/_gitignore +24 -0
- package/template/brain.ts +59 -0
- package/template/brains/example.ts +13 -0
- package/template/docs/brain-dsl-guide.md +601 -0
- package/template/docs/brain-testing-guide.md +307 -0
- package/template/docs/positronic-guide.md +236 -0
- package/template/docs/tips-for-agents.md +425 -0
- package/template/jest.config.js +24 -0
- package/template/package.json +29 -0
- package/template/positronic.config.json +8 -0
- package/template/resources/example.md +0 -0
- package/template/runner.ts +9 -0
- package/template/tests/example.test.ts +20 -0
- package/template/tsconfig.json +16 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Brain Testing Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to test Positronic brains using the testing utilities provided by `@positronic/core`.
|
|
4
|
+
|
|
5
|
+
## Testing Philosophy
|
|
6
|
+
|
|
7
|
+
Following the principles from [Kent C. Dodds' testing philosophy](https://kentcdodds.com/blog/write-tests):
|
|
8
|
+
- **Write tests. Not too many. Mostly integration.**
|
|
9
|
+
- Test user outcomes, not implementation details
|
|
10
|
+
- Focus on what your brain produces, not how it produces it
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
Testing brains is about verifying they produce the correct outputs given specific AI responses. The testing utilities make it easy to mock AI responses and assert on the final results.
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { createMockClient, runBrainTest } from '@positronic/core';
|
|
20
|
+
import yourBrain from './your-brain.js';
|
|
21
|
+
|
|
22
|
+
describe('your-brain', () => {
|
|
23
|
+
it('should process user data and generate a report', async () => {
|
|
24
|
+
// Arrange: Set up AI responses
|
|
25
|
+
const mockClient = createMockClient();
|
|
26
|
+
mockClient.mockResponses(
|
|
27
|
+
{ analysis: 'User shows high engagement', score: 0.85 },
|
|
28
|
+
{ report: 'Detailed engagement report...', recommendations: ['A', 'B'] }
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Act: Run the brain
|
|
32
|
+
const result = await runBrainTest(yourBrain, { client: mockClient });
|
|
33
|
+
|
|
34
|
+
// Assert: Verify the outcome
|
|
35
|
+
expect(result.completed).toBe(true);
|
|
36
|
+
expect(result.finalState.report).toContain('Detailed engagement report');
|
|
37
|
+
expect(result.finalState.recommendations).toHaveLength(2);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## API Reference
|
|
43
|
+
|
|
44
|
+
### runBrainTest
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const result = await runBrainTest(brain, {
|
|
48
|
+
client: mockClient, // Optional: defaults to createMockClient()
|
|
49
|
+
initialState: { count: 0 }, // Optional: initial state
|
|
50
|
+
resources: resourceLoader, // Optional: resources
|
|
51
|
+
brainOptions: { mode: 'test' } // Optional: brain-specific options
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Returns:**
|
|
56
|
+
- `completed: boolean` - Whether the brain completed successfully
|
|
57
|
+
- `error: Error | null` - Any error that occurred
|
|
58
|
+
- `finalState: State` - The final state after all steps
|
|
59
|
+
- `steps: string[]` - Names of executed steps in order
|
|
60
|
+
|
|
61
|
+
## MockObjectGenerator API
|
|
62
|
+
|
|
63
|
+
### Creating a Mock Client
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const mockClient = createMockClient();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Mocking Responses
|
|
70
|
+
|
|
71
|
+
#### Single Response
|
|
72
|
+
```typescript
|
|
73
|
+
mockClient.mockNextResponse({
|
|
74
|
+
answer: 'The capital of France is Paris',
|
|
75
|
+
confidence: 0.95
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Multiple Responses
|
|
80
|
+
```typescript
|
|
81
|
+
mockClient.mockResponses(
|
|
82
|
+
{ step1: 'completed' },
|
|
83
|
+
{ step2: 'processed' },
|
|
84
|
+
{ finalResult: 'success' }
|
|
85
|
+
);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### Error Responses
|
|
89
|
+
```typescript
|
|
90
|
+
mockClient.mockNextError('API rate limit exceeded');
|
|
91
|
+
// or
|
|
92
|
+
mockClient.mockNextError(new Error('Connection timeout'));
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Assertions
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// Check call count
|
|
99
|
+
mockClient.expectCallCount(3);
|
|
100
|
+
|
|
101
|
+
// Check call parameters
|
|
102
|
+
mockClient.expectCalledWith({
|
|
103
|
+
prompt: 'Generate a summary',
|
|
104
|
+
schemaName: 'summarySchema'
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Get call history
|
|
108
|
+
const calls = mockClient.getCalls();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Testing Patterns
|
|
112
|
+
|
|
113
|
+
### Testing Success Cases
|
|
114
|
+
|
|
115
|
+
Focus on what the brain produces for the user:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
it('should generate personalized recommendations', async () => {
|
|
119
|
+
// Arrange
|
|
120
|
+
mockClient.mockResponses(
|
|
121
|
+
{ preferences: ['tech', 'sports'], confidence: 0.9 },
|
|
122
|
+
{ recommendations: ['Item A', 'Item B', 'Item C'] }
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Act
|
|
126
|
+
const result = await runBrainTest(recommendationBrain, {
|
|
127
|
+
client: mockClient,
|
|
128
|
+
initialState: { userId: '123' }
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Assert outcomes, not implementation
|
|
132
|
+
expect(result.completed).toBe(true);
|
|
133
|
+
expect(result.finalState.recommendations).toHaveLength(3);
|
|
134
|
+
expect(result.finalState.confidence).toBeGreaterThan(0.8);
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Testing Error Cases
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
it('should handle API failures gracefully', async () => {
|
|
142
|
+
// Arrange
|
|
143
|
+
mockClient.mockNextError('Service temporarily unavailable');
|
|
144
|
+
|
|
145
|
+
// Act
|
|
146
|
+
const result = await runBrainTest(processingBrain, { client: mockClient });
|
|
147
|
+
|
|
148
|
+
// Assert
|
|
149
|
+
expect(result.completed).toBe(false);
|
|
150
|
+
expect(result.error?.message).toBe('Service temporarily unavailable');
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Testing State Flow
|
|
155
|
+
|
|
156
|
+
Verify that data flows correctly through your brain:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
it('should use customer data to generate personalized content', async () => {
|
|
160
|
+
// Arrange
|
|
161
|
+
const customerName = 'Alice';
|
|
162
|
+
mockClient.mockResponses(
|
|
163
|
+
{ greeting: 'Hello Alice!', tone: 'friendly' },
|
|
164
|
+
{ email: 'Personalized email content...' }
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Act
|
|
168
|
+
const result = await runBrainTest(emailBrain, {
|
|
169
|
+
client: mockClient,
|
|
170
|
+
initialState: { customerName }
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Assert that the AI used the customer data
|
|
174
|
+
const calls = mockClient.getCalls();
|
|
175
|
+
expect(calls[0].params.prompt).toContain(customerName);
|
|
176
|
+
expect(result.finalState.email).toContain('Personalized email content');
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Best Practices
|
|
181
|
+
|
|
182
|
+
1. **Test Behavior, Not Implementation**
|
|
183
|
+
- ✅ Test what the brain produces
|
|
184
|
+
- ❌ Don't test internal event sequences
|
|
185
|
+
- ❌ Don't test step counts unless it's a user requirement
|
|
186
|
+
|
|
187
|
+
2. **Use Descriptive Test Names**
|
|
188
|
+
- ✅ `it('should generate a summary from user feedback')`
|
|
189
|
+
- ❌ `it('should complete 4 steps and emit events')`
|
|
190
|
+
|
|
191
|
+
3. **Keep Tests Simple**
|
|
192
|
+
- Arrange: Set up mock responses
|
|
193
|
+
- Act: Run the brain
|
|
194
|
+
- Assert: Check the outcome
|
|
195
|
+
|
|
196
|
+
4. **Test Edge Cases That Matter**
|
|
197
|
+
- API errors
|
|
198
|
+
- Empty responses
|
|
199
|
+
- Invalid data that could affect users
|
|
200
|
+
|
|
201
|
+
## What Not to Test
|
|
202
|
+
|
|
203
|
+
Following testing best practices, avoid testing:
|
|
204
|
+
|
|
205
|
+
1. **Implementation Details**
|
|
206
|
+
- Don't check specific event types
|
|
207
|
+
- Don't verify internal state transformations
|
|
208
|
+
- Don't count patch operations
|
|
209
|
+
|
|
210
|
+
2. **Framework Behavior**
|
|
211
|
+
- Trust that the brain framework works
|
|
212
|
+
- Don't test that steps execute in order
|
|
213
|
+
- Don't verify event emission
|
|
214
|
+
|
|
215
|
+
3. **Mock Behavior**
|
|
216
|
+
- Don't test that mocks were called (unless verifying data flow)
|
|
217
|
+
- Focus on what the brain does with the responses
|
|
218
|
+
|
|
219
|
+
## Complete Example
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { createMockClient, runBrainTest } from '@positronic/core';
|
|
223
|
+
import analysisBrain from './analysis-brain.js';
|
|
224
|
+
|
|
225
|
+
describe('analysis-brain', () => {
|
|
226
|
+
let mockClient;
|
|
227
|
+
|
|
228
|
+
beforeEach(() => {
|
|
229
|
+
mockClient = createMockClient();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should analyze customer feedback and generate insights', async () => {
|
|
233
|
+
// Arrange: Set up AI to return analysis
|
|
234
|
+
mockClient.mockNextResponse({
|
|
235
|
+
sentiment: 'positive',
|
|
236
|
+
keywords: ['innovation', 'quality', 'service'],
|
|
237
|
+
summary: 'Customers appreciate product quality and innovation'
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Act: Run analysis on customer feedback
|
|
241
|
+
const result = await runBrainTest(analysisBrain, {
|
|
242
|
+
client: mockClient,
|
|
243
|
+
initialState: {
|
|
244
|
+
feedback: 'Your product is innovative and high quality...'
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Assert: Verify we got actionable insights
|
|
249
|
+
expect(result.completed).toBe(true);
|
|
250
|
+
expect(result.finalState.insights).toEqual({
|
|
251
|
+
sentiment: 'positive',
|
|
252
|
+
topThemes: ['innovation', 'quality', 'service'],
|
|
253
|
+
actionable: true,
|
|
254
|
+
summary: 'Customers appreciate product quality and innovation'
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should handle analysis service outages', async () => {
|
|
259
|
+
// Arrange: Simulate service failure
|
|
260
|
+
mockClient.mockNextError('Analysis service unavailable');
|
|
261
|
+
|
|
262
|
+
// Act: Attempt analysis
|
|
263
|
+
const result = await runBrainTest(analysisBrain, {
|
|
264
|
+
client: mockClient,
|
|
265
|
+
initialState: { feedback: 'Some feedback...' }
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Assert: Verify graceful failure
|
|
269
|
+
expect(result.completed).toBe(false);
|
|
270
|
+
expect(result.error?.message).toBe('Analysis service unavailable');
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Troubleshooting
|
|
276
|
+
|
|
277
|
+
### TypeScript Issues
|
|
278
|
+
|
|
279
|
+
The test utilities are fully typed. If you get type errors:
|
|
280
|
+
```typescript
|
|
281
|
+
// ✅ Type-safe: result.finalState is typed as your brain's state
|
|
282
|
+
const result = await runBrainTest(myBrain, { initialState });
|
|
283
|
+
expect(result.finalState.myProperty).toBe('value');
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Common Issues
|
|
287
|
+
|
|
288
|
+
1. **Mock count mismatch**: Ensure you mock the same number of responses as AI calls in your brain
|
|
289
|
+
2. **State assertions failing**: Check that your brain is actually setting the expected state
|
|
290
|
+
3. **Completion is false**: Your brain might be throwing an error - check `result.error`
|
|
291
|
+
|
|
292
|
+
### Debugging Tips
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// See what prompts are being sent to AI
|
|
296
|
+
const calls = mockClient.getCalls();
|
|
297
|
+
console.log('AI prompts:', calls.map(c => c.params.prompt));
|
|
298
|
+
|
|
299
|
+
// Check execution order
|
|
300
|
+
console.log('Steps executed:', result.steps);
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Next Steps
|
|
304
|
+
|
|
305
|
+
- Review the [Brain DSL Guide](./brain-dsl-guide.md) for more information on brain structure
|
|
306
|
+
- Check example tests in the codebase for real-world testing patterns
|
|
307
|
+
- Remember: focus on testing what matters to users, not how the brain works internally
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Positronic Project Guide
|
|
2
|
+
|
|
3
|
+
This guide covers project-level patterns and best practices for Positronic applications.
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
A typical Positronic project has the following structure:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
├── brain.ts # Project brain wrapper
|
|
11
|
+
├── brains/ # Brain definitions
|
|
12
|
+
├── resources/ # Files accessible to brains
|
|
13
|
+
├── tests/ # Test files
|
|
14
|
+
├── docs/ # Documentation
|
|
15
|
+
├── runner.ts # Local runner for development
|
|
16
|
+
└── positronic.config.json # Project configuration
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## The Project Brain Pattern
|
|
20
|
+
|
|
21
|
+
Every Positronic project includes a `brain.ts` file in the root directory. This file exports a custom `brain` function that wraps the core Positronic brain function.
|
|
22
|
+
|
|
23
|
+
### Why Use a Project Brain?
|
|
24
|
+
|
|
25
|
+
The project brain pattern provides a single place to:
|
|
26
|
+
- Configure services that all brains can access
|
|
27
|
+
- Set up logging, monitoring, or analytics
|
|
28
|
+
- Add authentication or API clients
|
|
29
|
+
- Establish project-wide conventions
|
|
30
|
+
|
|
31
|
+
### Basic Usage
|
|
32
|
+
|
|
33
|
+
All brains in your project should import from `../brain.js` instead of `@positronic/core`:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// ✅ DO THIS (from a file in the brains/ directory)
|
|
37
|
+
import { brain } from '../brain.js';
|
|
38
|
+
|
|
39
|
+
// ❌ NOT THIS
|
|
40
|
+
import { brain } from '@positronic/core';
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Configuring Services
|
|
44
|
+
|
|
45
|
+
To add project-wide services, modify the `brain.ts` file in the root directory:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { brain as coreBrain, type Brain } from '@positronic/core';
|
|
49
|
+
|
|
50
|
+
// Define your services
|
|
51
|
+
interface ProjectServices {
|
|
52
|
+
logger: {
|
|
53
|
+
info: (message: string) => void;
|
|
54
|
+
error: (message: string) => void;
|
|
55
|
+
};
|
|
56
|
+
database: {
|
|
57
|
+
get: (key: string) => Promise<any>;
|
|
58
|
+
set: (key: string, value: any) => Promise<void>;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Export the wrapped brain function
|
|
63
|
+
export function brain<
|
|
64
|
+
TOptions extends object = object,
|
|
65
|
+
TState extends object = object
|
|
66
|
+
>(
|
|
67
|
+
brainConfig: string | { title: string; description?: string }
|
|
68
|
+
): Brain<TOptions, TState, ProjectServices> {
|
|
69
|
+
return coreBrain<TOptions, TState, ProjectServices>(brainConfig)
|
|
70
|
+
.withServices({
|
|
71
|
+
logger: {
|
|
72
|
+
info: (msg) => console.log(`[<%= '${new Date().toISOString()}' %>] INFO: <%= '${msg}' %>`),
|
|
73
|
+
error: (msg) => console.error(`[<%= '${new Date().toISOString()}' %>] ERROR: <%= '${msg}' %>`)
|
|
74
|
+
},
|
|
75
|
+
database: {
|
|
76
|
+
get: async (key) => {
|
|
77
|
+
// Your database implementation
|
|
78
|
+
return localStorage.getItem(key);
|
|
79
|
+
},
|
|
80
|
+
set: async (key, value) => {
|
|
81
|
+
// Your database implementation
|
|
82
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Now all brains automatically have access to these services:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { brain } from '../brain.js';
|
|
93
|
+
|
|
94
|
+
export default brain('User Processor')
|
|
95
|
+
.step('Load User', async ({ logger, database }) => {
|
|
96
|
+
logger.info('Loading user data');
|
|
97
|
+
const userData = await database.get('current-user');
|
|
98
|
+
return { user: userData };
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Resource Organization
|
|
103
|
+
|
|
104
|
+
Resources are files that brains can access during execution. Organize them logically:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
resources/
|
|
108
|
+
├── prompts/ # AI prompt templates
|
|
109
|
+
│ ├── customer-support.md
|
|
110
|
+
│ └── code-review.md
|
|
111
|
+
├── data/ # Static data files
|
|
112
|
+
│ └── config.json
|
|
113
|
+
└── templates/ # Document templates
|
|
114
|
+
└── report.md
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Testing Strategy
|
|
118
|
+
|
|
119
|
+
Keep test files in the `tests/` directory to avoid deployment issues. Tests should:
|
|
120
|
+
- Focus on brain outcomes, not implementation
|
|
121
|
+
- Use mock clients and services
|
|
122
|
+
- Verify the final state and important side effects
|
|
123
|
+
|
|
124
|
+
See `/docs/brain-testing-guide.md` for detailed testing guidance.
|
|
125
|
+
|
|
126
|
+
## Development Workflow
|
|
127
|
+
|
|
128
|
+
1. **Start the development server**: `px server -d`
|
|
129
|
+
2. **Create or modify brains**: Always import from `./brain.js`
|
|
130
|
+
3. **Test locally**: `px brain run <brain-name>`
|
|
131
|
+
4. **Run tests**: `npm test`
|
|
132
|
+
5. **Deploy**: Backend-specific commands (e.g., `px deploy` for Cloudflare)
|
|
133
|
+
|
|
134
|
+
## Configuration
|
|
135
|
+
|
|
136
|
+
The `positronic.config.json` file contains project metadata:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"projectName": "my-project",
|
|
141
|
+
"backend": "cloudflare"
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Environment Variables
|
|
146
|
+
|
|
147
|
+
Use `.env` files for configuration:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# API Keys
|
|
151
|
+
ANTHROPIC_API_KEY=your-key-here
|
|
152
|
+
OPENAI_API_KEY=your-key-here
|
|
153
|
+
|
|
154
|
+
# Backend-specific
|
|
155
|
+
<% if (backend === 'cloudflare') { %>CLOUDFLARE_ACCOUNT_ID=your-account-id
|
|
156
|
+
CLOUDFLARE_API_TOKEN=your-api-token<% } %>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Best Practices
|
|
160
|
+
|
|
161
|
+
1. **Services**: Configure once in `brain.ts`, use everywhere
|
|
162
|
+
2. **Resources**: Use for content that non-developers should be able to edit
|
|
163
|
+
3. **Secrets**: Never commit API keys; use environment variables
|
|
164
|
+
4. **Organization**: Group related brains in folders as your project grows
|
|
165
|
+
5. **Testing**: Write tests for critical workflows
|
|
166
|
+
6. **Documentation**: Keep project-specific docs in the `docs/` folder
|
|
167
|
+
|
|
168
|
+
## Common Patterns
|
|
169
|
+
|
|
170
|
+
### Logging and Monitoring
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// In brain.ts
|
|
174
|
+
interface ProjectServices {
|
|
175
|
+
metrics: {
|
|
176
|
+
track: (event: string, properties?: any) => void;
|
|
177
|
+
time: (label: string) => () => void;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// In your brain
|
|
182
|
+
export default brain('Analytics Brain')
|
|
183
|
+
.step('Start Timer', ({ metrics }) => {
|
|
184
|
+
const endTimer = metrics.time('processing');
|
|
185
|
+
return { endTimer };
|
|
186
|
+
})
|
|
187
|
+
.step('Process', ({ state }) => {
|
|
188
|
+
// Do work...
|
|
189
|
+
return state;
|
|
190
|
+
})
|
|
191
|
+
.step('End Timer', ({ state, metrics }) => {
|
|
192
|
+
state.endTimer();
|
|
193
|
+
metrics.track('processing_complete');
|
|
194
|
+
return state;
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### API Integration
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// In brain.ts
|
|
202
|
+
interface ProjectServices {
|
|
203
|
+
api: {
|
|
204
|
+
get: (path: string) => Promise<any>;
|
|
205
|
+
post: (path: string, data: any) => Promise<any>;
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Configure with base URL and auth
|
|
210
|
+
const api = {
|
|
211
|
+
get: async (path: string) => {
|
|
212
|
+
const response = await fetch(`https://api.example.com<%= '${path}' %>`, {
|
|
213
|
+
headers: { 'Authorization': `Bearer <%= '${process.env.API_KEY}' %>` }
|
|
214
|
+
});
|
|
215
|
+
return response.json();
|
|
216
|
+
},
|
|
217
|
+
post: async (path: string, data: any) => {
|
|
218
|
+
const response = await fetch(`https://api.example.com<%= '${path}' %>`, {
|
|
219
|
+
method: 'POST',
|
|
220
|
+
headers: {
|
|
221
|
+
'Authorization': `Bearer <%= '${process.env.API_KEY}' %>`,
|
|
222
|
+
'Content-Type': 'application/json'
|
|
223
|
+
},
|
|
224
|
+
body: JSON.stringify(data)
|
|
225
|
+
});
|
|
226
|
+
return response.json();
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Getting Help
|
|
232
|
+
|
|
233
|
+
- **Documentation**: https://positronic.dev
|
|
234
|
+
- **CLI Help**: `px --help`
|
|
235
|
+
- **Brain DSL Guide**: `/docs/brain-dsl-guide.md`
|
|
236
|
+
- **Testing Guide**: `/docs/brain-testing-guide.md`
|