@astrive-ai/planner 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ export * from './planner.js';
2
+ export * from './simple-planner.js';
3
+ export * from './plan-optimizer.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './planner.js';
2
+ export * from './simple-planner.js';
3
+ export * from './plan-optimizer.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ExecutionPlan, PlanStep } from '@astrive-ai/types';
2
+ export declare class PlanOptimizer {
3
+ optimize(plan: ExecutionPlan): ExecutionPlan;
4
+ detectParallelizable(steps: PlanStep[]): string[][];
5
+ }
6
+ //# sourceMappingURL=plan-optimizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-optimizer.d.ts","sourceRoot":"","sources":["../src/plan-optimizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEjE,qBAAa,aAAa;IACxB,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa;IAkB5C,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,EAAE;CAoBpD"}
@@ -0,0 +1,30 @@
1
+ export class PlanOptimizer {
2
+ optimize(plan) {
3
+ // Basic optimization: reorder based on dependencies, mark parallel
4
+ const steps = [...plan.steps];
5
+ // Remove duplicate steps (same tool, same args)
6
+ const uniqueSteps = steps.filter((step, index, self) => index === self.findIndex((t) => (t.toolName === step.toolName &&
7
+ JSON.stringify(t.arguments) === JSON.stringify(step.arguments))));
8
+ return {
9
+ ...plan,
10
+ steps: uniqueSteps
11
+ };
12
+ }
13
+ detectParallelizable(steps) {
14
+ const levels = [];
15
+ const processed = new Set();
16
+ let currentLevel = steps
17
+ .filter(s => !s.dependsOn || s.dependsOn.length === 0)
18
+ .map(s => s.id);
19
+ while (currentLevel.length > 0) {
20
+ levels.push(currentLevel);
21
+ currentLevel.forEach(id => processed.add(id));
22
+ currentLevel = steps
23
+ .filter(s => !processed.has(s.id))
24
+ .filter(s => s.dependsOn && s.dependsOn.every(dep => processed.has(dep)))
25
+ .map(s => s.id);
26
+ }
27
+ return levels;
28
+ }
29
+ }
30
+ //# sourceMappingURL=plan-optimizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-optimizer.js","sourceRoot":"","sources":["../src/plan-optimizer.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,aAAa;IACxB,QAAQ,CAAC,IAAmB;QAC1B,mEAAmE;QACnE,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAE9B,gDAAgD;QAChD,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CACrD,KAAK,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAC9B,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ;YAC5B,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAC/D,CAAC,CACH,CAAC;QAEF,OAAO;YACL,GAAG,IAAI;YACP,KAAK,EAAE,WAAW;SACnB,CAAC;IACJ,CAAC;IAED,oBAAoB,CAAC,KAAiB;QACpC,MAAM,MAAM,GAAe,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QAEpC,IAAI,YAAY,GAAG,KAAK;aACrB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC;aACrD,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAElB,OAAO,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1B,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YAE9C,YAAY,GAAG,KAAK;iBACjB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;iBACjC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;iBACxE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ import type { IPlanner, IProvider, ILogger, IEventEmitter, PlanRequest, ExecutionPlan, AstriveError } from '@astrive-ai/types';
2
+ export declare class LLMPlanner implements IPlanner {
3
+ private provider;
4
+ private logger;
5
+ private events;
6
+ constructor(provider: IProvider, logger: ILogger, events: IEventEmitter);
7
+ plan(request: PlanRequest): Promise<ExecutionPlan>;
8
+ replan(plan: ExecutionPlan, error: AstriveError): Promise<ExecutionPlan>;
9
+ private buildPlanningPrompt;
10
+ private parsePlanResponse;
11
+ }
12
+ //# sourceMappingURL=planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAY,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGzI,qBAAa,UAAW,YAAW,QAAQ;IAEvC,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;gBAFN,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,aAAa;IAGzB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC;IAiClD,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IA2B9E,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,iBAAiB;CAiB1B"}
@@ -0,0 +1,100 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ export class LLMPlanner {
3
+ provider;
4
+ logger;
5
+ events;
6
+ constructor(provider, logger, events) {
7
+ this.provider = provider;
8
+ this.logger = logger;
9
+ this.events = events;
10
+ }
11
+ async plan(request) {
12
+ this.events.emit('plan:start', { request });
13
+ this.logger.debug('Starting planning phase', { request });
14
+ const prompt = this.buildPlanningPrompt(request);
15
+ const response = await this.provider.chat({
16
+ messages: [{ role: 'user', content: prompt }]
17
+ });
18
+ const parsed = this.parsePlanResponse(response.content);
19
+ const steps = parsed.steps.map(step => ({
20
+ id: randomUUID(),
21
+ toolName: step.toolName,
22
+ arguments: step.arguments,
23
+ description: step.description,
24
+ dependsOn: step.dependsOn || [],
25
+ status: 'pending'
26
+ }));
27
+ const plan = {
28
+ id: randomUUID(),
29
+ steps,
30
+ reasoning: parsed.reasoning,
31
+ status: 'pending',
32
+ estimatedDuration: 0
33
+ };
34
+ this.logger.debug('Planning complete', { plan });
35
+ this.events.emit('plan:end', { plan });
36
+ return plan;
37
+ }
38
+ async replan(plan, error) {
39
+ this.logger.debug('Replanning due to error', { error: error.message });
40
+ const prompt = `The previous plan failed with error: ${error.message}.\n\nOriginal Plan:\n${JSON.stringify(plan.steps, null, 2)}\n\nPlease provide a revised JSON plan that avoids this error.`;
41
+ const response = await this.provider.chat({
42
+ messages: [{ role: 'user', content: prompt }]
43
+ });
44
+ const parsed = this.parsePlanResponse(response.content);
45
+ const steps = parsed.steps.map(step => ({
46
+ id: randomUUID(),
47
+ toolName: step.toolName,
48
+ arguments: step.arguments,
49
+ description: step.description,
50
+ dependsOn: step.dependsOn || [],
51
+ status: 'pending'
52
+ }));
53
+ return {
54
+ id: randomUUID(),
55
+ steps,
56
+ reasoning: parsed.reasoning,
57
+ status: 'pending',
58
+ estimatedDuration: 0
59
+ };
60
+ }
61
+ buildPlanningPrompt(request) {
62
+ return `You are an AI planner. Your task is to analyze the user request and return a structured JSON plan.
63
+ Available Tools:
64
+ ${JSON.stringify(request.tools, null, 2)}
65
+
66
+ User Request: ${request.prompt}
67
+
68
+ Return ONLY a JSON object with the following structure:
69
+ {
70
+ "steps": [
71
+ {
72
+ "toolName": "name_of_tool",
73
+ "arguments": { "arg1": "value" },
74
+ "description": "What this step does",
75
+ "dependsOn": [] // Optional array of step IDs this step depends on
76
+ }
77
+ ],
78
+ "reasoning": "Explanation of the plan"
79
+ }
80
+ `;
81
+ }
82
+ parsePlanResponse(response) {
83
+ let jsonStr = response;
84
+ const match = response.match(/```(?:json)?\s*([\s\S]*?)```/);
85
+ if (match) {
86
+ jsonStr = match[1];
87
+ }
88
+ try {
89
+ const data = JSON.parse(jsonStr.trim());
90
+ return {
91
+ steps: Array.isArray(data.steps) ? data.steps : [],
92
+ reasoning: data.reasoning || ''
93
+ };
94
+ }
95
+ catch (err) {
96
+ throw new Error(`Failed to parse plan response: ${err}`);
97
+ }
98
+ }
99
+ }
100
+ //# sourceMappingURL=planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.js","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { IPlanner, PlanRequest, ExecutionPlan } from '@astrive-ai/types';
2
+ export declare class SimplePlanner implements IPlanner {
3
+ plan(request: PlanRequest): Promise<ExecutionPlan>;
4
+ replan(plan: ExecutionPlan, error: any): Promise<ExecutionPlan>;
5
+ private extractUrl;
6
+ }
7
+ //# sourceMappingURL=simple-planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simple-planner.d.ts","sourceRoot":"","sources":["../src/simple-planner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAY,MAAM,mBAAmB,CAAC;AAGxF,qBAAa,aAAc,YAAW,QAAQ;IACtC,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC;IA4DlD,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,aAAa,CAAC;IAIrE,OAAO,CAAC,UAAU;CAKnB"}
@@ -0,0 +1,78 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ export class SimplePlanner {
3
+ async plan(request) {
4
+ const input = request.prompt.toLowerCase();
5
+ const steps = [];
6
+ let reasoning = 'Direct response';
7
+ const addStep = (toolName, description, args = {}) => {
8
+ steps.push({
9
+ id: randomUUID(),
10
+ toolName,
11
+ arguments: args,
12
+ description,
13
+ dependsOn: [],
14
+ status: 'pending'
15
+ });
16
+ };
17
+ if (input.includes('download')) {
18
+ addStep('downloader', 'Download media', { url: this.extractUrl(input) });
19
+ reasoning = 'User wants to download media';
20
+ }
21
+ else if (input.includes('image') || input.includes('generate') || input.includes('create image')) {
22
+ addStep('texttoimage', 'Generate image', { prompt: request.prompt });
23
+ reasoning = 'User wants to generate an image';
24
+ }
25
+ else if (input.includes('remove background') || input.includes('removebg')) {
26
+ addStep('removebg', 'Remove background', { image: this.extractUrl(input) });
27
+ reasoning = 'User wants to remove background';
28
+ }
29
+ else if (input.includes('upscale')) {
30
+ addStep('upscaler', 'Upscale image', { image: this.extractUrl(input) });
31
+ reasoning = 'User wants to upscale an image';
32
+ }
33
+ else if (input.includes('face swap')) {
34
+ addStep('faceswap', 'Swap faces', { faceImage: '', targetImage: '' });
35
+ reasoning = 'User wants to swap faces';
36
+ }
37
+ else if (input.includes('search') || input.includes('find') || input.includes('look up')) {
38
+ addStep('websearch', 'Search web', { query: request.prompt });
39
+ reasoning = 'User wants to search the web';
40
+ }
41
+ else if (input.includes('speech') || input.includes('say') || input.includes('speak') || input.includes('tts')) {
42
+ addStep('texttospeech', 'Text to speech', { text: request.prompt });
43
+ reasoning = 'User wants text to speech';
44
+ }
45
+ else if (input.includes('transcribe') || input.includes('listen') || input.includes('stt')) {
46
+ addStep('speechtotext', 'Transcribe audio', { audio: '' });
47
+ reasoning = 'User wants audio transcription';
48
+ }
49
+ else if (input.includes('vision') || input.includes('describe image') || input.includes('analyze image')) {
50
+ addStep('vision', 'Analyze image', { image: this.extractUrl(input) });
51
+ reasoning = 'User wants to analyze an image';
52
+ }
53
+ else if (input.includes('ocr') || input.includes('read text') || input.includes('extract text')) {
54
+ addStep('ocr', 'Extract text', { image: this.extractUrl(input) });
55
+ reasoning = 'User wants to extract text from image';
56
+ }
57
+ else if (input.includes('edit image')) {
58
+ addStep('img2img', 'Edit image', { image: this.extractUrl(input), prompt: request.prompt });
59
+ reasoning = 'User wants to edit an image';
60
+ }
61
+ return {
62
+ id: randomUUID(),
63
+ steps,
64
+ reasoning,
65
+ status: 'pending',
66
+ estimatedDuration: 0
67
+ };
68
+ }
69
+ async replan(plan, error) {
70
+ return plan;
71
+ }
72
+ extractUrl(text) {
73
+ const urlRegex = /(https?:\/\/[^\s]+)/g;
74
+ const match = text.match(urlRegex);
75
+ return match ? match[0] : '';
76
+ }
77
+ }
78
+ //# sourceMappingURL=simple-planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simple-planner.js","sourceRoot":"","sources":["../src/simple-planner.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@astrive-ai/planner",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "dependencies": {
18
+ "@astrive-ai/types": "*",
19
+ "@astrive-ai/logger": "*",
20
+ "@astrive-ai/events": "*"
21
+ },
22
+ "devDependencies": {
23
+ "typescript": "5.4.5"
24
+ }
25
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './planner.js';
2
+ export * from './simple-planner.js';
3
+ export * from './plan-optimizer.js';
@@ -0,0 +1,42 @@
1
+ import type { ExecutionPlan, PlanStep } from '@astrive-ai/types';
2
+
3
+ export class PlanOptimizer {
4
+ optimize(plan: ExecutionPlan): ExecutionPlan {
5
+ // Basic optimization: reorder based on dependencies, mark parallel
6
+ const steps = [...plan.steps];
7
+
8
+ // Remove duplicate steps (same tool, same args)
9
+ const uniqueSteps = steps.filter((step, index, self) =>
10
+ index === self.findIndex((t) => (
11
+ t.toolName === step.toolName &&
12
+ JSON.stringify(t.arguments) === JSON.stringify(step.arguments)
13
+ ))
14
+ );
15
+
16
+ return {
17
+ ...plan,
18
+ steps: uniqueSteps
19
+ };
20
+ }
21
+
22
+ detectParallelizable(steps: PlanStep[]): string[][] {
23
+ const levels: string[][] = [];
24
+ const processed = new Set<string>();
25
+
26
+ let currentLevel = steps
27
+ .filter(s => !s.dependsOn || s.dependsOn.length === 0)
28
+ .map(s => s.id);
29
+
30
+ while (currentLevel.length > 0) {
31
+ levels.push(currentLevel);
32
+ currentLevel.forEach(id => processed.add(id));
33
+
34
+ currentLevel = steps
35
+ .filter(s => !processed.has(s.id))
36
+ .filter(s => s.dependsOn && s.dependsOn.every(dep => processed.has(dep)))
37
+ .map(s => s.id);
38
+ }
39
+
40
+ return levels;
41
+ }
42
+ }
package/src/planner.ts ADDED
@@ -0,0 +1,110 @@
1
+ import type { IPlanner, IProvider, ILogger, IEventEmitter, PlanRequest, ExecutionPlan, PlanStep, AstriveError } from '@astrive-ai/types';
2
+ import { randomUUID } from 'node:crypto';
3
+
4
+ export class LLMPlanner implements IPlanner {
5
+ constructor(
6
+ private provider: IProvider,
7
+ private logger: ILogger,
8
+ private events: IEventEmitter
9
+ ) {}
10
+
11
+ async plan(request: PlanRequest): Promise<ExecutionPlan> {
12
+ this.events.emit('plan:start', { request });
13
+ this.logger.debug('Starting planning phase', { request });
14
+
15
+ const prompt = this.buildPlanningPrompt(request);
16
+
17
+ const response = await this.provider.chat({
18
+ messages: [{ role: 'user', content: prompt }]
19
+ });
20
+ const parsed = this.parsePlanResponse(response.content);
21
+
22
+ const steps: PlanStep[] = parsed.steps.map(step => ({
23
+ id: randomUUID(),
24
+ toolName: step.toolName,
25
+ arguments: step.arguments,
26
+ description: step.description,
27
+ dependsOn: step.dependsOn || [],
28
+ status: 'pending'
29
+ }));
30
+
31
+ const plan: ExecutionPlan = {
32
+ id: randomUUID(),
33
+ steps,
34
+ reasoning: parsed.reasoning,
35
+ status: 'pending',
36
+ estimatedDuration: 0
37
+ };
38
+
39
+ this.logger.debug('Planning complete', { plan });
40
+ this.events.emit('plan:end', { plan });
41
+ return plan;
42
+ }
43
+
44
+ async replan(plan: ExecutionPlan, error: AstriveError): Promise<ExecutionPlan> {
45
+ this.logger.debug('Replanning due to error', { error: error.message });
46
+ const prompt = `The previous plan failed with error: ${error.message}.\n\nOriginal Plan:\n${JSON.stringify(plan.steps, null, 2)}\n\nPlease provide a revised JSON plan that avoids this error.`;
47
+
48
+ const response = await this.provider.chat({
49
+ messages: [{ role: 'user', content: prompt }]
50
+ });
51
+ const parsed = this.parsePlanResponse(response.content);
52
+
53
+ const steps: PlanStep[] = parsed.steps.map(step => ({
54
+ id: randomUUID(),
55
+ toolName: step.toolName,
56
+ arguments: step.arguments,
57
+ description: step.description,
58
+ dependsOn: step.dependsOn || [],
59
+ status: 'pending'
60
+ }));
61
+
62
+ return {
63
+ id: randomUUID(),
64
+ steps,
65
+ reasoning: parsed.reasoning,
66
+ status: 'pending',
67
+ estimatedDuration: 0
68
+ };
69
+ }
70
+
71
+ private buildPlanningPrompt(request: PlanRequest): string {
72
+ return `You are an AI planner. Your task is to analyze the user request and return a structured JSON plan.
73
+ Available Tools:
74
+ ${JSON.stringify(request.tools, null, 2)}
75
+
76
+ User Request: ${request.prompt}
77
+
78
+ Return ONLY a JSON object with the following structure:
79
+ {
80
+ "steps": [
81
+ {
82
+ "toolName": "name_of_tool",
83
+ "arguments": { "arg1": "value" },
84
+ "description": "What this step does",
85
+ "dependsOn": [] // Optional array of step IDs this step depends on
86
+ }
87
+ ],
88
+ "reasoning": "Explanation of the plan"
89
+ }
90
+ `;
91
+ }
92
+
93
+ private parsePlanResponse(response: string): { steps: any[], reasoning: string } {
94
+ let jsonStr = response;
95
+ const match = response.match(/```(?:json)?\s*([\s\S]*?)```/);
96
+ if (match) {
97
+ jsonStr = match[1];
98
+ }
99
+
100
+ try {
101
+ const data = JSON.parse(jsonStr.trim());
102
+ return {
103
+ steps: Array.isArray(data.steps) ? data.steps : [],
104
+ reasoning: data.reasoning || ''
105
+ };
106
+ } catch (err) {
107
+ throw new Error(`Failed to parse plan response: ${err}`);
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,74 @@
1
+ import type { IPlanner, PlanRequest, ExecutionPlan, PlanStep } from '@astrive-ai/types';
2
+ import { randomUUID } from 'node:crypto';
3
+
4
+ export class SimplePlanner implements IPlanner {
5
+ async plan(request: PlanRequest): Promise<ExecutionPlan> {
6
+ const input = request.prompt.toLowerCase();
7
+ const steps: PlanStep[] = [];
8
+ let reasoning = 'Direct response';
9
+
10
+ const addStep = (toolName: string, description: string, args: any = {}) => {
11
+ steps.push({
12
+ id: randomUUID(),
13
+ toolName,
14
+ arguments: args,
15
+ description,
16
+ dependsOn: [],
17
+ status: 'pending'
18
+ });
19
+ };
20
+
21
+ if (input.includes('download')) {
22
+ addStep('downloader', 'Download media', { url: this.extractUrl(input) });
23
+ reasoning = 'User wants to download media';
24
+ } else if (input.includes('image') || input.includes('generate') || input.includes('create image')) {
25
+ addStep('texttoimage', 'Generate image', { prompt: request.prompt });
26
+ reasoning = 'User wants to generate an image';
27
+ } else if (input.includes('remove background') || input.includes('removebg')) {
28
+ addStep('removebg', 'Remove background', { image: this.extractUrl(input) });
29
+ reasoning = 'User wants to remove background';
30
+ } else if (input.includes('upscale')) {
31
+ addStep('upscaler', 'Upscale image', { image: this.extractUrl(input) });
32
+ reasoning = 'User wants to upscale an image';
33
+ } else if (input.includes('face swap')) {
34
+ addStep('faceswap', 'Swap faces', { faceImage: '', targetImage: '' });
35
+ reasoning = 'User wants to swap faces';
36
+ } else if (input.includes('search') || input.includes('find') || input.includes('look up')) {
37
+ addStep('websearch', 'Search web', { query: request.prompt });
38
+ reasoning = 'User wants to search the web';
39
+ } else if (input.includes('speech') || input.includes('say') || input.includes('speak') || input.includes('tts')) {
40
+ addStep('texttospeech', 'Text to speech', { text: request.prompt });
41
+ reasoning = 'User wants text to speech';
42
+ } else if (input.includes('transcribe') || input.includes('listen') || input.includes('stt')) {
43
+ addStep('speechtotext', 'Transcribe audio', { audio: '' });
44
+ reasoning = 'User wants audio transcription';
45
+ } else if (input.includes('vision') || input.includes('describe image') || input.includes('analyze image')) {
46
+ addStep('vision', 'Analyze image', { image: this.extractUrl(input) });
47
+ reasoning = 'User wants to analyze an image';
48
+ } else if (input.includes('ocr') || input.includes('read text') || input.includes('extract text')) {
49
+ addStep('ocr', 'Extract text', { image: this.extractUrl(input) });
50
+ reasoning = 'User wants to extract text from image';
51
+ } else if (input.includes('edit image')) {
52
+ addStep('img2img', 'Edit image', { image: this.extractUrl(input), prompt: request.prompt });
53
+ reasoning = 'User wants to edit an image';
54
+ }
55
+
56
+ return {
57
+ id: randomUUID(),
58
+ steps,
59
+ reasoning,
60
+ status: 'pending',
61
+ estimatedDuration: 0
62
+ };
63
+ }
64
+
65
+ async replan(plan: ExecutionPlan, error: any): Promise<ExecutionPlan> {
66
+ return plan;
67
+ }
68
+
69
+ private extractUrl(text: string): string {
70
+ const urlRegex = /(https?:\/\/[^\s]+)/g;
71
+ const match = text.match(urlRegex);
72
+ return match ? match[0] : '';
73
+ }
74
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }