@positronic/core 0.0.1 → 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.md +141 -0
- package/dist/src/adapters/types.js +1 -16
- package/dist/src/clients/types.js +4 -1
- package/dist/src/dsl/brain-runner.js +487 -0
- package/dist/src/dsl/brain-runner.test.js +733 -0
- package/dist/src/dsl/brain.js +1128 -0
- package/dist/src/dsl/brain.test.js +4225 -0
- package/dist/src/dsl/constants.js +6 -6
- package/dist/src/dsl/json-patch.js +37 -9
- package/dist/src/index.js +11 -10
- package/dist/src/resources/resources.js +371 -0
- package/dist/src/test-utils.js +474 -0
- package/dist/src/testing.js +3 -0
- package/dist/types/adapters/types.d.ts +3 -8
- package/dist/types/adapters/types.d.ts.map +1 -1
- package/dist/types/clients/types.d.ts +46 -6
- package/dist/types/clients/types.d.ts.map +1 -1
- package/dist/types/dsl/brain-runner.d.ts +24 -0
- package/dist/types/dsl/brain-runner.d.ts.map +1 -0
- package/dist/types/dsl/brain.d.ts +136 -0
- package/dist/types/dsl/brain.d.ts.map +1 -0
- package/dist/types/dsl/constants.d.ts +5 -5
- package/dist/types/dsl/constants.d.ts.map +1 -1
- package/dist/types/dsl/json-patch.d.ts +2 -1
- package/dist/types/dsl/json-patch.d.ts.map +1 -1
- package/dist/types/index.d.ts +13 -11
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/resources/resource-loader.d.ts +6 -0
- package/dist/types/resources/resource-loader.d.ts.map +1 -0
- package/dist/types/resources/resources.d.ts +23 -0
- package/dist/types/resources/resources.d.ts.map +1 -0
- package/dist/types/test-utils.d.ts +94 -0
- package/dist/types/test-utils.d.ts.map +1 -0
- package/dist/types/testing.d.ts +2 -0
- package/dist/types/testing.d.ts.map +1 -0
- package/docs/core-testing-guide.md +289 -0
- package/package.json +26 -7
- package/src/adapters/types.ts +3 -22
- package/src/clients/types.ts +50 -10
- package/src/dsl/brain-runner.test.ts +384 -0
- package/src/dsl/brain-runner.ts +111 -0
- package/src/dsl/brain.test.ts +1981 -0
- package/src/dsl/brain.ts +740 -0
- package/src/dsl/constants.ts +6 -6
- package/src/dsl/json-patch.ts +24 -9
- package/src/dsl/types.ts +1 -1
- package/src/index.ts +30 -16
- package/src/resources/resource-loader.ts +8 -0
- package/src/resources/resources.ts +267 -0
- package/src/test-utils.ts +254 -0
- package/test/resources.test.ts +248 -0
- package/tsconfig.json +2 -2
- package/.swcrc +0 -31
- package/dist/src/dsl/extensions.js +0 -19
- package/dist/src/dsl/workflow-runner.js +0 -93
- package/dist/src/dsl/workflow.js +0 -308
- package/dist/src/file-stores/local-file-store.js +0 -12
- package/dist/src/utils/temp-files.js +0 -27
- package/dist/types/dsl/extensions.d.ts +0 -18
- package/dist/types/dsl/extensions.d.ts.map +0 -1
- package/dist/types/dsl/workflow-runner.d.ts +0 -28
- package/dist/types/dsl/workflow-runner.d.ts.map +0 -1
- package/dist/types/dsl/workflow.d.ts +0 -118
- package/dist/types/dsl/workflow.d.ts.map +0 -1
- package/dist/types/file-stores/local-file-store.d.ts +0 -7
- package/dist/types/file-stores/local-file-store.d.ts.map +0 -1
- package/dist/types/file-stores/types.d.ts +0 -4
- package/dist/types/file-stores/types.d.ts.map +0 -1
- package/dist/types/utils/temp-files.d.ts +0 -12
- package/dist/types/utils/temp-files.d.ts.map +0 -1
- package/src/dsl/extensions.ts +0 -58
- package/src/dsl/workflow-runner.test.ts +0 -203
- package/src/dsl/workflow-runner.ts +0 -146
- package/src/dsl/workflow.test.ts +0 -1435
- package/src/dsl/workflow.ts +0 -554
- package/src/file-stores/local-file-store.ts +0 -11
- package/src/file-stores/types.ts +0 -3
- package/src/utils/temp-files.ts +0 -46
- /package/dist/src/{file-stores/types.js → resources/resource-loader.js} +0 -0
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
2
|
+
try {
|
|
3
|
+
var info = gen[key](arg);
|
|
4
|
+
var value = info.value;
|
|
5
|
+
} catch (error) {
|
|
6
|
+
reject(error);
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (info.done) {
|
|
10
|
+
resolve(value);
|
|
11
|
+
} else {
|
|
12
|
+
Promise.resolve(value).then(_next, _throw);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function _async_to_generator(fn) {
|
|
16
|
+
return function() {
|
|
17
|
+
var self = this, args = arguments;
|
|
18
|
+
return new Promise(function(resolve, reject) {
|
|
19
|
+
var gen = fn.apply(self, args);
|
|
20
|
+
function _next(value) {
|
|
21
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
22
|
+
}
|
|
23
|
+
function _throw(err) {
|
|
24
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
25
|
+
}
|
|
26
|
+
_next(undefined);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function _define_property(obj, key, value) {
|
|
31
|
+
if (key in obj) {
|
|
32
|
+
Object.defineProperty(obj, key, {
|
|
33
|
+
value: value,
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: true
|
|
37
|
+
});
|
|
38
|
+
} else {
|
|
39
|
+
obj[key] = value;
|
|
40
|
+
}
|
|
41
|
+
return obj;
|
|
42
|
+
}
|
|
43
|
+
function _object_spread(target) {
|
|
44
|
+
for(var i = 1; i < arguments.length; i++){
|
|
45
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
46
|
+
var ownKeys = Object.keys(source);
|
|
47
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
48
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
49
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
ownKeys.forEach(function(key) {
|
|
53
|
+
_define_property(target, key, source[key]);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return target;
|
|
57
|
+
}
|
|
58
|
+
function ownKeys(object, enumerableOnly) {
|
|
59
|
+
var keys = Object.keys(object);
|
|
60
|
+
if (Object.getOwnPropertySymbols) {
|
|
61
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
62
|
+
if (enumerableOnly) {
|
|
63
|
+
symbols = symbols.filter(function(sym) {
|
|
64
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
keys.push.apply(keys, symbols);
|
|
68
|
+
}
|
|
69
|
+
return keys;
|
|
70
|
+
}
|
|
71
|
+
function _object_spread_props(target, source) {
|
|
72
|
+
source = source != null ? source : {};
|
|
73
|
+
if (Object.getOwnPropertyDescriptors) {
|
|
74
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
75
|
+
} else {
|
|
76
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
77
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return target;
|
|
81
|
+
}
|
|
82
|
+
function _ts_generator(thisArg, body) {
|
|
83
|
+
var f, y, t, _ = {
|
|
84
|
+
label: 0,
|
|
85
|
+
sent: function() {
|
|
86
|
+
if (t[0] & 1) throw t[1];
|
|
87
|
+
return t[1];
|
|
88
|
+
},
|
|
89
|
+
trys: [],
|
|
90
|
+
ops: []
|
|
91
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
92
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
|
|
93
|
+
return this;
|
|
94
|
+
}), g;
|
|
95
|
+
function verb(n) {
|
|
96
|
+
return function(v) {
|
|
97
|
+
return step([
|
|
98
|
+
n,
|
|
99
|
+
v
|
|
100
|
+
]);
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function step(op) {
|
|
104
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
105
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
106
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
107
|
+
if (y = 0, t) op = [
|
|
108
|
+
op[0] & 2,
|
|
109
|
+
t.value
|
|
110
|
+
];
|
|
111
|
+
switch(op[0]){
|
|
112
|
+
case 0:
|
|
113
|
+
case 1:
|
|
114
|
+
t = op;
|
|
115
|
+
break;
|
|
116
|
+
case 4:
|
|
117
|
+
_.label++;
|
|
118
|
+
return {
|
|
119
|
+
value: op[1],
|
|
120
|
+
done: false
|
|
121
|
+
};
|
|
122
|
+
case 5:
|
|
123
|
+
_.label++;
|
|
124
|
+
y = op[1];
|
|
125
|
+
op = [
|
|
126
|
+
0
|
|
127
|
+
];
|
|
128
|
+
continue;
|
|
129
|
+
case 7:
|
|
130
|
+
op = _.ops.pop();
|
|
131
|
+
_.trys.pop();
|
|
132
|
+
continue;
|
|
133
|
+
default:
|
|
134
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
135
|
+
_ = 0;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
139
|
+
_.label = op[1];
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
143
|
+
_.label = t[1];
|
|
144
|
+
t = op;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
if (t && _.label < t[2]) {
|
|
148
|
+
_.label = t[2];
|
|
149
|
+
_.ops.push(op);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
if (t[2]) _.ops.pop();
|
|
153
|
+
_.trys.pop();
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
op = body.call(thisArg, _);
|
|
157
|
+
} catch (e) {
|
|
158
|
+
op = [
|
|
159
|
+
6,
|
|
160
|
+
e
|
|
161
|
+
];
|
|
162
|
+
y = 0;
|
|
163
|
+
} finally{
|
|
164
|
+
f = t = 0;
|
|
165
|
+
}
|
|
166
|
+
if (op[0] & 5) throw op[1];
|
|
167
|
+
return {
|
|
168
|
+
value: op[0] ? op[1] : void 0,
|
|
169
|
+
done: true
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
import { BrainRunner } from './brain-runner.js';
|
|
174
|
+
import { brain } from './brain.js';
|
|
175
|
+
import { BRAIN_EVENTS, STATUS } from './constants.js';
|
|
176
|
+
import { jest, describe, it, expect, beforeEach } from '@jest/globals';
|
|
177
|
+
import { createResources } from '../resources/resources.js';
|
|
178
|
+
import { z } from 'zod';
|
|
179
|
+
describe('BrainRunner', function() {
|
|
180
|
+
var mockGenerateObject = jest.fn();
|
|
181
|
+
var mockClient = {
|
|
182
|
+
generateObject: mockGenerateObject
|
|
183
|
+
};
|
|
184
|
+
var mockDispatch = jest.fn();
|
|
185
|
+
var mockAdapter = {
|
|
186
|
+
dispatch: mockDispatch
|
|
187
|
+
};
|
|
188
|
+
beforeEach(function() {
|
|
189
|
+
jest.clearAllMocks();
|
|
190
|
+
});
|
|
191
|
+
it('should run a brain and dispatch events to adapters', function() {
|
|
192
|
+
return _async_to_generator(function() {
|
|
193
|
+
var runner, testBrain, stepCompletions;
|
|
194
|
+
return _ts_generator(this, function(_state) {
|
|
195
|
+
switch(_state.label){
|
|
196
|
+
case 0:
|
|
197
|
+
runner = new BrainRunner({
|
|
198
|
+
adapters: [
|
|
199
|
+
mockAdapter
|
|
200
|
+
],
|
|
201
|
+
client: mockClient
|
|
202
|
+
});
|
|
203
|
+
testBrain = brain('Test Brain').step('First Step', function() {
|
|
204
|
+
return {
|
|
205
|
+
value: 42
|
|
206
|
+
};
|
|
207
|
+
}).step('Async Step', function(param) {
|
|
208
|
+
var state = param.state;
|
|
209
|
+
return _async_to_generator(function() {
|
|
210
|
+
return _ts_generator(this, function(_state) {
|
|
211
|
+
switch(_state.label){
|
|
212
|
+
case 0:
|
|
213
|
+
return [
|
|
214
|
+
4,
|
|
215
|
+
new Promise(function(resolve) {
|
|
216
|
+
return setTimeout(resolve, 10);
|
|
217
|
+
})
|
|
218
|
+
];
|
|
219
|
+
case 1:
|
|
220
|
+
_state.sent();
|
|
221
|
+
return [
|
|
222
|
+
2,
|
|
223
|
+
_object_spread_props(_object_spread({}, state), {
|
|
224
|
+
asyncValue: 'completed'
|
|
225
|
+
})
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
})();
|
|
230
|
+
}).step('Final Step', function(param) {
|
|
231
|
+
var state = param.state;
|
|
232
|
+
return _object_spread_props(_object_spread({}, state), {
|
|
233
|
+
finalValue: state.value * 2
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
return [
|
|
237
|
+
4,
|
|
238
|
+
runner.run(testBrain)
|
|
239
|
+
];
|
|
240
|
+
case 1:
|
|
241
|
+
_state.sent();
|
|
242
|
+
// Verify adapter received all events in correct order
|
|
243
|
+
expect(mockAdapter.dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
244
|
+
type: BRAIN_EVENTS.START,
|
|
245
|
+
brainTitle: 'Test Brain'
|
|
246
|
+
}));
|
|
247
|
+
expect(mockAdapter.dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
248
|
+
type: BRAIN_EVENTS.STEP_COMPLETE,
|
|
249
|
+
stepTitle: 'First Step'
|
|
250
|
+
}));
|
|
251
|
+
expect(mockAdapter.dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
252
|
+
type: BRAIN_EVENTS.STEP_COMPLETE,
|
|
253
|
+
stepTitle: 'Async Step'
|
|
254
|
+
}));
|
|
255
|
+
expect(mockAdapter.dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
256
|
+
type: BRAIN_EVENTS.STEP_COMPLETE,
|
|
257
|
+
stepTitle: 'Final Step'
|
|
258
|
+
}));
|
|
259
|
+
expect(mockAdapter.dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
260
|
+
type: BRAIN_EVENTS.COMPLETE,
|
|
261
|
+
status: STATUS.COMPLETE
|
|
262
|
+
}));
|
|
263
|
+
// Verify the order of events
|
|
264
|
+
stepCompletions = mockAdapter.dispatch.mock.calls.filter(function(call) {
|
|
265
|
+
return call[0].type === BRAIN_EVENTS.STEP_COMPLETE;
|
|
266
|
+
}).map(function(call) {
|
|
267
|
+
return call[0].stepTitle;
|
|
268
|
+
});
|
|
269
|
+
expect(stepCompletions).toEqual([
|
|
270
|
+
'First Step',
|
|
271
|
+
'Async Step',
|
|
272
|
+
'Final Step'
|
|
273
|
+
]);
|
|
274
|
+
return [
|
|
275
|
+
2
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
})();
|
|
280
|
+
});
|
|
281
|
+
it('should handle brain errors', function() {
|
|
282
|
+
return _async_to_generator(function() {
|
|
283
|
+
var runner, errorBrain, error;
|
|
284
|
+
return _ts_generator(this, function(_state) {
|
|
285
|
+
switch(_state.label){
|
|
286
|
+
case 0:
|
|
287
|
+
runner = new BrainRunner({
|
|
288
|
+
adapters: [
|
|
289
|
+
mockAdapter
|
|
290
|
+
],
|
|
291
|
+
client: mockClient
|
|
292
|
+
});
|
|
293
|
+
errorBrain = brain('Error Brain').step('Error Step', function() {
|
|
294
|
+
throw new Error('Test error');
|
|
295
|
+
});
|
|
296
|
+
_state.label = 1;
|
|
297
|
+
case 1:
|
|
298
|
+
_state.trys.push([
|
|
299
|
+
1,
|
|
300
|
+
3,
|
|
301
|
+
,
|
|
302
|
+
4
|
|
303
|
+
]);
|
|
304
|
+
return [
|
|
305
|
+
4,
|
|
306
|
+
runner.run(errorBrain)
|
|
307
|
+
];
|
|
308
|
+
case 2:
|
|
309
|
+
_state.sent();
|
|
310
|
+
return [
|
|
311
|
+
3,
|
|
312
|
+
4
|
|
313
|
+
];
|
|
314
|
+
case 3:
|
|
315
|
+
error = _state.sent();
|
|
316
|
+
return [
|
|
317
|
+
3,
|
|
318
|
+
4
|
|
319
|
+
];
|
|
320
|
+
case 4:
|
|
321
|
+
// Verify error event was dispatched
|
|
322
|
+
expect(mockAdapter.dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
323
|
+
type: BRAIN_EVENTS.ERROR,
|
|
324
|
+
error: expect.objectContaining({
|
|
325
|
+
message: 'Test error'
|
|
326
|
+
})
|
|
327
|
+
}));
|
|
328
|
+
return [
|
|
329
|
+
2
|
|
330
|
+
];
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
})();
|
|
334
|
+
});
|
|
335
|
+
it('should maintain state between steps', function() {
|
|
336
|
+
return _async_to_generator(function() {
|
|
337
|
+
var runner, testBrain, result;
|
|
338
|
+
return _ts_generator(this, function(_state) {
|
|
339
|
+
switch(_state.label){
|
|
340
|
+
case 0:
|
|
341
|
+
runner = new BrainRunner({
|
|
342
|
+
adapters: [],
|
|
343
|
+
client: mockClient
|
|
344
|
+
});
|
|
345
|
+
testBrain = brain('Test Brain').step('First Step', function() {
|
|
346
|
+
return {
|
|
347
|
+
count: 1
|
|
348
|
+
};
|
|
349
|
+
}).step('Second Step', function(param) {
|
|
350
|
+
var state = param.state;
|
|
351
|
+
return {
|
|
352
|
+
count: state.count + 1
|
|
353
|
+
};
|
|
354
|
+
});
|
|
355
|
+
return [
|
|
356
|
+
4,
|
|
357
|
+
runner.run(testBrain)
|
|
358
|
+
];
|
|
359
|
+
case 1:
|
|
360
|
+
result = _state.sent();
|
|
361
|
+
expect(result.count).toEqual(2);
|
|
362
|
+
return [
|
|
363
|
+
2
|
|
364
|
+
];
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
})();
|
|
368
|
+
});
|
|
369
|
+
it('should pass resources to step actions', function() {
|
|
370
|
+
return _async_to_generator(function() {
|
|
371
|
+
var mockLoad, mockResourceLoader, testManifest, testResources, runner, textContent, binaryContent, resourceConsumingBrain;
|
|
372
|
+
return _ts_generator(this, function(_state) {
|
|
373
|
+
switch(_state.label){
|
|
374
|
+
case 0:
|
|
375
|
+
mockLoad = jest.fn(function(resourceName, type) {
|
|
376
|
+
return _async_to_generator(function() {
|
|
377
|
+
return _ts_generator(this, function(_state) {
|
|
378
|
+
if (type === 'binary') {
|
|
379
|
+
return [
|
|
380
|
+
2,
|
|
381
|
+
Buffer.from("content of ".concat(resourceName))
|
|
382
|
+
];
|
|
383
|
+
}
|
|
384
|
+
return [
|
|
385
|
+
2,
|
|
386
|
+
"content of ".concat(resourceName)
|
|
387
|
+
];
|
|
388
|
+
});
|
|
389
|
+
})();
|
|
390
|
+
});
|
|
391
|
+
mockResourceLoader = {
|
|
392
|
+
load: mockLoad
|
|
393
|
+
};
|
|
394
|
+
testManifest = {
|
|
395
|
+
myTextFile: {
|
|
396
|
+
type: 'text',
|
|
397
|
+
key: 'myTextFile',
|
|
398
|
+
path: '/test/myTextFile.txt'
|
|
399
|
+
},
|
|
400
|
+
myBinaryFile: {
|
|
401
|
+
type: 'binary',
|
|
402
|
+
key: 'myBinaryFile',
|
|
403
|
+
path: '/test/myBinaryFile.bin'
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
testResources = createResources(mockResourceLoader, testManifest);
|
|
407
|
+
runner = new BrainRunner({
|
|
408
|
+
adapters: [],
|
|
409
|
+
client: mockClient
|
|
410
|
+
}).withResources(testResources);
|
|
411
|
+
resourceConsumingBrain = brain('Resource Brain').step('Load Resources', function(param) {
|
|
412
|
+
var resources = param.resources;
|
|
413
|
+
return _async_to_generator(function() {
|
|
414
|
+
return _ts_generator(this, function(_state) {
|
|
415
|
+
switch(_state.label){
|
|
416
|
+
case 0:
|
|
417
|
+
return [
|
|
418
|
+
4,
|
|
419
|
+
resources.myTextFile.loadText()
|
|
420
|
+
];
|
|
421
|
+
case 1:
|
|
422
|
+
textContent = _state.sent();
|
|
423
|
+
return [
|
|
424
|
+
4,
|
|
425
|
+
resources.myBinaryFile.loadBinary()
|
|
426
|
+
];
|
|
427
|
+
case 2:
|
|
428
|
+
binaryContent = _state.sent();
|
|
429
|
+
return [
|
|
430
|
+
2,
|
|
431
|
+
{}
|
|
432
|
+
];
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
})();
|
|
436
|
+
});
|
|
437
|
+
return [
|
|
438
|
+
4,
|
|
439
|
+
runner.run(resourceConsumingBrain)
|
|
440
|
+
];
|
|
441
|
+
case 1:
|
|
442
|
+
_state.sent();
|
|
443
|
+
expect(mockLoad).toHaveBeenCalledWith('myTextFile', 'text');
|
|
444
|
+
expect(mockLoad).toHaveBeenCalledWith('myBinaryFile', 'binary');
|
|
445
|
+
expect(textContent).toBe('content of myTextFile');
|
|
446
|
+
expect(binaryContent === null || binaryContent === void 0 ? void 0 : binaryContent.toString()).toBe('content of myBinaryFile');
|
|
447
|
+
return [
|
|
448
|
+
2
|
|
449
|
+
];
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
})();
|
|
453
|
+
});
|
|
454
|
+
it('should chain adapters with withAdapters method', function() {
|
|
455
|
+
return _async_to_generator(function() {
|
|
456
|
+
var mockAdapter2, mockAdapter3, runner, updatedRunner, testBrain;
|
|
457
|
+
return _ts_generator(this, function(_state) {
|
|
458
|
+
switch(_state.label){
|
|
459
|
+
case 0:
|
|
460
|
+
mockAdapter2 = {
|
|
461
|
+
dispatch: jest.fn()
|
|
462
|
+
};
|
|
463
|
+
mockAdapter3 = {
|
|
464
|
+
dispatch: jest.fn()
|
|
465
|
+
};
|
|
466
|
+
runner = new BrainRunner({
|
|
467
|
+
adapters: [
|
|
468
|
+
mockAdapter
|
|
469
|
+
],
|
|
470
|
+
client: mockClient
|
|
471
|
+
});
|
|
472
|
+
// Chain additional adapters
|
|
473
|
+
updatedRunner = runner.withAdapters([
|
|
474
|
+
mockAdapter2,
|
|
475
|
+
mockAdapter3
|
|
476
|
+
]);
|
|
477
|
+
testBrain = brain('Test Brain').step('Step 1', function() {
|
|
478
|
+
return {
|
|
479
|
+
value: 1
|
|
480
|
+
};
|
|
481
|
+
});
|
|
482
|
+
return [
|
|
483
|
+
4,
|
|
484
|
+
updatedRunner.run(testBrain)
|
|
485
|
+
];
|
|
486
|
+
case 1:
|
|
487
|
+
_state.sent();
|
|
488
|
+
// Verify all adapters received events
|
|
489
|
+
expect(mockAdapter.dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
490
|
+
type: BRAIN_EVENTS.START
|
|
491
|
+
}));
|
|
492
|
+
expect(mockAdapter2.dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
493
|
+
type: BRAIN_EVENTS.START
|
|
494
|
+
}));
|
|
495
|
+
expect(mockAdapter3.dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
496
|
+
type: BRAIN_EVENTS.START
|
|
497
|
+
}));
|
|
498
|
+
// Verify all adapters received the same number of events
|
|
499
|
+
expect(mockAdapter.dispatch).toHaveBeenCalledTimes(mockAdapter2.dispatch.mock.calls.length);
|
|
500
|
+
expect(mockAdapter2.dispatch).toHaveBeenCalledTimes(mockAdapter3.dispatch.mock.calls.length);
|
|
501
|
+
return [
|
|
502
|
+
2
|
|
503
|
+
];
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
})();
|
|
507
|
+
});
|
|
508
|
+
it('should replace client with withClient method', function() {
|
|
509
|
+
return _async_to_generator(function() {
|
|
510
|
+
var originalClient, newClient, runner, updatedRunner, testSchema, testBrain, result;
|
|
511
|
+
return _ts_generator(this, function(_state) {
|
|
512
|
+
switch(_state.label){
|
|
513
|
+
case 0:
|
|
514
|
+
originalClient = {
|
|
515
|
+
generateObject: jest.fn()
|
|
516
|
+
};
|
|
517
|
+
newClient = {
|
|
518
|
+
generateObject: jest.fn()
|
|
519
|
+
};
|
|
520
|
+
// Configure the new client's response
|
|
521
|
+
newClient.generateObject.mockResolvedValue({
|
|
522
|
+
result: 'from new client'
|
|
523
|
+
});
|
|
524
|
+
runner = new BrainRunner({
|
|
525
|
+
adapters: [],
|
|
526
|
+
client: originalClient
|
|
527
|
+
});
|
|
528
|
+
// Replace the client
|
|
529
|
+
updatedRunner = runner.withClient(newClient);
|
|
530
|
+
// Define schema once to ensure same reference
|
|
531
|
+
testSchema = z.object({
|
|
532
|
+
result: z.string()
|
|
533
|
+
});
|
|
534
|
+
testBrain = brain('Test Brain').step('Generate', function(param) {
|
|
535
|
+
var client = param.client;
|
|
536
|
+
return _async_to_generator(function() {
|
|
537
|
+
var response;
|
|
538
|
+
return _ts_generator(this, function(_state) {
|
|
539
|
+
switch(_state.label){
|
|
540
|
+
case 0:
|
|
541
|
+
return [
|
|
542
|
+
4,
|
|
543
|
+
client.generateObject({
|
|
544
|
+
prompt: 'test prompt',
|
|
545
|
+
schema: testSchema,
|
|
546
|
+
schemaName: 'TestSchema'
|
|
547
|
+
})
|
|
548
|
+
];
|
|
549
|
+
case 1:
|
|
550
|
+
response = _state.sent();
|
|
551
|
+
return [
|
|
552
|
+
2,
|
|
553
|
+
{
|
|
554
|
+
generated: response.result
|
|
555
|
+
}
|
|
556
|
+
];
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
})();
|
|
560
|
+
});
|
|
561
|
+
return [
|
|
562
|
+
4,
|
|
563
|
+
updatedRunner.run(testBrain)
|
|
564
|
+
];
|
|
565
|
+
case 1:
|
|
566
|
+
result = _state.sent();
|
|
567
|
+
// Verify new client was used, not the original
|
|
568
|
+
expect(originalClient.generateObject).not.toHaveBeenCalled();
|
|
569
|
+
expect(newClient.generateObject).toHaveBeenCalledWith({
|
|
570
|
+
prompt: 'test prompt',
|
|
571
|
+
schema: testSchema,
|
|
572
|
+
schemaName: 'TestSchema'
|
|
573
|
+
});
|
|
574
|
+
expect(result.generated).toBe('from new client');
|
|
575
|
+
return [
|
|
576
|
+
2
|
|
577
|
+
];
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
})();
|
|
581
|
+
});
|
|
582
|
+
it('should apply patches from initialCompletedSteps and continue from correct state', function() {
|
|
583
|
+
return _async_to_generator(function() {
|
|
584
|
+
var runner, completedSteps, testBrain, result, stepCompleteEvents;
|
|
585
|
+
return _ts_generator(this, function(_state) {
|
|
586
|
+
switch(_state.label){
|
|
587
|
+
case 0:
|
|
588
|
+
runner = new BrainRunner({
|
|
589
|
+
adapters: [
|
|
590
|
+
mockAdapter
|
|
591
|
+
],
|
|
592
|
+
client: mockClient
|
|
593
|
+
});
|
|
594
|
+
// Simulate completed steps with patches
|
|
595
|
+
completedSteps = [
|
|
596
|
+
{
|
|
597
|
+
id: 'step-1',
|
|
598
|
+
title: 'First Step',
|
|
599
|
+
status: STATUS.COMPLETE,
|
|
600
|
+
patch: [
|
|
601
|
+
{
|
|
602
|
+
op: 'add',
|
|
603
|
+
path: '/count',
|
|
604
|
+
value: 10
|
|
605
|
+
}
|
|
606
|
+
]
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
id: 'step-2',
|
|
610
|
+
title: 'Second Step',
|
|
611
|
+
status: STATUS.COMPLETE,
|
|
612
|
+
patch: [
|
|
613
|
+
{
|
|
614
|
+
op: 'add',
|
|
615
|
+
path: '/name',
|
|
616
|
+
value: 'test'
|
|
617
|
+
}
|
|
618
|
+
]
|
|
619
|
+
}
|
|
620
|
+
];
|
|
621
|
+
testBrain = brain('Test Brain').step('First Step', function() {
|
|
622
|
+
return {
|
|
623
|
+
count: 10
|
|
624
|
+
};
|
|
625
|
+
}).step('Second Step', function(param) {
|
|
626
|
+
var state = param.state;
|
|
627
|
+
return _object_spread_props(_object_spread({}, state), {
|
|
628
|
+
name: 'test'
|
|
629
|
+
});
|
|
630
|
+
}).step('Third Step', function(param) {
|
|
631
|
+
var state = param.state;
|
|
632
|
+
return _object_spread_props(_object_spread({}, state), {
|
|
633
|
+
count: state.count + 5,
|
|
634
|
+
message: "".concat(state.name, " completed")
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
return [
|
|
638
|
+
4,
|
|
639
|
+
runner.run(testBrain, {
|
|
640
|
+
initialCompletedSteps: completedSteps,
|
|
641
|
+
brainRunId: 'test-run-123'
|
|
642
|
+
})
|
|
643
|
+
];
|
|
644
|
+
case 1:
|
|
645
|
+
result = _state.sent();
|
|
646
|
+
// Verify the final state includes patches from completed steps
|
|
647
|
+
expect(result).toEqual({
|
|
648
|
+
count: 15,
|
|
649
|
+
name: 'test',
|
|
650
|
+
message: 'test completed'
|
|
651
|
+
});
|
|
652
|
+
// Verify that the brain runner applied the patches correctly
|
|
653
|
+
// The runner should have seen all steps execute, but the first two were already completed
|
|
654
|
+
stepCompleteEvents = mockAdapter.dispatch.mock.calls.filter(function(call) {
|
|
655
|
+
return call[0].type === BRAIN_EVENTS.STEP_COMPLETE;
|
|
656
|
+
});
|
|
657
|
+
// All steps will emit complete events in the current implementation
|
|
658
|
+
expect(stepCompleteEvents.length).toBeGreaterThanOrEqual(1);
|
|
659
|
+
return [
|
|
660
|
+
2
|
|
661
|
+
];
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
})();
|
|
665
|
+
});
|
|
666
|
+
it('should stop execution after specified number of steps with endAfter parameter', function() {
|
|
667
|
+
return _async_to_generator(function() {
|
|
668
|
+
var runner, testBrain, result, stepCompleteEvents, completeEvents;
|
|
669
|
+
return _ts_generator(this, function(_state) {
|
|
670
|
+
switch(_state.label){
|
|
671
|
+
case 0:
|
|
672
|
+
runner = new BrainRunner({
|
|
673
|
+
adapters: [
|
|
674
|
+
mockAdapter
|
|
675
|
+
],
|
|
676
|
+
client: mockClient
|
|
677
|
+
});
|
|
678
|
+
testBrain = brain('Test Brain').step('Step 1', function() {
|
|
679
|
+
return {
|
|
680
|
+
step1: 'done'
|
|
681
|
+
};
|
|
682
|
+
}).step('Step 2', function(param) {
|
|
683
|
+
var state = param.state;
|
|
684
|
+
return _object_spread_props(_object_spread({}, state), {
|
|
685
|
+
step2: 'done'
|
|
686
|
+
});
|
|
687
|
+
}).step('Step 3', function(param) {
|
|
688
|
+
var state = param.state;
|
|
689
|
+
return _object_spread_props(_object_spread({}, state), {
|
|
690
|
+
step3: 'done'
|
|
691
|
+
});
|
|
692
|
+
}).step('Step 4', function(param) {
|
|
693
|
+
var state = param.state;
|
|
694
|
+
return _object_spread_props(_object_spread({}, state), {
|
|
695
|
+
step4: 'done'
|
|
696
|
+
});
|
|
697
|
+
});
|
|
698
|
+
return [
|
|
699
|
+
4,
|
|
700
|
+
runner.run(testBrain, {
|
|
701
|
+
endAfter: 2
|
|
702
|
+
})
|
|
703
|
+
];
|
|
704
|
+
case 1:
|
|
705
|
+
result = _state.sent();
|
|
706
|
+
// Verify state only has results from first 2 steps
|
|
707
|
+
expect(result).toEqual({
|
|
708
|
+
step1: 'done',
|
|
709
|
+
step2: 'done'
|
|
710
|
+
});
|
|
711
|
+
// Verify only 2 step complete events were dispatched
|
|
712
|
+
stepCompleteEvents = mockAdapter.dispatch.mock.calls.filter(function(call) {
|
|
713
|
+
return call[0].type === BRAIN_EVENTS.STEP_COMPLETE;
|
|
714
|
+
}).map(function(call) {
|
|
715
|
+
return call[0].stepTitle;
|
|
716
|
+
});
|
|
717
|
+
expect(stepCompleteEvents).toEqual([
|
|
718
|
+
'Step 1',
|
|
719
|
+
'Step 2'
|
|
720
|
+
]);
|
|
721
|
+
// Verify that COMPLETE event was NOT dispatched (brain didn't finish)
|
|
722
|
+
completeEvents = mockAdapter.dispatch.mock.calls.filter(function(call) {
|
|
723
|
+
return call[0].type === BRAIN_EVENTS.COMPLETE;
|
|
724
|
+
});
|
|
725
|
+
expect(completeEvents.length).toBe(0);
|
|
726
|
+
return [
|
|
727
|
+
2
|
|
728
|
+
];
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
})();
|
|
732
|
+
});
|
|
733
|
+
});
|