@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.
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/plan-optimizer.d.ts +6 -0
- package/dist/plan-optimizer.d.ts.map +1 -0
- package/dist/plan-optimizer.js +30 -0
- package/dist/plan-optimizer.js.map +1 -0
- package/dist/planner.d.ts +12 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +100 -0
- package/dist/planner.js.map +1 -0
- package/dist/simple-planner.d.ts +7 -0
- package/dist/simple-planner.d.ts.map +1 -0
- package/dist/simple-planner.js +78 -0
- package/dist/simple-planner.js.map +1 -0
- package/package.json +25 -0
- package/src/index.ts +3 -0
- package/src/plan-optimizer.ts +42 -0
- package/src/planner.ts +110 -0
- package/src/simple-planner.ts +74 -0
- package/tsconfig.json +8 -0
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/planner.js
ADDED
|
@@ -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,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
|
+
}
|