@ai.ntellect/core 0.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/README.FR.md +303 -0
- package/README.md +299 -0
- package/agents/orchestrator/context.ts +35 -0
- package/agents/orchestrator/index.ts +33 -0
- package/agents/synthesizer/context.ts +50 -0
- package/agents/synthesizer/index.ts +52 -0
- package/dist/agents/orchestrator/context.js +33 -0
- package/dist/agents/orchestrator/index.js +40 -0
- package/dist/agents/synthesizer/context.js +52 -0
- package/dist/agents/synthesizer/index.js +54 -0
- package/dist/index.js +22 -0
- package/dist/memory/index.js +190 -0
- package/dist/services/queue.js +136 -0
- package/dist/types.js +14 -0
- package/dist/utils/queue-item-transformer.js +26 -0
- package/dist/workflow/index.js +187 -0
- package/index.ts +7 -0
- package/memory/index.ts +201 -0
- package/package.json +30 -0
- package/services/queue.ts +147 -0
- package/tsconfig.json +112 -0
- package/types.ts +156 -0
- package/utils/queue-item-transformer.ts +26 -0
- package/workflow/index.ts +253 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.QueueItemTransformer = void 0;
|
4
|
+
class QueueItemTransformer {
|
5
|
+
static transformActionToQueueItem(action) {
|
6
|
+
return {
|
7
|
+
name: action.name || '',
|
8
|
+
parameters: QueueItemTransformer.transformParameters(action.parameters || {})
|
9
|
+
};
|
10
|
+
}
|
11
|
+
static transformFromSimilarActions(similarActions) {
|
12
|
+
var _a;
|
13
|
+
const firstMatch = (_a = similarActions === null || similarActions === void 0 ? void 0 : similarActions[0]) === null || _a === void 0 ? void 0 : _a.data;
|
14
|
+
return firstMatch === null || firstMatch === void 0 ? void 0 : firstMatch.map((action) => QueueItemTransformer.transformActionToQueueItem(action));
|
15
|
+
}
|
16
|
+
static transformParameters(parameters) {
|
17
|
+
return Object.entries(parameters).map(([name, value]) => ({
|
18
|
+
name,
|
19
|
+
value: typeof value === 'object' ? JSON.stringify(value) : String(value)
|
20
|
+
}));
|
21
|
+
}
|
22
|
+
static transformActionsToQueueItems(actions) {
|
23
|
+
return actions === null || actions === void 0 ? void 0 : actions.map(action => this.transformActionToQueueItem(action));
|
24
|
+
}
|
25
|
+
}
|
26
|
+
exports.QueueItemTransformer = QueueItemTransformer;
|
@@ -0,0 +1,187 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.Workflow = void 0;
|
13
|
+
const synthesizer_1 = require("../agents/synthesizer");
|
14
|
+
const queue_1 = require("../services/queue");
|
15
|
+
const types_1 = require("../types");
|
16
|
+
const queue_item_transformer_1 = require("../utils/queue-item-transformer");
|
17
|
+
class Workflow {
|
18
|
+
constructor(user, dependencies) {
|
19
|
+
this.user = user;
|
20
|
+
this.dependencies = dependencies;
|
21
|
+
this.CONFIRMATION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
|
22
|
+
this.SIMILARITY_THRESHOLD = 95;
|
23
|
+
this.MAX_RESULTS = 1;
|
24
|
+
}
|
25
|
+
start(prompt, promptWithContext, stream) {
|
26
|
+
return __awaiter(this, void 0, void 0, function* () {
|
27
|
+
const request = yield this.dependencies.orchestrator.process(promptWithContext);
|
28
|
+
this.dependencies.eventEmitter.emit("orchestrator-update", {
|
29
|
+
type: "on-message",
|
30
|
+
data: request,
|
31
|
+
});
|
32
|
+
if (request.actions.length > 0) {
|
33
|
+
return this.handleActions(prompt, request.actions);
|
34
|
+
}
|
35
|
+
});
|
36
|
+
}
|
37
|
+
handleActions(prompt, actions) {
|
38
|
+
return __awaiter(this, void 0, void 0, function* () {
|
39
|
+
let predefinedActions = actions;
|
40
|
+
console.log("\nš Predefined actions:", predefinedActions);
|
41
|
+
const similarActions = yield this.dependencies.memoryCache.findBestMatches(prompt, {
|
42
|
+
similarityThreshold: this.SIMILARITY_THRESHOLD,
|
43
|
+
maxResults: this.MAX_RESULTS,
|
44
|
+
userId: this.user.id,
|
45
|
+
scope: types_1.MemoryScope.USER,
|
46
|
+
});
|
47
|
+
console.log("\nš Similar actions:", similarActions);
|
48
|
+
predefinedActions =
|
49
|
+
queue_item_transformer_1.QueueItemTransformer.transformActionsToQueueItems(predefinedActions) ||
|
50
|
+
[];
|
51
|
+
console.log("\nš Transformed predefined actions:", predefinedActions);
|
52
|
+
if (similarActions && similarActions.length > 0) {
|
53
|
+
predefinedActions =
|
54
|
+
queue_item_transformer_1.QueueItemTransformer.transformFromSimilarActions(similarActions) || [];
|
55
|
+
console.log("\nš Transformed similar actions:", predefinedActions);
|
56
|
+
}
|
57
|
+
console.log("\nš Final actions:", predefinedActions);
|
58
|
+
const callbacks = this.createCallbacks(prompt, similarActions);
|
59
|
+
console.log("\nš Queue prepared");
|
60
|
+
const actionsResult = yield this.executeActions(predefinedActions, this.dependencies.orchestrator.tools, callbacks);
|
61
|
+
console.log("\nš Actions result:", actionsResult);
|
62
|
+
return this.handleActionResults(Object.assign(Object.assign({}, actionsResult), { initialPrompt: prompt }), prompt);
|
63
|
+
});
|
64
|
+
}
|
65
|
+
executeActions(predefinedActions, tools, callbacks) {
|
66
|
+
return __awaiter(this, void 0, void 0, function* () {
|
67
|
+
try {
|
68
|
+
const queueManager = new queue_1.ActionQueueManager(tools, {
|
69
|
+
onActionStart: callbacks === null || callbacks === void 0 ? void 0 : callbacks.onActionStart,
|
70
|
+
onActionComplete: callbacks === null || callbacks === void 0 ? void 0 : callbacks.onActionComplete,
|
71
|
+
onQueueComplete: callbacks === null || callbacks === void 0 ? void 0 : callbacks.onQueueComplete,
|
72
|
+
onConfirmationRequired: (message) => __awaiter(this, void 0, void 0, function* () {
|
73
|
+
if (callbacks === null || callbacks === void 0 ? void 0 : callbacks.onConfirmationRequired) {
|
74
|
+
return yield callbacks.onConfirmationRequired(message);
|
75
|
+
}
|
76
|
+
return false;
|
77
|
+
}),
|
78
|
+
});
|
79
|
+
queueManager.addToQueue(predefinedActions);
|
80
|
+
if (callbacks === null || callbacks === void 0 ? void 0 : callbacks.onQueueStart) {
|
81
|
+
callbacks.onQueueStart(predefinedActions);
|
82
|
+
}
|
83
|
+
console.log("Processing queue...");
|
84
|
+
const results = yield queueManager.processQueue();
|
85
|
+
console.log("Queue completed:");
|
86
|
+
console.dir(results, { depth: null });
|
87
|
+
return {
|
88
|
+
type: "success",
|
89
|
+
data: results || [],
|
90
|
+
};
|
91
|
+
}
|
92
|
+
catch (error) {
|
93
|
+
console.error("Error processing prompt:", error);
|
94
|
+
throw error;
|
95
|
+
}
|
96
|
+
});
|
97
|
+
}
|
98
|
+
createCallbacks(prompt, similarActions) {
|
99
|
+
return {
|
100
|
+
onQueueStart: (actions) => __awaiter(this, void 0, void 0, function* () {
|
101
|
+
console.dir(actions, { depth: null });
|
102
|
+
this.dependencies.eventEmitter.emit("orchestrator-update", {
|
103
|
+
type: "queue-start",
|
104
|
+
actions,
|
105
|
+
});
|
106
|
+
}),
|
107
|
+
onActionStart: (action) => {
|
108
|
+
this.dependencies.eventEmitter.emit("orchestrator-update", {
|
109
|
+
type: "action-start",
|
110
|
+
action: action.name,
|
111
|
+
args: action.parameters,
|
112
|
+
});
|
113
|
+
},
|
114
|
+
onActionComplete: (action) => {
|
115
|
+
this.dependencies.eventEmitter.emit("orchestrator-update", {
|
116
|
+
type: "action-complete",
|
117
|
+
action: action.name,
|
118
|
+
result: action.result,
|
119
|
+
});
|
120
|
+
},
|
121
|
+
onQueueComplete: (actions) => __awaiter(this, void 0, void 0, function* () {
|
122
|
+
if (!similarActions.length) {
|
123
|
+
yield this.saveToMemory(prompt, actions);
|
124
|
+
}
|
125
|
+
this.dependencies.eventEmitter.emit("orchestrator-update", {
|
126
|
+
type: "queue-complete",
|
127
|
+
});
|
128
|
+
}),
|
129
|
+
onConfirmationRequired: this.handleConfirmationRequest.bind(this),
|
130
|
+
};
|
131
|
+
}
|
132
|
+
handleConfirmationRequest(message) {
|
133
|
+
return __awaiter(this, void 0, void 0, function* () {
|
134
|
+
return new Promise((resolve) => {
|
135
|
+
const confirmationId = Date.now().toString();
|
136
|
+
const handleConfirmation = (data) => {
|
137
|
+
if (data.confirmationId === confirmationId) {
|
138
|
+
this.dependencies.eventEmitter.removeListener("confirmation-response", handleConfirmation);
|
139
|
+
resolve(data.confirmed);
|
140
|
+
}
|
141
|
+
};
|
142
|
+
this.dependencies.eventEmitter.once("confirmation-response", handleConfirmation);
|
143
|
+
this.dependencies.eventEmitter.emit("orchestrator-update", {
|
144
|
+
type: "confirmation-required",
|
145
|
+
id: confirmationId,
|
146
|
+
message,
|
147
|
+
});
|
148
|
+
setTimeout(() => {
|
149
|
+
this.dependencies.eventEmitter.removeListener("confirmation-response", handleConfirmation);
|
150
|
+
resolve(false);
|
151
|
+
}, this.CONFIRMATION_TIMEOUT);
|
152
|
+
});
|
153
|
+
});
|
154
|
+
}
|
155
|
+
saveToMemory(prompt, actions) {
|
156
|
+
return __awaiter(this, void 0, void 0, function* () {
|
157
|
+
console.log("\nš Creating memory...");
|
158
|
+
yield this.dependencies.memoryCache.createMemory({
|
159
|
+
content: prompt,
|
160
|
+
userId: this.user.id,
|
161
|
+
scope: types_1.MemoryScope.USER,
|
162
|
+
type: types_1.MemoryType.ACTION,
|
163
|
+
data: actions,
|
164
|
+
});
|
165
|
+
});
|
166
|
+
}
|
167
|
+
handleActionResults(actionsResult_1, prompt_1) {
|
168
|
+
return __awaiter(this, arguments, void 0, function* (actionsResult, prompt, stream = true) {
|
169
|
+
if (!this.hasNonPrepareActions(actionsResult.data)) {
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
const summarizer = new synthesizer_1.Summarizer();
|
173
|
+
const summaryData = JSON.stringify({
|
174
|
+
result: actionsResult.data,
|
175
|
+
initialPrompt: prompt,
|
176
|
+
});
|
177
|
+
return stream
|
178
|
+
? (yield summarizer.streamProcess(summaryData)).toDataStreamResponse()
|
179
|
+
: yield summarizer.process(summaryData);
|
180
|
+
});
|
181
|
+
}
|
182
|
+
hasNonPrepareActions(actions) {
|
183
|
+
return (Array.isArray(actions) &&
|
184
|
+
actions.some((action) => { var _a; return ((_a = action.name) === null || _a === void 0 ? void 0 : _a.split("-")[0]) !== "prepare"; }));
|
185
|
+
}
|
186
|
+
}
|
187
|
+
exports.Workflow = Workflow;
|
package/index.ts
ADDED
package/memory/index.ts
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
import { openai } from "@ai-sdk/openai";
|
2
|
+
import { cosineSimilarity, embed, embedMany, generateObject } from "ai";
|
3
|
+
import { createClient } from 'redis';
|
4
|
+
import { z } from "zod";
|
5
|
+
import { CreateMemoryInput, MatchOptions, Memory, MemoryCacheOptions, MemoryScope } from "../types";
|
6
|
+
|
7
|
+
export class MemoryCache {
|
8
|
+
private redis;
|
9
|
+
private readonly CACHE_PREFIX: string;
|
10
|
+
private readonly CACHE_TTL: number;
|
11
|
+
|
12
|
+
constructor(options: MemoryCacheOptions = {}) {
|
13
|
+
const ttlInHours = options.cacheTTL ?? 1;
|
14
|
+
this.CACHE_TTL = ttlInHours * 60 * 60;
|
15
|
+
this.CACHE_PREFIX = options.cachePrefix ?? 'memory:';
|
16
|
+
|
17
|
+
this.redis = createClient({
|
18
|
+
url: options.redisUrl || process.env.REDIS_URL,
|
19
|
+
socket: {
|
20
|
+
tls: true,
|
21
|
+
rejectUnauthorized: true
|
22
|
+
}
|
23
|
+
});
|
24
|
+
this.initRedis();
|
25
|
+
}
|
26
|
+
|
27
|
+
private async initRedis() {
|
28
|
+
this.redis.on('error', err => {
|
29
|
+
console.error('Redis Client Error:', err);
|
30
|
+
// Implement retry logic if needed
|
31
|
+
});
|
32
|
+
|
33
|
+
try {
|
34
|
+
await this.redis.connect();
|
35
|
+
console.log('Successfully connected to Redis');
|
36
|
+
} catch (error) {
|
37
|
+
console.error('Failed to connect to Redis:', error);
|
38
|
+
// Handle connection failure
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
private getMemoryKey(scope: MemoryScope, userId?: string): string {
|
43
|
+
if (scope === MemoryScope.GLOBAL) {
|
44
|
+
return `${this.CACHE_PREFIX}global:`;
|
45
|
+
}
|
46
|
+
return `${this.CACHE_PREFIX}user:${userId}:`;
|
47
|
+
}
|
48
|
+
|
49
|
+
private async storeMemory(memory: Memory) {
|
50
|
+
const prefix = this.getMemoryKey(memory.scope, memory.userId);
|
51
|
+
const key = `${prefix}${memory.id}`;
|
52
|
+
await this.redis.set(key, JSON.stringify(memory), {
|
53
|
+
EX: this.CACHE_TTL
|
54
|
+
});
|
55
|
+
}
|
56
|
+
|
57
|
+
async findBestMatches(
|
58
|
+
query: string,
|
59
|
+
options: MatchOptions & { userId?: string; scope?: MemoryScope } = {}
|
60
|
+
): Promise<{
|
61
|
+
data: any;
|
62
|
+
similarityPercentage: number;
|
63
|
+
purpose: string;
|
64
|
+
}[]> {
|
65
|
+
console.log("\nš Searching for query:", query);
|
66
|
+
|
67
|
+
const { embedding } = await embed({
|
68
|
+
model: openai.embedding("text-embedding-3-small"),
|
69
|
+
value: query
|
70
|
+
});
|
71
|
+
|
72
|
+
const memories = await this.getAllMemories(options.scope, options.userId);
|
73
|
+
console.log("\nš Found", memories.length, "memories to compare with");
|
74
|
+
|
75
|
+
const matches = memories
|
76
|
+
.map(memory => {
|
77
|
+
const similarities = memory.embeddings.map(emb => {
|
78
|
+
const similarity = cosineSimilarity(embedding, emb);
|
79
|
+
|
80
|
+
return (similarity + 1) * 50; // Convert to percentage
|
81
|
+
});
|
82
|
+
|
83
|
+
const maxSimilarity = Math.max(...similarities);
|
84
|
+
console.log(`\nš Memory "${memory.purpose}":
|
85
|
+
- Similarity: ${maxSimilarity.toFixed(2)}%
|
86
|
+
- Original queries: ${memory.queries.join(", ")}`);
|
87
|
+
|
88
|
+
return {
|
89
|
+
data: memory.data,
|
90
|
+
similarityPercentage: maxSimilarity,
|
91
|
+
purpose: memory.purpose,
|
92
|
+
};
|
93
|
+
})
|
94
|
+
.filter(match => match.similarityPercentage >= (options.similarityThreshold ?? 70))
|
95
|
+
.sort((a, b) => b.similarityPercentage - a.similarityPercentage);
|
96
|
+
|
97
|
+
const results = options.maxResults ? matches.slice(0, options.maxResults) : matches;
|
98
|
+
|
99
|
+
if (results.length > 0) {
|
100
|
+
console.log("\n⨠Best matches found:");
|
101
|
+
results.forEach(match => {
|
102
|
+
console.log(`- ${match.purpose} (${match.similarityPercentage.toFixed(2)}%)`);
|
103
|
+
});
|
104
|
+
} else {
|
105
|
+
console.log("No matches found");
|
106
|
+
}
|
107
|
+
|
108
|
+
console.dir({results});
|
109
|
+
return results;
|
110
|
+
}
|
111
|
+
|
112
|
+
private async getAllMemories(scope?: MemoryScope, userId?: string): Promise<Memory[]> {
|
113
|
+
let patterns: Memory[] = [];
|
114
|
+
|
115
|
+
if (!scope || scope === MemoryScope.GLOBAL) {
|
116
|
+
const globalPrefix = this.getMemoryKey(MemoryScope.GLOBAL);
|
117
|
+
const globalKeys = await this.redis.keys(`${globalPrefix}*`);
|
118
|
+
const globalPatterns = await this.getMemoriesFromKeys(globalKeys);
|
119
|
+
patterns = patterns.concat(globalPatterns);
|
120
|
+
}
|
121
|
+
|
122
|
+
if (userId && (!scope || scope === MemoryScope.USER)) {
|
123
|
+
const userPrefix = this.getMemoryKey(MemoryScope.USER, userId);
|
124
|
+
const userKeys = await this.redis.keys(`${userPrefix}*`);
|
125
|
+
const userPatterns = await this.getMemoriesFromKeys(userKeys);
|
126
|
+
patterns = patterns.concat(userPatterns);
|
127
|
+
}
|
128
|
+
|
129
|
+
return patterns;
|
130
|
+
}
|
131
|
+
|
132
|
+
private async getMemoriesFromKeys(keys: string[]): Promise<Memory[]> {
|
133
|
+
const memories: Memory[] = [];
|
134
|
+
for (const key of keys) {
|
135
|
+
const data = await this.redis.get(key);
|
136
|
+
if (data) {
|
137
|
+
memories.push(JSON.parse(data));
|
138
|
+
}
|
139
|
+
}
|
140
|
+
return memories;
|
141
|
+
}
|
142
|
+
|
143
|
+
public async createMemory(input: CreateMemoryInput): Promise<string | undefined> {
|
144
|
+
const { embedding } = await embed({
|
145
|
+
model: openai.embedding("text-embedding-3-small"),
|
146
|
+
value: input.content
|
147
|
+
});
|
148
|
+
|
149
|
+
const existingPattern = await this.findBestMatches(input.content, {
|
150
|
+
similarityThreshold: 95,
|
151
|
+
userId: input.userId,
|
152
|
+
scope: input.scope
|
153
|
+
});
|
154
|
+
|
155
|
+
if (existingPattern.length > 0) {
|
156
|
+
console.log("\nš Similar memory found:");
|
157
|
+
// Display only the name and similarity percentage
|
158
|
+
existingPattern.forEach(match => {
|
159
|
+
console.log(`- ${match.purpose} (${match.similarityPercentage.toFixed(2)}%)`);
|
160
|
+
});
|
161
|
+
return;
|
162
|
+
}
|
163
|
+
|
164
|
+
const variations = await generateObject({
|
165
|
+
model: openai("gpt-4"),
|
166
|
+
schema: z.object({
|
167
|
+
request: z.string().describe("The request to be performed"),
|
168
|
+
queries: z.array(z.object({
|
169
|
+
text: z.string()
|
170
|
+
}))
|
171
|
+
}),
|
172
|
+
prompt: `For this input: "${input.content}"
|
173
|
+
Generate similar variations that should match the same context.
|
174
|
+
Context type: ${input.type}
|
175
|
+
Data: ${JSON.stringify(input.data)}
|
176
|
+
- Keep variations natural and human-like
|
177
|
+
- Include the original input
|
178
|
+
- Add 3-5 variations`
|
179
|
+
});
|
180
|
+
|
181
|
+
const embeddingResults = await embedMany({
|
182
|
+
model: openai.embedding("text-embedding-3-small"),
|
183
|
+
values: variations.object.queries.map(q => q.text)
|
184
|
+
});
|
185
|
+
|
186
|
+
const memory: Memory = {
|
187
|
+
id: crypto.randomUUID(),
|
188
|
+
type: input.type,
|
189
|
+
data: input.data,
|
190
|
+
purpose: variations.object.request,
|
191
|
+
queries: [input.content, ...variations.object.queries.map(q => q.text)],
|
192
|
+
embeddings: [embedding, ...embeddingResults.embeddings],
|
193
|
+
userId: input.userId,
|
194
|
+
scope: input.scope || (input.userId ? MemoryScope.USER : MemoryScope.GLOBAL),
|
195
|
+
createdAt: new Date()
|
196
|
+
};
|
197
|
+
|
198
|
+
await this.storeMemory(memory);
|
199
|
+
return variations.object.request;
|
200
|
+
}
|
201
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"name": "@ai.ntellect/core",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"description": "",
|
5
|
+
"main": "dist/index.js",
|
6
|
+
"scripts": {
|
7
|
+
"build": "rm -rf dist && tsc",
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
9
|
+
},
|
10
|
+
"keywords": [],
|
11
|
+
"author": "Lorcann Rauzduel",
|
12
|
+
"license": "ISC",
|
13
|
+
"dependencies": {
|
14
|
+
"@ai-sdk/openai": "1.0.6",
|
15
|
+
"ai": "^3.0.0",
|
16
|
+
"redis": "^4.7.0",
|
17
|
+
"zod": "^3.24.1"
|
18
|
+
},
|
19
|
+
"devDependencies": {
|
20
|
+
"typescript": "^5.7.2"
|
21
|
+
},
|
22
|
+
"repository": {
|
23
|
+
"type": "git",
|
24
|
+
"url": "git+https://github.com/ai.ntellect/core.git"
|
25
|
+
},
|
26
|
+
"bugs": {
|
27
|
+
"url": "https://github.com/ai.ntellect/core/issues"
|
28
|
+
},
|
29
|
+
"homepage": "https://github.com/ai.ntellect/core#readme"
|
30
|
+
}
|
@@ -0,0 +1,147 @@
|
|
1
|
+
import {
|
2
|
+
ActionSchema,
|
3
|
+
QueueCallbacks,
|
4
|
+
QueueItem,
|
5
|
+
QueueItemParameter,
|
6
|
+
QueueResult,
|
7
|
+
} from "../types";
|
8
|
+
|
9
|
+
export class ActionQueueManager {
|
10
|
+
private queue: QueueItem[] = [];
|
11
|
+
private results: QueueResult[] = [];
|
12
|
+
private callbacks: QueueCallbacks;
|
13
|
+
private actions: ActionSchema[];
|
14
|
+
private isProcessing: boolean = false;
|
15
|
+
|
16
|
+
constructor(actions: ActionSchema[], callbacks: QueueCallbacks = {}) {
|
17
|
+
this.actions = actions;
|
18
|
+
this.callbacks = callbacks;
|
19
|
+
}
|
20
|
+
|
21
|
+
addToQueue(actions: QueueItem | QueueItem[]) {
|
22
|
+
if (Array.isArray(actions)) {
|
23
|
+
console.log(
|
24
|
+
"Adding actions to queue:",
|
25
|
+
actions.map((a) => a.name).join(", ")
|
26
|
+
);
|
27
|
+
this.queue.push(...actions);
|
28
|
+
} else {
|
29
|
+
console.log("Adding action to queue:", actions.name);
|
30
|
+
this.queue.push(actions);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
async processQueue() {
|
35
|
+
if (this.isProcessing) {
|
36
|
+
console.warn("Queue is already being processed");
|
37
|
+
return;
|
38
|
+
}
|
39
|
+
|
40
|
+
this.isProcessing = true;
|
41
|
+
const actionPromises = [];
|
42
|
+
|
43
|
+
for (const action of this.queue) {
|
44
|
+
const actionConfig = this.actions.find((a) => a.name === action.name);
|
45
|
+
|
46
|
+
if (actionConfig?.confirmation?.requireConfirmation) {
|
47
|
+
// Wait for user confirmation before executing this action
|
48
|
+
const shouldProceed = await this.callbacks.onConfirmationRequired?.(
|
49
|
+
actionConfig.confirmation.message ||
|
50
|
+
`Do you want to proceed with action: ${action.name}?`
|
51
|
+
);
|
52
|
+
|
53
|
+
if (!shouldProceed) {
|
54
|
+
// Skip this action and add a cancelled result
|
55
|
+
this.results.push({
|
56
|
+
name: action.name,
|
57
|
+
parameters: this.formatArguments(action.parameters),
|
58
|
+
result: null,
|
59
|
+
error: "Action cancelled by user",
|
60
|
+
cancelled: true,
|
61
|
+
});
|
62
|
+
continue;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
actionPromises.push(
|
67
|
+
this.executeAction(action)
|
68
|
+
.then((result) => {
|
69
|
+
this.callbacks.onActionComplete?.(result);
|
70
|
+
return result;
|
71
|
+
})
|
72
|
+
.catch((error) => {
|
73
|
+
const result = {
|
74
|
+
name: action.name,
|
75
|
+
parameters: this.formatArguments(action.parameters),
|
76
|
+
result: null,
|
77
|
+
error: error.message || "Unknown error occurred",
|
78
|
+
};
|
79
|
+
this.callbacks.onActionComplete?.(result);
|
80
|
+
return result;
|
81
|
+
})
|
82
|
+
);
|
83
|
+
}
|
84
|
+
|
85
|
+
try {
|
86
|
+
const results = await Promise.all(actionPromises);
|
87
|
+
this.results.push(...results);
|
88
|
+
this.queue = [];
|
89
|
+
this.callbacks.onQueueComplete?.(this.results);
|
90
|
+
this.isProcessing = false;
|
91
|
+
return this.results;
|
92
|
+
} catch (error) {
|
93
|
+
this.isProcessing = false;
|
94
|
+
console.error("Unexpected error in queue processing:", error);
|
95
|
+
throw error;
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
private formatArguments(args: QueueItemParameter[]): Record<string, string> {
|
100
|
+
return args.reduce<Record<string, string>>((acc, arg) => {
|
101
|
+
acc[arg.name] = arg.value;
|
102
|
+
return acc;
|
103
|
+
}, {});
|
104
|
+
}
|
105
|
+
|
106
|
+
private async executeAction(action: QueueItem): Promise<QueueResult> {
|
107
|
+
// Call onActionStart callback
|
108
|
+
this.callbacks.onActionStart?.(action);
|
109
|
+
const actionConfig = this.actions.find((a) => a.name === action.name);
|
110
|
+
if (!actionConfig) {
|
111
|
+
return {
|
112
|
+
name: action.name,
|
113
|
+
parameters: {},
|
114
|
+
result: null,
|
115
|
+
error: `Action '${action.name}' not found in actions list`,
|
116
|
+
};
|
117
|
+
}
|
118
|
+
|
119
|
+
const actionArgs = action.parameters.reduce<Record<string, string>>(
|
120
|
+
(acc: Record<string, string>, arg: QueueItemParameter) => {
|
121
|
+
acc[arg.name] = arg.value;
|
122
|
+
return acc;
|
123
|
+
},
|
124
|
+
{}
|
125
|
+
);
|
126
|
+
|
127
|
+
console.log(`Executing ${action.name} with args:`, actionArgs);
|
128
|
+
|
129
|
+
try {
|
130
|
+
const result = await actionConfig.execute(actionArgs);
|
131
|
+
return {
|
132
|
+
name: action.name,
|
133
|
+
parameters: actionArgs,
|
134
|
+
result,
|
135
|
+
error: null,
|
136
|
+
};
|
137
|
+
} catch (error) {
|
138
|
+
console.error(`Error executing action ${action.name}:`, error);
|
139
|
+
return {
|
140
|
+
name: action.name,
|
141
|
+
parameters: actionArgs,
|
142
|
+
result: null,
|
143
|
+
error: (error as Error).message || "Unknown error occurred",
|
144
|
+
};
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|