@positronic/template-new-project 0.0.76 → 0.0.78
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/index.js +5 -4
- package/package.json +1 -1
- package/template/.positronic/build-brains.mjs +93 -0
- package/template/.positronic/bundle.ts +1 -1
- package/template/.positronic/src/index.ts +6 -1
- package/template/.positronic/wrangler.jsonc +4 -0
- package/template/CLAUDE.md +149 -50
- package/template/docs/brain-dsl-guide.md +661 -510
- package/template/docs/brain-testing-guide.md +63 -3
- package/template/docs/memory-guide.md +116 -100
- package/template/docs/plugin-guide.md +218 -0
- package/template/docs/positronic-guide.md +99 -78
- package/template/docs/tips-for-agents.md +179 -79
- package/template/src/brain.ts +73 -0
- package/template/src/brains/hello.ts +46 -0
- package/template/{runner.ts → src/runner.ts} +9 -12
- package/template/tests/example.test.ts +1 -1
- package/template/tests/test-utils.ts +1 -4
- package/template/tsconfig.json +4 -2
- package/template/brain.ts +0 -96
- package/template/brains/hello.ts +0 -44
- /package/template/{brains → src/brains}/example.ts +0 -0
- /package/template/{components → src/components}/index.ts +0 -0
- /package/template/{utils → src/utils}/bottleneck.ts +0 -0
- /package/template/{webhooks → src/webhooks}/.gitkeep +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createBrain } from '@positronic/core';
|
|
2
|
+
import { components } from './components/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Project-level brain function with pre-configured components.
|
|
6
|
+
*
|
|
7
|
+
* All brains in your project should import from this file:
|
|
8
|
+
*
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { brain } from '../brain.js';
|
|
11
|
+
*
|
|
12
|
+
* export default brain('my-brain')
|
|
13
|
+
* .step('Do something', ({ state }) => ({ ...state, done: true }));
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* ## Prompt steps with tool-calling loops
|
|
17
|
+
*
|
|
18
|
+
* Use `.prompt()` with a `loop` property to run an LLM with tools:
|
|
19
|
+
*
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { generatePage, waitForWebhook } from '@positronic/core';
|
|
22
|
+
*
|
|
23
|
+
* export default brain('my-brain')
|
|
24
|
+
* .prompt('Do Work', ({ state }) => ({
|
|
25
|
+
* system: 'You are a helpful assistant',
|
|
26
|
+
* message: `Help the user with: <%= '${state.task}' %>`,
|
|
27
|
+
* outputSchema: z.object({ result: z.string() }),
|
|
28
|
+
* loop: {
|
|
29
|
+
* tools: { generatePage, waitForWebhook },
|
|
30
|
+
* },
|
|
31
|
+
* }));
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Without `loop`, `.prompt()` makes a single LLM call for structured output.
|
|
35
|
+
* With `loop`, the LLM calls tools iteratively until it calls the auto-generated
|
|
36
|
+
* 'done' tool with data matching the outputSchema.
|
|
37
|
+
*
|
|
38
|
+
* ## Adding plugins
|
|
39
|
+
*
|
|
40
|
+
* Plugins provide services, tools, and event adapters to brains.
|
|
41
|
+
* Configure them in createBrain or per-brain with .withPlugin():
|
|
42
|
+
*
|
|
43
|
+
* ```typescript
|
|
44
|
+
* import { createBrain } from '@positronic/core';
|
|
45
|
+
* import { components } from './components/index.js';
|
|
46
|
+
* import { mem0 } from '@positronic/mem0';
|
|
47
|
+
*
|
|
48
|
+
* export const brain = createBrain({
|
|
49
|
+
* plugins: [mem0.setup({ apiKey: process.env.MEM0_API_KEY! })],
|
|
50
|
+
* components,
|
|
51
|
+
* });
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* Then plugin services are available in all brain steps under the plugin name:
|
|
55
|
+
*
|
|
56
|
+
* ```typescript
|
|
57
|
+
* export default brain('my-brain')
|
|
58
|
+
* .step('Remember', async ({ mem0 }) => {
|
|
59
|
+
* const prefs = await mem0.search('user preferences');
|
|
60
|
+
* return { preferences: prefs };
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* Or declare multiple plugins upfront:
|
|
65
|
+
*
|
|
66
|
+
* ```typescript
|
|
67
|
+
* brain({ title: 'my-brain', plugins: { slack, mem0 } })
|
|
68
|
+
* .step('Go', ({ slack, mem0 }) => { ... });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export const brain = createBrain({
|
|
72
|
+
components,
|
|
73
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { brain } from '../brain.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { generatePage, waitForWebhook } from '@positronic/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A simple brain that demonstrates .prompt() with a tool-calling loop.
|
|
7
|
+
*
|
|
8
|
+
* This brain:
|
|
9
|
+
* 1. Uses generatePage to create a form asking for the user's name
|
|
10
|
+
* 2. Waits for the user to submit the form
|
|
11
|
+
* 3. Completes with a structured welcome message (via the 'done' tool)
|
|
12
|
+
* 4. The follow-up step logs the greeting, demonstrating type inference
|
|
13
|
+
*
|
|
14
|
+
* The `loop` property on `.prompt()` enables tool-calling: the LLM calls
|
|
15
|
+
* tools iteratively until it calls the auto-generated 'done' tool with
|
|
16
|
+
* data matching the outputSchema. The result is spread onto state.
|
|
17
|
+
*
|
|
18
|
+
* Run with: px brain run hello
|
|
19
|
+
*/
|
|
20
|
+
export default brain('hello')
|
|
21
|
+
.prompt('Greet User', () => ({
|
|
22
|
+
system: `
|
|
23
|
+
You are a friendly greeter for the Positronic framework.
|
|
24
|
+
|
|
25
|
+
Your job is to welcome new users and make them feel excited about building AI workflows.
|
|
26
|
+
|
|
27
|
+
Use the generatePage tool to create a form asking for the user's name.
|
|
28
|
+
Once you have their name, call 'done' with a personalized greeting.
|
|
29
|
+
`,
|
|
30
|
+
message: 'Begin.',
|
|
31
|
+
outputSchema: z.object({
|
|
32
|
+
userName: z.string().describe('The name the user provided'),
|
|
33
|
+
greeting: z
|
|
34
|
+
.string()
|
|
35
|
+
.describe('A personalized welcome message for the user'),
|
|
36
|
+
}),
|
|
37
|
+
loop: {
|
|
38
|
+
tools: { generatePage, waitForWebhook },
|
|
39
|
+
},
|
|
40
|
+
}))
|
|
41
|
+
.step('Log Welcome', ({ state }) => {
|
|
42
|
+
// TypeScript knows state has userName and greeting (spread from outputSchema)
|
|
43
|
+
console.log('\n✨ ' + state.greeting);
|
|
44
|
+
console.log(' Welcome aboard, ' + state.userName + '!\n');
|
|
45
|
+
return state;
|
|
46
|
+
});
|
|
@@ -38,24 +38,21 @@ import { google } from '@ai-sdk/google';
|
|
|
38
38
|
*
|
|
39
39
|
* ## Memory
|
|
40
40
|
*
|
|
41
|
-
* To add memory
|
|
41
|
+
* To add semantic memory via Mem0, use the mem0 plugin in your
|
|
42
|
+
* src/brain.ts file:
|
|
42
43
|
*
|
|
43
44
|
* ```typescript
|
|
44
|
-
* import {
|
|
45
|
+
* import { createBrain } from '@positronic/core';
|
|
46
|
+
* import { mem0 } from '@positronic/mem0';
|
|
45
47
|
*
|
|
46
|
-
* const
|
|
47
|
-
* apiKey: process.env.MEM0_API_KEY
|
|
48
|
-
* });
|
|
49
|
-
*
|
|
50
|
-
* const memoryAdapter = createMem0Adapter({ provider });
|
|
51
|
-
*
|
|
52
|
-
* export const runner = new BrainRunner({
|
|
53
|
-
* adapters: [memoryAdapter],
|
|
54
|
-
* client: new VercelClient(google('gemini-3-pro-preview')),
|
|
55
|
-
* resources: {},
|
|
48
|
+
* export const brain = createBrain({
|
|
49
|
+
* plugins: [mem0.setup({ apiKey: process.env.MEM0_API_KEY! })],
|
|
56
50
|
* });
|
|
57
51
|
* ```
|
|
58
52
|
*
|
|
53
|
+
* Then use `mem0.search()` and `mem0.add()` in brain steps, or pass
|
|
54
|
+
* `mem0.tools` to a prompt loop for LLM-driven memory.
|
|
55
|
+
*
|
|
59
56
|
* See docs/memory-guide.md for more details.
|
|
60
57
|
*/
|
|
61
58
|
const client = new VercelClient(google('gemini-3-pro-preview'));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createMockClient, runBrainTest } from './test-utils.js';
|
|
2
|
-
import exampleBrain from '../brains/example.js';
|
|
2
|
+
import exampleBrain from '../src/brains/example.js';
|
|
3
3
|
|
|
4
4
|
describe('example brain', () => {
|
|
5
5
|
it('should complete successfully with welcome messages', async () => {
|
|
@@ -48,7 +48,6 @@ export interface BrainTestResult<TState> {
|
|
|
48
48
|
export async function runBrainTest<
|
|
49
49
|
TOptions extends object = object,
|
|
50
50
|
TState extends object = object,
|
|
51
|
-
TServices extends object = object
|
|
52
51
|
>(
|
|
53
52
|
brain: any,
|
|
54
53
|
params?: {
|
|
@@ -56,7 +55,6 @@ export async function runBrainTest<
|
|
|
56
55
|
initialState?: Partial<TState>;
|
|
57
56
|
resources?: any;
|
|
58
57
|
options?: TOptions;
|
|
59
|
-
services?: TServices;
|
|
60
58
|
}
|
|
61
59
|
): Promise<BrainTestResult<TState>> {
|
|
62
60
|
const events: BrainEvent<any>[] = [];
|
|
@@ -72,8 +70,7 @@ export async function runBrainTest<
|
|
|
72
70
|
options: params?.options,
|
|
73
71
|
};
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
const brainToRun = params?.services ? brain.withServices(params.services) : brain;
|
|
73
|
+
const brainToRun = brain;
|
|
77
74
|
|
|
78
75
|
for await (const event of brainToRun.run(runOptions)) {
|
|
79
76
|
events.push(event);
|
package/template/tsconfig.json
CHANGED
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
"skipLibCheck": true,
|
|
10
10
|
"forceConsistentCasingInFileNames": true,
|
|
11
11
|
"outDir": "./dist",
|
|
12
|
-
"rootDir": "./"
|
|
12
|
+
"rootDir": "./",
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"jsxImportSource": "@positronic/core"
|
|
13
15
|
},
|
|
14
|
-
"include": ["brains/**/*.ts", "resources.d.ts"],
|
|
16
|
+
"include": ["brains/**/*.ts", "brains/**/*.tsx", "resources.d.ts"],
|
|
15
17
|
"exclude": ["node_modules", ".positronic"]
|
|
16
18
|
}
|
package/template/brain.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { createBrain, defaultTools } from '@positronic/core';
|
|
2
|
-
import { components } from './components/index.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Project-level brain function with pre-configured components and tools.
|
|
6
|
-
*
|
|
7
|
-
* All brains in your project should import from this file:
|
|
8
|
-
*
|
|
9
|
-
* ```typescript
|
|
10
|
-
* import { brain } from '../brain.js';
|
|
11
|
-
*
|
|
12
|
-
* export default brain('my-brain')
|
|
13
|
-
* .step('Do something', ({ state }) => ({ ...state, done: true }));
|
|
14
|
-
* ```
|
|
15
|
-
*
|
|
16
|
-
* Default tools available in agent steps:
|
|
17
|
-
* - generateUI: Generate interactive UI components
|
|
18
|
-
* - consoleLog: Log messages for debugging
|
|
19
|
-
* - done: Complete the agent and return a result
|
|
20
|
-
*
|
|
21
|
-
* Tool configuration:
|
|
22
|
-
* - `withTools({ ... })` — replaces the default tools entirely
|
|
23
|
-
* - `withExtraTools({ ... })` — adds tools alongside the defaults
|
|
24
|
-
*
|
|
25
|
-
* To add services (e.g., Slack, Gmail, database clients):
|
|
26
|
-
*
|
|
27
|
-
* ```typescript
|
|
28
|
-
* import { createBrain, defaultTools } from '@positronic/core';
|
|
29
|
-
* import { components } from './components/index.js';
|
|
30
|
-
* import slack from './services/slack.js';
|
|
31
|
-
* import gmail from './services/gmail.js';
|
|
32
|
-
*
|
|
33
|
-
* export const brain = createBrain({
|
|
34
|
-
* services: { slack, gmail },
|
|
35
|
-
* components,
|
|
36
|
-
* defaultTools,
|
|
37
|
-
* });
|
|
38
|
-
* ```
|
|
39
|
-
*
|
|
40
|
-
* Then services are available in all brain steps:
|
|
41
|
-
*
|
|
42
|
-
* ```typescript
|
|
43
|
-
* export default brain('notify')
|
|
44
|
-
* .step('Send alert', ({ slack }) => {
|
|
45
|
-
* slack.postMessage('#alerts', 'Something happened!');
|
|
46
|
-
* return { notified: true };
|
|
47
|
-
* });
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* You can also create agents directly with access to default tools:
|
|
51
|
-
*
|
|
52
|
-
* ```typescript
|
|
53
|
-
* export default brain('my-agent', ({ slack, tools }) => ({
|
|
54
|
-
* system: 'You are a helpful assistant',
|
|
55
|
-
* prompt: 'Help the user with their request',
|
|
56
|
-
* tools: {
|
|
57
|
-
* ...tools, // includes generateUI, consoleLog, done
|
|
58
|
-
* notify: {
|
|
59
|
-
* description: 'Send a Slack notification',
|
|
60
|
-
* inputSchema: z.object({ message: z.string() }),
|
|
61
|
-
* execute: ({ message }) => slack.postMessage('#general', message),
|
|
62
|
-
* },
|
|
63
|
-
* },
|
|
64
|
-
* }));
|
|
65
|
-
* ```
|
|
66
|
-
*
|
|
67
|
-
* To add memory (long-term storage with semantic search):
|
|
68
|
-
*
|
|
69
|
-
* ```typescript
|
|
70
|
-
* import { createBrain, defaultTools } from '@positronic/core';
|
|
71
|
-
* import { createMem0Provider, createMem0Tools } from '@positronic/mem0';
|
|
72
|
-
* import { components } from './components/index.js';
|
|
73
|
-
*
|
|
74
|
-
* const memory = createMem0Provider({
|
|
75
|
-
* apiKey: process.env.MEM0_API_KEY!,
|
|
76
|
-
* });
|
|
77
|
-
*
|
|
78
|
-
* export const brain = createBrain({
|
|
79
|
-
* components,
|
|
80
|
-
* defaultTools,
|
|
81
|
-
* memory, // All brains now have access to memory
|
|
82
|
-
* });
|
|
83
|
-
*
|
|
84
|
-
* // Memory tools (rememberFact, recallMemories) can be added to agents:
|
|
85
|
-
* const memoryTools = createMem0Tools();
|
|
86
|
-
* ```
|
|
87
|
-
*
|
|
88
|
-
* Memory is automatically scoped to the current user (via currentUser.name)
|
|
89
|
-
* and the brain name. No need to pass userId manually.
|
|
90
|
-
*
|
|
91
|
-
* See docs/memory-guide.md for full details.
|
|
92
|
-
*/
|
|
93
|
-
export const brain = createBrain({
|
|
94
|
-
components,
|
|
95
|
-
defaultTools,
|
|
96
|
-
});
|
package/template/brains/hello.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { brain } from '../brain.js';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A simple agent brain that demonstrates the default tools and outputSchema.
|
|
6
|
-
*
|
|
7
|
-
* This brain uses only a system prompt and tools - no explicit user prompt needed.
|
|
8
|
-
* When prompt is omitted, the agent automatically starts with "Begin."
|
|
9
|
-
*
|
|
10
|
-
* The outputSchema ensures the agent returns structured data that gets stored
|
|
11
|
-
* in state.welcome - making it available for subsequent steps.
|
|
12
|
-
*
|
|
13
|
-
* This brain:
|
|
14
|
-
* 1. Uses generateUI to create a form asking for the user's name
|
|
15
|
-
* 2. Waits for the user to submit the form
|
|
16
|
-
* 3. Completes with a structured welcome message (via outputSchema)
|
|
17
|
-
* 4. The follow-up step logs the greeting, demonstrating type inference
|
|
18
|
-
*
|
|
19
|
-
* Run with: px brain run hello
|
|
20
|
-
*/
|
|
21
|
-
export default brain('hello', {
|
|
22
|
-
system: `
|
|
23
|
-
You are a friendly greeter for the Positronic framework.
|
|
24
|
-
|
|
25
|
-
Your job is to welcome new users and make them feel excited about building AI workflows.
|
|
26
|
-
|
|
27
|
-
You have access to a few different tools. Use these tools to greet the user and ask them for their name.
|
|
28
|
-
|
|
29
|
-
Once you have the user's name send them a personalized greeting!
|
|
30
|
-
`,
|
|
31
|
-
outputSchema: {
|
|
32
|
-
schema: z.object({
|
|
33
|
-
userName: z.string().describe('The name the user provided'),
|
|
34
|
-
greeting: z.string().describe('A personalized welcome message for the user'),
|
|
35
|
-
}),
|
|
36
|
-
name: 'welcome' as const,
|
|
37
|
-
},
|
|
38
|
-
})
|
|
39
|
-
.step('Log Welcome', ({ state }) => {
|
|
40
|
-
// TypeScript knows state.welcome has userName and greeting
|
|
41
|
-
console.log('\n✨ ' + state.welcome.greeting);
|
|
42
|
-
console.log(' Welcome aboard, ' + state.welcome.userName + '!\n');
|
|
43
|
-
return state;
|
|
44
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|