@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,136 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ObjectGenerator } from '../clients/types.js';
|
|
3
|
+
import type { State, JsonPatch } from './types.js';
|
|
4
|
+
import { STATUS, BRAIN_EVENTS } from './constants.js';
|
|
5
|
+
import type { Resources } from '../resources/resources.js';
|
|
6
|
+
export type SerializedError = {
|
|
7
|
+
name: string;
|
|
8
|
+
message: string;
|
|
9
|
+
stack?: string;
|
|
10
|
+
};
|
|
11
|
+
interface BaseEvent<TOptions extends object = object> {
|
|
12
|
+
type: (typeof BRAIN_EVENTS)[keyof typeof BRAIN_EVENTS];
|
|
13
|
+
options: TOptions;
|
|
14
|
+
brainRunId: string;
|
|
15
|
+
}
|
|
16
|
+
interface BrainBaseEvent<TOptions extends object = object> extends BaseEvent<TOptions> {
|
|
17
|
+
brainTitle: string;
|
|
18
|
+
brainDescription?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface BrainStartEvent<TOptions extends object = object> extends BrainBaseEvent<TOptions> {
|
|
21
|
+
type: typeof BRAIN_EVENTS.START | typeof BRAIN_EVENTS.RESTART;
|
|
22
|
+
initialState: State;
|
|
23
|
+
status: typeof STATUS.RUNNING;
|
|
24
|
+
}
|
|
25
|
+
export interface BrainCompleteEvent<TOptions extends object = object> extends BrainBaseEvent<TOptions> {
|
|
26
|
+
type: typeof BRAIN_EVENTS.COMPLETE;
|
|
27
|
+
status: typeof STATUS.COMPLETE;
|
|
28
|
+
}
|
|
29
|
+
export interface BrainErrorEvent<TOptions extends object = object> extends BrainBaseEvent<TOptions> {
|
|
30
|
+
type: typeof BRAIN_EVENTS.ERROR;
|
|
31
|
+
status: typeof STATUS.ERROR;
|
|
32
|
+
error: SerializedError;
|
|
33
|
+
}
|
|
34
|
+
export interface StepStatusEvent<TOptions extends object = object> extends BaseEvent<TOptions> {
|
|
35
|
+
type: typeof BRAIN_EVENTS.STEP_STATUS;
|
|
36
|
+
steps: SerializedStepStatus[];
|
|
37
|
+
}
|
|
38
|
+
export interface StepStartedEvent<TOptions extends object = object> extends BaseEvent<TOptions> {
|
|
39
|
+
type: typeof BRAIN_EVENTS.STEP_START;
|
|
40
|
+
status: typeof STATUS.RUNNING;
|
|
41
|
+
stepTitle: string;
|
|
42
|
+
stepId: string;
|
|
43
|
+
}
|
|
44
|
+
export interface StepCompletedEvent<TOptions extends object = object> extends BaseEvent<TOptions> {
|
|
45
|
+
type: typeof BRAIN_EVENTS.STEP_COMPLETE;
|
|
46
|
+
status: typeof STATUS.RUNNING;
|
|
47
|
+
stepTitle: string;
|
|
48
|
+
stepId: string;
|
|
49
|
+
patch: JsonPatch;
|
|
50
|
+
}
|
|
51
|
+
export type BrainEvent<TOptions extends object = object> = BrainStartEvent<TOptions> | BrainCompleteEvent<TOptions> | BrainErrorEvent<TOptions> | StepStatusEvent<TOptions> | StepStartedEvent<TOptions> | StepCompletedEvent<TOptions>;
|
|
52
|
+
export interface SerializedStep {
|
|
53
|
+
title: string;
|
|
54
|
+
status: (typeof STATUS)[keyof typeof STATUS];
|
|
55
|
+
id: string;
|
|
56
|
+
patch?: JsonPatch;
|
|
57
|
+
}
|
|
58
|
+
export type SerializedStepStatus = Omit<SerializedStep, 'patch'>;
|
|
59
|
+
export interface BrainStructure {
|
|
60
|
+
title: string;
|
|
61
|
+
description?: string;
|
|
62
|
+
steps: Array<{
|
|
63
|
+
type: 'step' | 'brain';
|
|
64
|
+
title: string;
|
|
65
|
+
innerBrain?: BrainStructure;
|
|
66
|
+
}>;
|
|
67
|
+
}
|
|
68
|
+
export interface BrainFactory {
|
|
69
|
+
<TOptions extends object = object, TState extends State = object, TServices extends object = object>(brainConfig: string | {
|
|
70
|
+
title: string;
|
|
71
|
+
description?: string;
|
|
72
|
+
}): Brain<TOptions, TState, TServices>;
|
|
73
|
+
}
|
|
74
|
+
interface BaseRunParams<TOptions extends object = object> {
|
|
75
|
+
client: ObjectGenerator;
|
|
76
|
+
resources?: Resources;
|
|
77
|
+
options?: TOptions;
|
|
78
|
+
}
|
|
79
|
+
export interface InitialRunParams<TOptions extends object = object> extends BaseRunParams<TOptions> {
|
|
80
|
+
initialState?: State;
|
|
81
|
+
initialCompletedSteps?: never;
|
|
82
|
+
brainRunId?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface RerunParams<TOptions extends object = object> extends BaseRunParams<TOptions> {
|
|
85
|
+
initialState: State;
|
|
86
|
+
initialCompletedSteps: SerializedStep[];
|
|
87
|
+
brainRunId: string;
|
|
88
|
+
}
|
|
89
|
+
export declare class Brain<TOptions extends object = object, TState extends State = object, TServices extends object = object> {
|
|
90
|
+
readonly title: string;
|
|
91
|
+
private description?;
|
|
92
|
+
private blocks;
|
|
93
|
+
type: 'brain';
|
|
94
|
+
private defaultOptions;
|
|
95
|
+
private services;
|
|
96
|
+
constructor(title: string, description?: string | undefined);
|
|
97
|
+
get structure(): BrainStructure;
|
|
98
|
+
withOptions(options: Partial<TOptions>): this;
|
|
99
|
+
withServices<TNewServices extends object>(services: TNewServices): Brain<TOptions, TState, TNewServices>;
|
|
100
|
+
step<TNewState extends State>(title: string, action: (params: {
|
|
101
|
+
state: TState;
|
|
102
|
+
options: TOptions;
|
|
103
|
+
client: ObjectGenerator;
|
|
104
|
+
resources: Resources;
|
|
105
|
+
} & TServices) => TNewState | Promise<TNewState>): Brain<TOptions, TNewState, TServices>;
|
|
106
|
+
brain<TInnerState extends State, TNewState extends State>(title: string, innerBrain: Brain<TOptions, TInnerState, TServices>, action: (params: {
|
|
107
|
+
state: TState;
|
|
108
|
+
brainState: TInnerState;
|
|
109
|
+
services: TServices;
|
|
110
|
+
}) => TNewState, initialState?: State | ((state: TState) => State)): Brain<TOptions, TNewState, TServices>;
|
|
111
|
+
prompt<TResponseKey extends string & {
|
|
112
|
+
readonly brand?: unique symbol;
|
|
113
|
+
}, TSchema extends z.ZodObject<any>, TNewState extends State = TState & {
|
|
114
|
+
[K in TResponseKey]: z.infer<TSchema>;
|
|
115
|
+
}>(title: string, config: {
|
|
116
|
+
template: (state: TState, resources: Resources) => string | Promise<string>;
|
|
117
|
+
outputSchema: {
|
|
118
|
+
schema: TSchema;
|
|
119
|
+
name: TResponseKey & (string extends TResponseKey ? never : unknown);
|
|
120
|
+
};
|
|
121
|
+
client?: ObjectGenerator;
|
|
122
|
+
}, reduce?: (params: {
|
|
123
|
+
state: TState;
|
|
124
|
+
response: z.infer<TSchema>;
|
|
125
|
+
options: TOptions;
|
|
126
|
+
prompt: string;
|
|
127
|
+
resources: Resources;
|
|
128
|
+
} & TServices) => TNewState | Promise<TNewState>): Brain<TOptions, TNewState, TServices>;
|
|
129
|
+
run(params: InitialRunParams<TOptions>): AsyncGenerator<BrainEvent<TOptions>>;
|
|
130
|
+
run(params: RerunParams<TOptions>): AsyncGenerator<BrainEvent<TOptions>>;
|
|
131
|
+
private withBlocks;
|
|
132
|
+
private nextBrain;
|
|
133
|
+
}
|
|
134
|
+
export declare const brain: BrainFactory;
|
|
135
|
+
export {};
|
|
136
|
+
//# sourceMappingURL=brain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brain.d.ts","sourceRoot":"","sources":["../../../src/dsl/brain.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
readonly START: "
|
|
3
|
-
readonly RESTART: "
|
|
1
|
+
export declare const BRAIN_EVENTS: {
|
|
2
|
+
readonly START: "brain:start";
|
|
3
|
+
readonly RESTART: "brain:restart";
|
|
4
4
|
readonly STEP_START: "step:start";
|
|
5
5
|
readonly STEP_COMPLETE: "step:complete";
|
|
6
6
|
readonly STEP_STATUS: "step:status";
|
|
7
|
-
readonly ERROR: "
|
|
8
|
-
readonly COMPLETE: "
|
|
7
|
+
readonly ERROR: "brain:error";
|
|
8
|
+
readonly COMPLETE: "brain:complete";
|
|
9
9
|
};
|
|
10
10
|
export declare const STATUS: {
|
|
11
11
|
readonly PENDING: "pending";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/dsl/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/dsl/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAC;AAEX,eAAO,MAAM,MAAM;;;;;CAKT,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-patch.d.ts","sourceRoot":"","sources":["../../../src/dsl/json-patch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"json-patch.d.ts","sourceRoot":"","sources":["../../../src/dsl/json-patch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAE9C,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,SAAS,CAKzE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,GAC/B,KAAK,CAkBP"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export type {
|
|
7
|
-
export type {
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
1
|
+
export { Brain, brain } from './dsl/brain.js';
|
|
2
|
+
export { BrainRunner } from './dsl/brain-runner.js';
|
|
3
|
+
export { STATUS, BRAIN_EVENTS } from './dsl/constants.js';
|
|
4
|
+
export type { Adapter } from './adapters/types.js';
|
|
5
|
+
export type { BrainEvent, SerializedStep, InitialRunParams, RerunParams, BrainStartEvent, BrainCompleteEvent, BrainErrorEvent, StepStatusEvent, StepStartedEvent, StepCompletedEvent, BrainStructure, BrainFactory, } from './dsl/brain.js';
|
|
6
|
+
export type { ObjectGenerator, Message } from './clients/types.js';
|
|
7
|
+
export type { State } from './dsl/types.js';
|
|
8
|
+
export { createPatch, applyPatches } from './dsl/json-patch.js';
|
|
9
|
+
export { z } from 'zod';
|
|
10
|
+
export type { ResourceLoader } from './resources/resource-loader.js';
|
|
11
|
+
export { createResources, type Resources } from './resources/resources.js';
|
|
12
|
+
export type { Manifest as ResourceManifest, Entry as ResourceEntry, ResourceType, } from './resources/resources.js';
|
|
13
|
+
export { RESOURCE_TYPES } from './resources/resources.js';
|
|
12
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC1D,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACnE,YAAY,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMhE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,YAAY,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC3E,YAAY,EACV,QAAQ,IAAI,gBAAgB,EAC5B,KAAK,IAAI,aAAa,EACtB,YAAY,GACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface ResourceLoader {
|
|
2
|
+
load(resourceName: string, type?: 'text'): Promise<string>;
|
|
3
|
+
load(resourceName: string, type: 'binary'): Promise<Buffer>;
|
|
4
|
+
load(resourceName: string, type?: 'text' | 'binary'): Promise<string | Buffer>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=resource-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-loader.d.ts","sourceRoot":"","sources":["../../../src/resources/resource-loader.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,IAAI,CACF,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GACvB,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CAC7B"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ResourceLoader } from './resource-loader.js';
|
|
2
|
+
export declare const RESOURCE_TYPES: readonly ["text", "binary"];
|
|
3
|
+
export type ResourceType = (typeof RESOURCE_TYPES)[number];
|
|
4
|
+
export interface Entry {
|
|
5
|
+
type: ResourceType;
|
|
6
|
+
path: string;
|
|
7
|
+
key: string;
|
|
8
|
+
}
|
|
9
|
+
export interface Manifest {
|
|
10
|
+
[key: string]: ManifestEntry;
|
|
11
|
+
}
|
|
12
|
+
type ManifestEntry = Entry | Manifest;
|
|
13
|
+
interface Resource {
|
|
14
|
+
load: () => Promise<string | Buffer>;
|
|
15
|
+
loadText: () => Promise<string>;
|
|
16
|
+
loadBinary: () => Promise<Buffer>;
|
|
17
|
+
}
|
|
18
|
+
export interface Resources {
|
|
19
|
+
[key: string]: Resource | Resources;
|
|
20
|
+
}
|
|
21
|
+
export declare function createResources<M extends Manifest>(loader: ResourceLoader, initialManifest: M): Resources;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../../src/resources/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,eAAO,MAAM,cAAc,6BAA8B,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC;CAC9B;AAED,KAAK,aAAa,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEtC,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACrC,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;CACrC;AAMD,wBAAgB,eAAe,CAAC,CAAC,SAAS,QAAQ,EAChD,MAAM,EAAE,cAAc,EACtB,eAAe,EAAE,CAAC,aAwOnB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ObjectGenerator, Message } from './clients/types.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { jest } from '@jest/globals';
|
|
4
|
+
import type { Brain } from './dsl/brain.js';
|
|
5
|
+
import type { State } from './dsl/types.js';
|
|
6
|
+
import type { Resources } from './resources/resources.js';
|
|
7
|
+
/**
|
|
8
|
+
* Mock implementation of ObjectGenerator for testing
|
|
9
|
+
*/
|
|
10
|
+
export declare class MockObjectGenerator implements ObjectGenerator {
|
|
11
|
+
private generateObjectMock;
|
|
12
|
+
private calls;
|
|
13
|
+
constructor();
|
|
14
|
+
generateObject<T extends z.AnyZodObject>(params: Parameters<ObjectGenerator['generateObject']>[0]): Promise<z.infer<T>>;
|
|
15
|
+
/**
|
|
16
|
+
* Mock a response for the next generateObject call
|
|
17
|
+
*/
|
|
18
|
+
mockNextResponse<T>(response: T): void;
|
|
19
|
+
/**
|
|
20
|
+
* Mock multiple responses in sequence
|
|
21
|
+
*/
|
|
22
|
+
mockResponses(...responses: any[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* Mock an error for the next generateObject call
|
|
25
|
+
*/
|
|
26
|
+
mockNextError(error: Error | string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Get the underlying jest.Mock for advanced mocking scenarios
|
|
29
|
+
*/
|
|
30
|
+
get mock(): jest.Mock<any>;
|
|
31
|
+
/**
|
|
32
|
+
* Clear all mocks and call history
|
|
33
|
+
*/
|
|
34
|
+
clear(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Reset mock to initial state
|
|
37
|
+
*/
|
|
38
|
+
reset(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Get all calls made to generateObject
|
|
41
|
+
*/
|
|
42
|
+
getCalls(): {
|
|
43
|
+
params: Parameters<ObjectGenerator["generateObject"]>[0];
|
|
44
|
+
timestamp: Date;
|
|
45
|
+
}[];
|
|
46
|
+
/**
|
|
47
|
+
* Get the last call made to generateObject
|
|
48
|
+
*/
|
|
49
|
+
getLastCall(): {
|
|
50
|
+
params: Parameters<ObjectGenerator["generateObject"]>[0];
|
|
51
|
+
timestamp: Date;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Assert that generateObject was called with specific parameters
|
|
55
|
+
*/
|
|
56
|
+
expectCalledWith(expected: {
|
|
57
|
+
prompt?: string | ((actual: string) => boolean);
|
|
58
|
+
schemaName?: string;
|
|
59
|
+
messages?: Message[];
|
|
60
|
+
system?: string;
|
|
61
|
+
}): void;
|
|
62
|
+
/**
|
|
63
|
+
* Assert that generateObject was called N times
|
|
64
|
+
*/
|
|
65
|
+
expectCallCount(count: number): void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Creates a mock client for testing brains
|
|
69
|
+
*/
|
|
70
|
+
export declare function createMockClient(): MockObjectGenerator;
|
|
71
|
+
/**
|
|
72
|
+
* Test runner options
|
|
73
|
+
*/
|
|
74
|
+
export interface TestRunnerOptions<TState extends State = State> {
|
|
75
|
+
client?: ObjectGenerator;
|
|
76
|
+
resources?: Resources;
|
|
77
|
+
initialState?: TState;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Result of running a brain test
|
|
81
|
+
*/
|
|
82
|
+
export interface TestRunResult<TState extends State = State> {
|
|
83
|
+
finalState: TState;
|
|
84
|
+
steps: string[];
|
|
85
|
+
error: Error | null;
|
|
86
|
+
completed: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Runs a brain with test utilities and returns collected data
|
|
90
|
+
*/
|
|
91
|
+
export declare function runBrainTest<TOptions extends object = {}, TState extends State = {}>(brain: Brain<TOptions, TState, any>, options?: TestRunnerOptions<TState> & {
|
|
92
|
+
brainOptions?: TOptions;
|
|
93
|
+
}): Promise<TestRunResult<TState>>;
|
|
94
|
+
//# sourceMappingURL=test-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# Core Testing Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Core package tests focus on Brain DSL workflows, event sequences, and state management. This guide helps you write effective tests while avoiding common pitfalls.
|
|
6
|
+
|
|
7
|
+
## Key Testing Patterns
|
|
8
|
+
|
|
9
|
+
### 1. Brain Event Collection Pattern
|
|
10
|
+
|
|
11
|
+
**The Challenge**: Brains emit async events that need to be collected and verified.
|
|
12
|
+
|
|
13
|
+
**Solution**: Always collect ALL events before making assertions:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// CORRECT: Collect all events first
|
|
17
|
+
const events = [];
|
|
18
|
+
for await (const event of brain.run({ client: mockClient })) {
|
|
19
|
+
events.push(event);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Now make assertions
|
|
23
|
+
expect(events.map(e => e.type)).toContain(BRAIN_EVENTS.COMPLETE);
|
|
24
|
+
|
|
25
|
+
// WRONG: Don't try to assert while iterating
|
|
26
|
+
for await (const event of brain.run()) {
|
|
27
|
+
expect(event).toBeDefined(); // This can miss events!
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 2. State Reconstruction from Patches
|
|
32
|
+
|
|
33
|
+
**The Challenge**: Brain state is represented as JSON patches, not direct state objects.
|
|
34
|
+
|
|
35
|
+
**Solution**: Apply patches to reconstruct final state:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { applyPatches } from '@positronic/core';
|
|
39
|
+
|
|
40
|
+
// Helper to reconstruct state
|
|
41
|
+
function reconstructState(events: BrainEvent[]) {
|
|
42
|
+
let state = {};
|
|
43
|
+
for (const event of events) {
|
|
44
|
+
if (event.type === BRAIN_EVENTS.STEP_COMPLETE) {
|
|
45
|
+
state = applyPatches(state, [event.patch]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return state;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Usage
|
|
52
|
+
const events = await collectAllEvents(brain.run());
|
|
53
|
+
const finalState = reconstructState(events);
|
|
54
|
+
expect(finalState).toEqual({ expected: 'state' });
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 3. Mock Setup for AI Clients
|
|
58
|
+
|
|
59
|
+
**The Challenge**: Brains often depend on AI client calls that need specific mock setup.
|
|
60
|
+
|
|
61
|
+
**Solution**: Create properly typed mocks:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// Setup mock client with proper typing
|
|
65
|
+
const mockGenerateObject = jest.fn<ObjectGenerator['generateObject']>();
|
|
66
|
+
const mockClient: jest.Mocked<ObjectGenerator> = {
|
|
67
|
+
generateObject: mockGenerateObject,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Configure response BEFORE running brain
|
|
71
|
+
mockGenerateObject.mockResolvedValue({ result: 'test data' });
|
|
72
|
+
|
|
73
|
+
// Now run the brain
|
|
74
|
+
const brain = brain('test').step('Gen', async ({ client }) => {
|
|
75
|
+
const res = await client.generateObject({ prompt: 'test' });
|
|
76
|
+
return { data: res.result };
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 4. Resource Loading Mocks
|
|
81
|
+
|
|
82
|
+
**The Challenge**: Resources use a proxy API that's tricky to mock.
|
|
83
|
+
|
|
84
|
+
**Solution**: Mock the underlying loader, not the proxy:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
const mockResourceLoad = jest.fn();
|
|
88
|
+
const mockResourceLoader: ResourceLoader = {
|
|
89
|
+
load: mockResourceLoad,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Setup resource responses
|
|
93
|
+
const mockResources = {
|
|
94
|
+
'example.txt': { type: 'text', content: 'Hello' },
|
|
95
|
+
'data.json': { type: 'text', content: '{"key": "value"}' },
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
mockResourceLoad.mockImplementation(async (path) => {
|
|
99
|
+
const resource = mockResources[path];
|
|
100
|
+
if (!resource) throw new Error(`Resource not found: ${path}`);
|
|
101
|
+
return resource;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Use in brain - the proxy API will call your mock
|
|
105
|
+
const brain = brain('test').step('Load', async ({ resources }) => {
|
|
106
|
+
const text = await resources.example.loadText(); // Proxy API
|
|
107
|
+
return { content: text };
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 5. Testing Error Events
|
|
112
|
+
|
|
113
|
+
**The Challenge**: Errors emit special events but brain execution continues.
|
|
114
|
+
|
|
115
|
+
**Solution**: Look for ERROR events, not exceptions:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const errorBrain = brain('test').step('Fail', () => {
|
|
119
|
+
throw new Error('Step failed');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const events = [];
|
|
123
|
+
for await (const event of errorBrain.run()) {
|
|
124
|
+
events.push(event);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Find the error event
|
|
128
|
+
const errorEvent = events.find(e => e.type === BRAIN_EVENTS.ERROR);
|
|
129
|
+
expect(errorEvent).toBeDefined();
|
|
130
|
+
expect(errorEvent?.error.message).toBe('Step failed');
|
|
131
|
+
|
|
132
|
+
// Brain still completes!
|
|
133
|
+
expect(events.some(e => e.type === BRAIN_EVENTS.COMPLETE)).toBe(true);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 6. Type Inference Testing
|
|
137
|
+
|
|
138
|
+
**The Challenge**: Brain DSL uses complex TypeScript inference that needs testing.
|
|
139
|
+
|
|
140
|
+
**Solution**: Use compile-time type assertions:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Define a type equality helper
|
|
144
|
+
type AssertEquals<T, U> = T extends U ? (U extends T ? true : false) : false;
|
|
145
|
+
|
|
146
|
+
// Test that state types are inferred correctly
|
|
147
|
+
const typedBrain = brain('test')
|
|
148
|
+
.step('Init', () => ({ count: 0 }))
|
|
149
|
+
.step('Inc', ({ state }) => ({ count: state.count + 1 }));
|
|
150
|
+
|
|
151
|
+
// Extract the inferred state type
|
|
152
|
+
type InferredState = typeof typedBrain extends Brain<infer S> ? S : never;
|
|
153
|
+
|
|
154
|
+
// This line will fail compilation if types don't match
|
|
155
|
+
type Test = AssertEquals<InferredState, { count: number }>;
|
|
156
|
+
const _: Test = true;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Common Pitfalls & Solutions
|
|
160
|
+
|
|
161
|
+
### Pitfall 1: Forgetting to Mock Client Methods
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// WRONG: Forgot to mock generateObject
|
|
165
|
+
const brain = brain('test').step('Gen', async ({ client }) => {
|
|
166
|
+
const res = await client.generateObject({ prompt: 'test' });
|
|
167
|
+
return res;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// This will fail with "mockGenerateObject is not a function"
|
|
171
|
+
await brain.run({ client: mockClient });
|
|
172
|
+
|
|
173
|
+
// CORRECT: Always mock methods before use
|
|
174
|
+
mockGenerateObject.mockResolvedValue({ data: 'test' });
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Pitfall 2: Testing During Event Iteration
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// WRONG: Testing while iterating can miss events
|
|
181
|
+
let foundComplete = false;
|
|
182
|
+
for await (const event of brain.run()) {
|
|
183
|
+
if (event.type === BRAIN_EVENTS.COMPLETE) {
|
|
184
|
+
foundComplete = true;
|
|
185
|
+
break; // Stops iteration early!
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// CORRECT: Collect all events first
|
|
190
|
+
const events = await collectAllEvents(brain.run());
|
|
191
|
+
const foundComplete = events.some(e => e.type === BRAIN_EVENTS.COMPLETE);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Pitfall 3: Not Handling Async Steps
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// WRONG: Synchronous step function when async is needed
|
|
198
|
+
const brain = brain('test').step('Async', ({ client }) => {
|
|
199
|
+
// This won't wait for the promise!
|
|
200
|
+
client.generateObject({ prompt: 'test' });
|
|
201
|
+
return { done: true };
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// CORRECT: Use async/await
|
|
205
|
+
const brain = brain('test').step('Async', async ({ client }) => {
|
|
206
|
+
const result = await client.generateObject({ prompt: 'test' });
|
|
207
|
+
return { done: true, result };
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Pitfall 4: Incorrect Event Sequence Expectations
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// WRONG: Expecting only main events
|
|
215
|
+
expect(events.map(e => e.type)).toEqual([
|
|
216
|
+
BRAIN_EVENTS.START,
|
|
217
|
+
BRAIN_EVENTS.COMPLETE
|
|
218
|
+
]);
|
|
219
|
+
|
|
220
|
+
// CORRECT: Include all events in sequence
|
|
221
|
+
expect(events.map(e => e.type)).toEqual([
|
|
222
|
+
BRAIN_EVENTS.START,
|
|
223
|
+
BRAIN_EVENTS.STEP_STATUS, // Don't forget status events!
|
|
224
|
+
BRAIN_EVENTS.STEP_START,
|
|
225
|
+
BRAIN_EVENTS.STEP_COMPLETE,
|
|
226
|
+
BRAIN_EVENTS.STEP_STATUS,
|
|
227
|
+
BRAIN_EVENTS.COMPLETE
|
|
228
|
+
]);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Quick Test Template
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { brain, BRAIN_EVENTS, applyPatches } from '@positronic/core';
|
|
235
|
+
import type { ObjectGenerator, ResourceLoader } from '@positronic/core';
|
|
236
|
+
|
|
237
|
+
describe('my brain feature', () => {
|
|
238
|
+
// Setup mocks
|
|
239
|
+
const mockGenerateObject = jest.fn<ObjectGenerator['generateObject']>();
|
|
240
|
+
const mockClient: jest.Mocked<ObjectGenerator> = {
|
|
241
|
+
generateObject: mockGenerateObject,
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
beforeEach(() => {
|
|
245
|
+
jest.clearAllMocks();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('should do something', async () => {
|
|
249
|
+
// Configure mocks
|
|
250
|
+
mockGenerateObject.mockResolvedValue({ result: 'test' });
|
|
251
|
+
|
|
252
|
+
// Define brain
|
|
253
|
+
const testBrain = brain('test')
|
|
254
|
+
.step('Process', async ({ client }) => {
|
|
255
|
+
const res = await client.generateObject({ prompt: 'test' });
|
|
256
|
+
return { processed: res.result };
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Collect all events
|
|
260
|
+
const events = [];
|
|
261
|
+
for await (const event of testBrain.run({ client: mockClient })) {
|
|
262
|
+
events.push(event);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Verify final state
|
|
266
|
+
let finalState = {};
|
|
267
|
+
for (const event of events) {
|
|
268
|
+
if (event.type === BRAIN_EVENTS.STEP_COMPLETE) {
|
|
269
|
+
finalState = applyPatches(finalState, [event.patch]);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
expect(finalState).toEqual({ processed: 'test' });
|
|
273
|
+
|
|
274
|
+
// Verify completion
|
|
275
|
+
expect(events.some(e => e.type === BRAIN_EVENTS.COMPLETE)).toBe(true);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Running Tests
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
# From monorepo root (required!)
|
|
284
|
+
npm test -- packages/core # Run all core tests
|
|
285
|
+
npm test -- brain.test.ts # Run specific test file
|
|
286
|
+
npm test -- -t "should process" # Run tests matching pattern
|
|
287
|
+
npm run test:watch # Watch mode
|
|
288
|
+
npm run build:workspaces # Ensure TypeScript compiles
|
|
289
|
+
```
|