@ai.ntellect/core 0.0.25 → 0.0.27
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/agent/handlers/ActionHandler.d.ts +8 -0
- package/dist/agent/handlers/ActionHandler.js +36 -0
- package/dist/agent/handlers/ConfirmationHandler.d.ts +7 -0
- package/dist/agent/handlers/ConfirmationHandler.js +31 -0
- package/dist/agent/handlers/EventHandler.d.ts +10 -0
- package/dist/agent/handlers/EventHandler.js +34 -0
- package/dist/agent/index.d.ts +22 -0
- package/dist/agent/index.js +73 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/dist/llm/synthesizer/context.d.ts +10 -0
- package/dist/llm/synthesizer/context.js +52 -0
- package/dist/llm/synthesizer/index.d.ts +14 -0
- package/dist/llm/synthesizer/index.js +42 -0
- package/dist/memory/index.d.ts +21 -0
- package/dist/memory/index.js +168 -0
- package/dist/services/queue.d.ts +13 -0
- package/dist/services/queue.js +124 -0
- package/dist/utils/queue-item-transformer.d.ts +7 -0
- package/dist/utils/queue-item-transformer.js +25 -0
- package/llm/orchestrator/context.ts +6 -2
- package/llm/orchestrator/index.ts +1 -1
- package/package.json +1 -1
- package/tsconfig.json +1 -2
- package/dist/test/llm/orchestrator.test.d.ts +0 -1
- package/dist/test/llm/orchestrator.test.js +0 -40
- package/dist/test/llm/synthesizer.test.d.ts +0 -0
- package/dist/test/llm/synthesizer.test.js +0 -27
- package/dist/test/services/queue.test.d.ts +0 -0
- package/dist/test/services/queue.test.js +0 -39
@@ -0,0 +1,8 @@
|
|
1
|
+
import { ActionSchema, ProcessPromptCallbacks, QueueItem, QueueResult } from "../../types";
|
2
|
+
export declare class ActionHandler {
|
3
|
+
executeActions(predefinedActions: QueueItem[], tools: ActionSchema[], callbacks?: ProcessPromptCallbacks): Promise<{
|
4
|
+
type: string;
|
5
|
+
data: QueueResult[];
|
6
|
+
}>;
|
7
|
+
hasNonPrepareActions(actions: QueueResult[]): boolean;
|
8
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.ActionHandler = void 0;
|
4
|
+
const queue_1 = require("../../services/queue");
|
5
|
+
class ActionHandler {
|
6
|
+
async executeActions(predefinedActions, tools, callbacks) {
|
7
|
+
try {
|
8
|
+
const queueManager = new queue_1.ActionQueueManager(tools, {
|
9
|
+
onActionStart: callbacks?.onActionStart,
|
10
|
+
onActionComplete: callbacks?.onActionComplete,
|
11
|
+
onQueueComplete: callbacks?.onQueueComplete,
|
12
|
+
onConfirmationRequired: async (message) => {
|
13
|
+
if (callbacks?.onConfirmationRequired) {
|
14
|
+
return await callbacks.onConfirmationRequired(message);
|
15
|
+
}
|
16
|
+
return false;
|
17
|
+
},
|
18
|
+
});
|
19
|
+
queueManager.addToQueue(predefinedActions);
|
20
|
+
if (callbacks?.onQueueStart) {
|
21
|
+
callbacks.onQueueStart(predefinedActions);
|
22
|
+
}
|
23
|
+
const results = await queueManager.processQueue();
|
24
|
+
return { type: "success", data: results || [] };
|
25
|
+
}
|
26
|
+
catch (error) {
|
27
|
+
console.error("Error processing prompt:", error);
|
28
|
+
throw error;
|
29
|
+
}
|
30
|
+
}
|
31
|
+
hasNonPrepareActions(actions) {
|
32
|
+
return (Array.isArray(actions) &&
|
33
|
+
actions.some((action) => action.name?.split("-")[0] !== "prepare"));
|
34
|
+
}
|
35
|
+
}
|
36
|
+
exports.ActionHandler = ActionHandler;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.ConfirmationHandler = void 0;
|
4
|
+
class ConfirmationHandler {
|
5
|
+
constructor(eventEmitter) {
|
6
|
+
this.eventEmitter = eventEmitter;
|
7
|
+
this.CONFIRMATION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
|
8
|
+
}
|
9
|
+
async handleConfirmationRequest(message) {
|
10
|
+
return new Promise((resolve) => {
|
11
|
+
const confirmationId = Date.now().toString();
|
12
|
+
const handleConfirmation = (data) => {
|
13
|
+
if (data.confirmationId === confirmationId) {
|
14
|
+
this.eventEmitter.removeListener("confirmation-response", handleConfirmation);
|
15
|
+
resolve(data.confirmed);
|
16
|
+
}
|
17
|
+
};
|
18
|
+
this.eventEmitter.once("confirmation-response", handleConfirmation);
|
19
|
+
this.eventEmitter.emit("orchestrator-update", {
|
20
|
+
type: "confirmation-required",
|
21
|
+
id: confirmationId,
|
22
|
+
message,
|
23
|
+
});
|
24
|
+
setTimeout(() => {
|
25
|
+
this.eventEmitter.removeListener("confirmation-response", handleConfirmation);
|
26
|
+
resolve(false);
|
27
|
+
}, this.CONFIRMATION_TIMEOUT);
|
28
|
+
});
|
29
|
+
}
|
30
|
+
}
|
31
|
+
exports.ConfirmationHandler = ConfirmationHandler;
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import EventEmitter from "events";
|
2
|
+
import { IEventHandler, QueueItem, QueueResult } from "../../types";
|
3
|
+
export declare class EventHandler implements IEventHandler {
|
4
|
+
private readonly eventEmitter;
|
5
|
+
constructor(eventEmitter: EventEmitter);
|
6
|
+
emitQueueStart(actions: QueueItem[]): void;
|
7
|
+
emitActionStart(action: QueueItem): void;
|
8
|
+
emitActionComplete(action: QueueResult): void;
|
9
|
+
emitQueueComplete(): void;
|
10
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.EventHandler = void 0;
|
4
|
+
class EventHandler {
|
5
|
+
constructor(eventEmitter) {
|
6
|
+
this.eventEmitter = eventEmitter;
|
7
|
+
}
|
8
|
+
emitQueueStart(actions) {
|
9
|
+
this.eventEmitter.emit("orchestrator-update", {
|
10
|
+
type: "queue-start",
|
11
|
+
actions,
|
12
|
+
});
|
13
|
+
}
|
14
|
+
emitActionStart(action) {
|
15
|
+
this.eventEmitter.emit("orchestrator-update", {
|
16
|
+
type: "action-start",
|
17
|
+
action: action.name,
|
18
|
+
args: action.parameters,
|
19
|
+
});
|
20
|
+
}
|
21
|
+
emitActionComplete(action) {
|
22
|
+
this.eventEmitter.emit("orchestrator-update", {
|
23
|
+
type: "action-complete",
|
24
|
+
action: action.name,
|
25
|
+
result: action.result,
|
26
|
+
});
|
27
|
+
}
|
28
|
+
emitQueueComplete() {
|
29
|
+
this.eventEmitter.emit("orchestrator-update", {
|
30
|
+
type: "queue-complete",
|
31
|
+
});
|
32
|
+
}
|
33
|
+
}
|
34
|
+
exports.EventHandler = EventHandler;
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import EventEmitter from "events";
|
2
|
+
import { Orchestrator } from "../llm/orchestrator";
|
3
|
+
import { MemoryCache } from "../memory";
|
4
|
+
import { AgentEvent, User } from "../types";
|
5
|
+
export declare class Agent {
|
6
|
+
private readonly user;
|
7
|
+
private readonly dependencies;
|
8
|
+
private readonly stream;
|
9
|
+
private readonly SIMILARITY_THRESHOLD;
|
10
|
+
private readonly MAX_RESULTS;
|
11
|
+
private readonly actionHandler;
|
12
|
+
constructor(user: User, dependencies: {
|
13
|
+
orchestrator: Orchestrator;
|
14
|
+
memoryCache: MemoryCache;
|
15
|
+
eventEmitter: EventEmitter;
|
16
|
+
}, stream?: boolean);
|
17
|
+
start(prompt: string, contextualizedPrompt: string, events: AgentEvent): Promise<any>;
|
18
|
+
private handleActions;
|
19
|
+
private findSimilarActions;
|
20
|
+
private transformActions;
|
21
|
+
private handleActionResults;
|
22
|
+
}
|
@@ -0,0 +1,73 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.Agent = void 0;
|
4
|
+
const synthesizer_1 = require("../llm/synthesizer");
|
5
|
+
const types_1 = require("../types");
|
6
|
+
const queue_item_transformer_1 = require("../utils/queue-item-transformer");
|
7
|
+
const ActionHandler_1 = require("./handlers/ActionHandler");
|
8
|
+
class Agent {
|
9
|
+
constructor(user, dependencies, stream = true) {
|
10
|
+
this.user = user;
|
11
|
+
this.dependencies = dependencies;
|
12
|
+
this.stream = stream;
|
13
|
+
this.SIMILARITY_THRESHOLD = 95;
|
14
|
+
this.MAX_RESULTS = 1;
|
15
|
+
this.actionHandler = new ActionHandler_1.ActionHandler();
|
16
|
+
}
|
17
|
+
async start(prompt, contextualizedPrompt, events) {
|
18
|
+
const request = await this.dependencies.orchestrator.process(contextualizedPrompt);
|
19
|
+
events.onMessage?.(request);
|
20
|
+
if (request.actions.length > 0) {
|
21
|
+
return this.handleActions({
|
22
|
+
initialPrompt: prompt,
|
23
|
+
actions: request.actions,
|
24
|
+
}, events);
|
25
|
+
}
|
26
|
+
}
|
27
|
+
async handleActions({ initialPrompt, actions, }, events) {
|
28
|
+
const similarActions = await this.findSimilarActions(initialPrompt);
|
29
|
+
const predefinedActions = this.transformActions(actions, similarActions);
|
30
|
+
const callbacks = {
|
31
|
+
onQueueStart: events.onQueueStart,
|
32
|
+
onActionStart: events.onActionStart,
|
33
|
+
onActionComplete: events.onActionComplete,
|
34
|
+
onQueueComplete: events.onQueueComplete,
|
35
|
+
onConfirmationRequired: events.onConfirmationRequired,
|
36
|
+
};
|
37
|
+
const actionsResult = await this.actionHandler.executeActions(predefinedActions, this.dependencies.orchestrator.tools, callbacks);
|
38
|
+
if (!this.actionHandler.hasNonPrepareActions(actionsResult.data)) {
|
39
|
+
return {
|
40
|
+
data: actionsResult.data,
|
41
|
+
initialPrompt,
|
42
|
+
};
|
43
|
+
}
|
44
|
+
return this.handleActionResults({ ...actionsResult, initialPrompt });
|
45
|
+
}
|
46
|
+
async findSimilarActions(prompt) {
|
47
|
+
return this.dependencies.memoryCache.findBestMatches(prompt, {
|
48
|
+
similarityThreshold: this.SIMILARITY_THRESHOLD,
|
49
|
+
maxResults: this.MAX_RESULTS,
|
50
|
+
userId: this.user.id,
|
51
|
+
scope: types_1.MemoryScope.USER,
|
52
|
+
});
|
53
|
+
}
|
54
|
+
transformActions(actions, similarActions) {
|
55
|
+
let predefinedActions = queue_item_transformer_1.QueueItemTransformer.transformActionsToQueueItems(actions) || [];
|
56
|
+
if (similarActions?.length > 0) {
|
57
|
+
predefinedActions =
|
58
|
+
queue_item_transformer_1.QueueItemTransformer.transformFromSimilarActions(similarActions) || [];
|
59
|
+
}
|
60
|
+
return predefinedActions;
|
61
|
+
}
|
62
|
+
async handleActionResults(actionsResult) {
|
63
|
+
const summarizer = new synthesizer_1.Summarizer();
|
64
|
+
const summaryData = JSON.stringify({
|
65
|
+
result: actionsResult.data,
|
66
|
+
initialPrompt: actionsResult.initialPrompt,
|
67
|
+
});
|
68
|
+
return this.stream
|
69
|
+
? (await summarizer.streamProcess(summaryData)).toDataStreamResponse()
|
70
|
+
: await summarizer.process(summaryData);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
exports.Agent = Agent;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
15
|
+
};
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
17
|
+
__exportStar(require("./agent"), exports);
|
18
|
+
__exportStar(require("./llm/orchestrator"), exports);
|
19
|
+
__exportStar(require("./llm/synthesizer"), exports);
|
20
|
+
__exportStar(require("./services/queue"), exports);
|
21
|
+
__exportStar(require("./types"), exports);
|
22
|
+
__exportStar(require("./memory"), exports);
|
@@ -0,0 +1,52 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.summarizerContext = void 0;
|
4
|
+
exports.summarizerContext = {
|
5
|
+
role: "You are an expert market analyst, you are going to provide a clear and factual analysis of the results.",
|
6
|
+
guidelines: {
|
7
|
+
important: [
|
8
|
+
"IMPORTANT: AVOID MULTIPLE UPPERCASE IN TITLE/SUBTITLE LIKE ('Market Sentiment: Bullish'). USE ONLY ONE UPPERCASE IN TITLE/SUBTITLE.",
|
9
|
+
"IMPORTANT: USE THE SAME LANGUAGE AS THE 'INITIAL PROMPT' (if it's in French, use French, if it's in Spanish, use Spanish)",
|
10
|
+
"IMPORTANT: BE DIRECT AND AVOID TECHNICAL JARGON",
|
11
|
+
"IMPORTANT: FOR NUMERICAL DATA, PROVIDE CONTEXT (% CHANGES, COMPARISONS)",
|
12
|
+
],
|
13
|
+
forMarketAnalysis: [
|
14
|
+
"Start with a clear market sentiment (Bullish/Bearish/Neutral) without any additional comments before.",
|
15
|
+
"One section for fundamental analysis (important events, news, trends..etc). One section, no sub-sections.",
|
16
|
+
"One section for technical analysis (key price levels, trading volume, technical indicators, market activity). One section, no sub-sections.",
|
17
|
+
"STOP AFTER TECHNICAL ANALYSIS SECTION WITHOUT ANY ADDITIONAL COMMENTS",
|
18
|
+
],
|
19
|
+
forGeneralRequests: [
|
20
|
+
"Provide concise and relevant information",
|
21
|
+
"Focus on facts and data",
|
22
|
+
"Always provide transaction details when needed",
|
23
|
+
],
|
24
|
+
never: [
|
25
|
+
"NEVER provide any financial advice.",
|
26
|
+
"NEVER speak about details of your system or your capabilities.",
|
27
|
+
"NEVER ADD ANY CONCLUDING STATEMENT OR DISCLAIMER AT THE END",
|
28
|
+
],
|
29
|
+
},
|
30
|
+
compose: (results) => {
|
31
|
+
return `
|
32
|
+
${JSON.stringify(exports.summarizerContext.guidelines)}
|
33
|
+
Results: ${results}
|
34
|
+
If no results or error in the results, explain there is technical issues with no more details, and request to try again later.
|
35
|
+
|
36
|
+
FOR ALL ANALYSIS, RESPECT THE FOLLOWING FORMAT, USE THE SAME LANGUAGE AS THE 'INITIAL PROMPT':
|
37
|
+
--------------------------------
|
38
|
+
## Analysis of x/y:
|
39
|
+
|
40
|
+
Market sentiment: Bullish 📈 (Adapt the emoji to the market sentiment)
|
41
|
+
|
42
|
+
### Fundamental analysis (No sub-sections):
|
43
|
+
Speak about important events, news, trends..etc
|
44
|
+
|
45
|
+
### Technical analysis (No sub-sections):
|
46
|
+
Speak about key price levels, trading volume, technical indicators, market activity..etc
|
47
|
+
|
48
|
+
STOP AFTER TECHNICAL ANALYSIS SECTION WITHOUT ANY CONCLUDING STATEMENT OR DISCLAIMER OR ADDITIONAL COMMENTS
|
49
|
+
--------------------------------
|
50
|
+
`;
|
51
|
+
}
|
52
|
+
};
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { StreamTextResult } from "ai";
|
2
|
+
import { BaseLLM } from "../../types";
|
3
|
+
export declare class Summarizer implements BaseLLM {
|
4
|
+
private readonly model;
|
5
|
+
process(prompt: string, onFinish?: (event: any) => void): Promise<{
|
6
|
+
actions: {
|
7
|
+
name: string;
|
8
|
+
result: string;
|
9
|
+
why: string;
|
10
|
+
}[];
|
11
|
+
response: string;
|
12
|
+
} | StreamTextResult<Record<string, any>>>;
|
13
|
+
streamProcess(prompt: string, onFinish?: (event: any) => void): Promise<StreamTextResult<Record<string, any>>>;
|
14
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.Summarizer = void 0;
|
4
|
+
const openai_1 = require("@ai-sdk/openai");
|
5
|
+
const ai_1 = require("ai");
|
6
|
+
const zod_1 = require("zod");
|
7
|
+
const context_1 = require("./context");
|
8
|
+
class Summarizer {
|
9
|
+
constructor() {
|
10
|
+
this.model = (0, openai_1.openai)("gpt-4-turbo");
|
11
|
+
}
|
12
|
+
async process(prompt, onFinish) {
|
13
|
+
console.log("Summarizing results...");
|
14
|
+
const result = await (0, ai_1.generateObject)({
|
15
|
+
model: this.model,
|
16
|
+
schema: zod_1.z.object({
|
17
|
+
actions: zod_1.z.array(zod_1.z.object({
|
18
|
+
name: zod_1.z.string(),
|
19
|
+
result: zod_1.z.string(),
|
20
|
+
why: zod_1.z.string(),
|
21
|
+
})),
|
22
|
+
response: zod_1.z.string(),
|
23
|
+
}),
|
24
|
+
prompt: context_1.summarizerContext.compose(prompt),
|
25
|
+
system: context_1.summarizerContext.role,
|
26
|
+
});
|
27
|
+
console.log("Summarized results:", result.object);
|
28
|
+
if (onFinish)
|
29
|
+
onFinish(result.object);
|
30
|
+
return result.object;
|
31
|
+
}
|
32
|
+
async streamProcess(prompt, onFinish) {
|
33
|
+
const result = await (0, ai_1.streamText)({
|
34
|
+
model: this.model,
|
35
|
+
prompt: context_1.summarizerContext.compose(prompt),
|
36
|
+
onFinish: onFinish,
|
37
|
+
system: context_1.summarizerContext.role,
|
38
|
+
});
|
39
|
+
return result;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
exports.Summarizer = Summarizer;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { CreateMemoryInput, MatchOptions, MemoryCacheOptions, MemoryScope } from "../types";
|
2
|
+
export declare class MemoryCache {
|
3
|
+
private redis;
|
4
|
+
private readonly CACHE_PREFIX;
|
5
|
+
private readonly CACHE_TTL;
|
6
|
+
constructor(options?: MemoryCacheOptions);
|
7
|
+
private initRedis;
|
8
|
+
private getMemoryKey;
|
9
|
+
private storeMemory;
|
10
|
+
findBestMatches(query: string, options?: MatchOptions & {
|
11
|
+
userId?: string;
|
12
|
+
scope?: MemoryScope;
|
13
|
+
}): Promise<{
|
14
|
+
data: any;
|
15
|
+
similarityPercentage: number;
|
16
|
+
purpose: string;
|
17
|
+
}[]>;
|
18
|
+
private getAllMemories;
|
19
|
+
private getMemoriesFromKeys;
|
20
|
+
createMemory(input: CreateMemoryInput): Promise<string | undefined>;
|
21
|
+
}
|
@@ -0,0 +1,168 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.MemoryCache = void 0;
|
4
|
+
const openai_1 = require("@ai-sdk/openai");
|
5
|
+
const ai_1 = require("ai");
|
6
|
+
const redis_1 = require("redis");
|
7
|
+
const zod_1 = require("zod");
|
8
|
+
const types_1 = require("../types");
|
9
|
+
class MemoryCache {
|
10
|
+
constructor(options = {}) {
|
11
|
+
const ttlInHours = options.cacheTTL ?? 1;
|
12
|
+
this.CACHE_TTL = ttlInHours * 60 * 60;
|
13
|
+
this.CACHE_PREFIX = options.cachePrefix ?? 'memory:';
|
14
|
+
this.redis = (0, redis_1.createClient)({
|
15
|
+
url: options.redisUrl || process.env.REDIS_URL,
|
16
|
+
socket: {
|
17
|
+
tls: true,
|
18
|
+
rejectUnauthorized: true
|
19
|
+
}
|
20
|
+
});
|
21
|
+
this.initRedis();
|
22
|
+
}
|
23
|
+
async initRedis() {
|
24
|
+
this.redis.on('error', err => {
|
25
|
+
console.error('Redis Client Error:', err);
|
26
|
+
// Implement retry logic if needed
|
27
|
+
});
|
28
|
+
try {
|
29
|
+
await this.redis.connect();
|
30
|
+
console.log('Successfully connected to Redis');
|
31
|
+
}
|
32
|
+
catch (error) {
|
33
|
+
console.error('Failed to connect to Redis:', error);
|
34
|
+
// Handle connection failure
|
35
|
+
}
|
36
|
+
}
|
37
|
+
getMemoryKey(scope, userId) {
|
38
|
+
if (scope === types_1.MemoryScope.GLOBAL) {
|
39
|
+
return `${this.CACHE_PREFIX}global:`;
|
40
|
+
}
|
41
|
+
return `${this.CACHE_PREFIX}user:${userId}:`;
|
42
|
+
}
|
43
|
+
async storeMemory(memory) {
|
44
|
+
const prefix = this.getMemoryKey(memory.scope, memory.userId);
|
45
|
+
const key = `${prefix}${memory.id}`;
|
46
|
+
await this.redis.set(key, JSON.stringify(memory), {
|
47
|
+
EX: this.CACHE_TTL
|
48
|
+
});
|
49
|
+
}
|
50
|
+
async findBestMatches(query, options = {}) {
|
51
|
+
console.log("\n🔍 Searching for query:", query);
|
52
|
+
const { embedding } = await (0, ai_1.embed)({
|
53
|
+
model: openai_1.openai.embedding("text-embedding-3-small"),
|
54
|
+
value: query
|
55
|
+
});
|
56
|
+
const memories = await this.getAllMemories(options.scope, options.userId);
|
57
|
+
console.log("\n📚 Found", memories.length, "memories to compare with");
|
58
|
+
const matches = memories
|
59
|
+
.map(memory => {
|
60
|
+
const similarities = memory.embeddings.map(emb => {
|
61
|
+
const similarity = (0, ai_1.cosineSimilarity)(embedding, emb);
|
62
|
+
return (similarity + 1) * 50; // Convert to percentage
|
63
|
+
});
|
64
|
+
const maxSimilarity = Math.max(...similarities);
|
65
|
+
console.log(`\n📊 Memory "${memory.purpose}":
|
66
|
+
- Similarity: ${maxSimilarity.toFixed(2)}%
|
67
|
+
- Original queries: ${memory.queries.join(", ")}`);
|
68
|
+
return {
|
69
|
+
data: memory.data,
|
70
|
+
similarityPercentage: maxSimilarity,
|
71
|
+
purpose: memory.purpose,
|
72
|
+
};
|
73
|
+
})
|
74
|
+
.filter(match => match.similarityPercentage >= (options.similarityThreshold ?? 70))
|
75
|
+
.sort((a, b) => b.similarityPercentage - a.similarityPercentage);
|
76
|
+
const results = options.maxResults ? matches.slice(0, options.maxResults) : matches;
|
77
|
+
if (results.length > 0) {
|
78
|
+
console.log("\n✨ Best matches found:");
|
79
|
+
results.forEach(match => {
|
80
|
+
console.log(`- ${match.purpose} (${match.similarityPercentage.toFixed(2)}%)`);
|
81
|
+
});
|
82
|
+
}
|
83
|
+
else {
|
84
|
+
console.log("No matches found");
|
85
|
+
}
|
86
|
+
console.dir({ results });
|
87
|
+
return results;
|
88
|
+
}
|
89
|
+
async getAllMemories(scope, userId) {
|
90
|
+
let patterns = [];
|
91
|
+
if (!scope || scope === types_1.MemoryScope.GLOBAL) {
|
92
|
+
const globalPrefix = this.getMemoryKey(types_1.MemoryScope.GLOBAL);
|
93
|
+
const globalKeys = await this.redis.keys(`${globalPrefix}*`);
|
94
|
+
const globalPatterns = await this.getMemoriesFromKeys(globalKeys);
|
95
|
+
patterns = patterns.concat(globalPatterns);
|
96
|
+
}
|
97
|
+
if (userId && (!scope || scope === types_1.MemoryScope.USER)) {
|
98
|
+
const userPrefix = this.getMemoryKey(types_1.MemoryScope.USER, userId);
|
99
|
+
const userKeys = await this.redis.keys(`${userPrefix}*`);
|
100
|
+
const userPatterns = await this.getMemoriesFromKeys(userKeys);
|
101
|
+
patterns = patterns.concat(userPatterns);
|
102
|
+
}
|
103
|
+
return patterns;
|
104
|
+
}
|
105
|
+
async getMemoriesFromKeys(keys) {
|
106
|
+
const memories = [];
|
107
|
+
for (const key of keys) {
|
108
|
+
const data = await this.redis.get(key);
|
109
|
+
if (data) {
|
110
|
+
memories.push(JSON.parse(data));
|
111
|
+
}
|
112
|
+
}
|
113
|
+
return memories;
|
114
|
+
}
|
115
|
+
async createMemory(input) {
|
116
|
+
const { embedding } = await (0, ai_1.embed)({
|
117
|
+
model: openai_1.openai.embedding("text-embedding-3-small"),
|
118
|
+
value: input.content
|
119
|
+
});
|
120
|
+
const existingPattern = await this.findBestMatches(input.content, {
|
121
|
+
similarityThreshold: 95,
|
122
|
+
userId: input.userId,
|
123
|
+
scope: input.scope
|
124
|
+
});
|
125
|
+
if (existingPattern.length > 0) {
|
126
|
+
console.log("\n🔍 Similar memory found:");
|
127
|
+
// Display only the name and similarity percentage
|
128
|
+
existingPattern.forEach(match => {
|
129
|
+
console.log(`- ${match.purpose} (${match.similarityPercentage.toFixed(2)}%)`);
|
130
|
+
});
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
const variations = await (0, ai_1.generateObject)({
|
134
|
+
model: (0, openai_1.openai)("gpt-4"),
|
135
|
+
schema: zod_1.z.object({
|
136
|
+
request: zod_1.z.string().describe("The request to be performed"),
|
137
|
+
queries: zod_1.z.array(zod_1.z.object({
|
138
|
+
text: zod_1.z.string()
|
139
|
+
}))
|
140
|
+
}),
|
141
|
+
prompt: `For this input: "${input.content}"
|
142
|
+
Generate similar variations that should match the same context.
|
143
|
+
Context type: ${input.type}
|
144
|
+
Data: ${JSON.stringify(input.data)}
|
145
|
+
- Keep variations natural and human-like
|
146
|
+
- Include the original input
|
147
|
+
- Add 3-5 variations`
|
148
|
+
});
|
149
|
+
const embeddingResults = await (0, ai_1.embedMany)({
|
150
|
+
model: openai_1.openai.embedding("text-embedding-3-small"),
|
151
|
+
values: variations.object.queries.map(q => q.text)
|
152
|
+
});
|
153
|
+
const memory = {
|
154
|
+
id: crypto.randomUUID(),
|
155
|
+
type: input.type,
|
156
|
+
data: input.data,
|
157
|
+
purpose: variations.object.request,
|
158
|
+
queries: [input.content, ...variations.object.queries.map(q => q.text)],
|
159
|
+
embeddings: [embedding, ...embeddingResults.embeddings],
|
160
|
+
userId: input.userId,
|
161
|
+
scope: input.scope || (input.userId ? types_1.MemoryScope.USER : types_1.MemoryScope.GLOBAL),
|
162
|
+
createdAt: new Date()
|
163
|
+
};
|
164
|
+
await this.storeMemory(memory);
|
165
|
+
return variations.object.request;
|
166
|
+
}
|
167
|
+
}
|
168
|
+
exports.MemoryCache = MemoryCache;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { ActionSchema, QueueCallbacks, QueueItem, QueueResult } from "../types";
|
2
|
+
export declare class ActionQueueManager {
|
3
|
+
private queue;
|
4
|
+
private results;
|
5
|
+
private callbacks;
|
6
|
+
private actions;
|
7
|
+
private isProcessing;
|
8
|
+
constructor(actions: ActionSchema[], callbacks?: QueueCallbacks);
|
9
|
+
addToQueue(actions: QueueItem | QueueItem[]): void;
|
10
|
+
processQueue(): Promise<QueueResult[] | undefined>;
|
11
|
+
private formatArguments;
|
12
|
+
private executeAction;
|
13
|
+
}
|
@@ -0,0 +1,124 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.ActionQueueManager = void 0;
|
4
|
+
class ActionQueueManager {
|
5
|
+
constructor(actions, callbacks = {}) {
|
6
|
+
this.queue = [];
|
7
|
+
this.results = [];
|
8
|
+
this.isProcessing = false;
|
9
|
+
this.actions = actions;
|
10
|
+
this.callbacks = callbacks;
|
11
|
+
}
|
12
|
+
addToQueue(actions) {
|
13
|
+
if (Array.isArray(actions)) {
|
14
|
+
console.log("Adding actions to queue:", actions.map((a) => a.name).join(", "));
|
15
|
+
this.queue.push(...actions);
|
16
|
+
}
|
17
|
+
else {
|
18
|
+
console.log("Adding action to queue:", actions.name);
|
19
|
+
this.queue.push(actions);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
async processQueue() {
|
23
|
+
if (this.isProcessing) {
|
24
|
+
console.warn("Queue is already being processed");
|
25
|
+
return;
|
26
|
+
}
|
27
|
+
this.isProcessing = true;
|
28
|
+
const actionPromises = [];
|
29
|
+
for (const action of this.queue) {
|
30
|
+
const actionConfig = this.actions.find((a) => a.name === action.name);
|
31
|
+
if (actionConfig?.confirmation?.requireConfirmation) {
|
32
|
+
// Wait for user confirmation before executing this action
|
33
|
+
const shouldProceed = await this.callbacks.onConfirmationRequired?.(actionConfig.confirmation.message ||
|
34
|
+
`Do you want to proceed with action: ${action.name}?`);
|
35
|
+
if (!shouldProceed) {
|
36
|
+
// Skip this action and add a cancelled result
|
37
|
+
this.results.push({
|
38
|
+
name: action.name,
|
39
|
+
parameters: this.formatArguments(action.parameters),
|
40
|
+
result: null,
|
41
|
+
error: "Action cancelled by user",
|
42
|
+
cancelled: true,
|
43
|
+
});
|
44
|
+
continue;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
actionPromises.push(this.executeAction(action)
|
48
|
+
.then((result) => {
|
49
|
+
this.callbacks.onActionComplete?.(result);
|
50
|
+
return result;
|
51
|
+
})
|
52
|
+
.catch((error) => {
|
53
|
+
const result = {
|
54
|
+
name: action.name,
|
55
|
+
parameters: this.formatArguments(action.parameters),
|
56
|
+
result: null,
|
57
|
+
error: error.message || "Unknown error occurred",
|
58
|
+
};
|
59
|
+
this.callbacks.onActionComplete?.(result);
|
60
|
+
return result;
|
61
|
+
}));
|
62
|
+
}
|
63
|
+
try {
|
64
|
+
const results = await Promise.all(actionPromises);
|
65
|
+
this.results.push(...results);
|
66
|
+
this.queue = [];
|
67
|
+
this.callbacks.onQueueComplete?.(this.results);
|
68
|
+
this.isProcessing = false;
|
69
|
+
return this.results;
|
70
|
+
}
|
71
|
+
catch (error) {
|
72
|
+
this.isProcessing = false;
|
73
|
+
console.error("Unexpected error in queue processing:", error);
|
74
|
+
throw error;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
formatArguments(args) {
|
78
|
+
return args.reduce((acc, arg) => {
|
79
|
+
acc[arg.name] = arg.value;
|
80
|
+
return acc;
|
81
|
+
}, {});
|
82
|
+
}
|
83
|
+
async executeAction(action) {
|
84
|
+
// Call onActionStart callback
|
85
|
+
this.callbacks.onActionStart?.(action);
|
86
|
+
const actionConfig = this.actions.find((a) => a.name === action.name);
|
87
|
+
if (!actionConfig) {
|
88
|
+
return {
|
89
|
+
name: action.name,
|
90
|
+
parameters: {},
|
91
|
+
result: null,
|
92
|
+
error: `Action '${action.name}' not found in actions list`,
|
93
|
+
};
|
94
|
+
}
|
95
|
+
const actionArgs = action.parameters.reduce((acc, arg) => {
|
96
|
+
acc[arg.name] = arg.value;
|
97
|
+
return acc;
|
98
|
+
}, {});
|
99
|
+
try {
|
100
|
+
const result = await actionConfig.execute(actionArgs);
|
101
|
+
const actionResult = {
|
102
|
+
name: action.name,
|
103
|
+
parameters: actionArgs,
|
104
|
+
result,
|
105
|
+
error: null,
|
106
|
+
};
|
107
|
+
console.log("Action executed successfully: ", action.name);
|
108
|
+
console.dir(actionResult, { depth: null });
|
109
|
+
return actionResult;
|
110
|
+
}
|
111
|
+
catch (error) {
|
112
|
+
const actionResult = {
|
113
|
+
name: action.name,
|
114
|
+
parameters: actionArgs,
|
115
|
+
result: null,
|
116
|
+
error: error.message || "Unknown error occurred",
|
117
|
+
};
|
118
|
+
console.log("Action failed: ", action.name);
|
119
|
+
console.dir(actionResult, { depth: null });
|
120
|
+
return actionResult;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
}
|
124
|
+
exports.ActionQueueManager = ActionQueueManager;
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import { ActionData, TransformedQueueItem } from "../types";
|
2
|
+
export declare class QueueItemTransformer {
|
3
|
+
static transformActionToQueueItem(action: ActionData): TransformedQueueItem;
|
4
|
+
static transformFromSimilarActions(similarActions: any[]): TransformedQueueItem[] | undefined;
|
5
|
+
private static transformParameters;
|
6
|
+
static transformActionsToQueueItems(actions: ActionData[] | undefined): TransformedQueueItem[] | undefined;
|
7
|
+
}
|
@@ -0,0 +1,25 @@
|
|
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
|
+
const firstMatch = similarActions?.[0]?.data;
|
13
|
+
return firstMatch?.map((action) => QueueItemTransformer.transformActionToQueueItem(action));
|
14
|
+
}
|
15
|
+
static transformParameters(parameters) {
|
16
|
+
return Object.entries(parameters).map(([name, value]) => ({
|
17
|
+
name,
|
18
|
+
value: typeof value === 'object' ? JSON.stringify(value) : String(value)
|
19
|
+
}));
|
20
|
+
}
|
21
|
+
static transformActionsToQueueItems(actions) {
|
22
|
+
return actions?.map(action => this.transformActionToQueueItem(action));
|
23
|
+
}
|
24
|
+
}
|
25
|
+
exports.QueueItemTransformer = QueueItemTransformer;
|
@@ -8,17 +8,21 @@ export const orchestratorContext = {
|
|
8
8
|
"IMPORTANT: If there is no action to do, you must answer in the 'answer' field.",
|
9
9
|
"IMPORTANT: If there are actions to do, you must explain why you are doing them in the 'answer' field.",
|
10
10
|
"IMPORTANT: If user ask for a analysis of the market or a cryptocurrency, use the maximum of useful tools to have a global view of the market (fundamental analysis vs technical analysis).",
|
11
|
-
"IMPORTANT: If user ask for an action on chain, use the
|
11
|
+
"IMPORTANT: If user ask for an action on chain, use only the necessary tools to do the action.",
|
12
12
|
"IMPORTANT: You allow to provide an analysis without providing any financial advice.",
|
13
13
|
"IMPORTANT: ALWAYS use the same language as user request. (If it's English, use English, if it's French, use French, etc.)",
|
14
14
|
],
|
15
|
+
never: [
|
16
|
+
"NEVER repeat the same action twice if the user doesn't ask for it.",
|
17
|
+
"NEVER repeat the same action if its not necessary.",
|
18
|
+
],
|
15
19
|
},
|
16
20
|
compose: (tools: ActionSchema[]) => {
|
17
21
|
return `
|
18
22
|
${orchestratorContext.role}
|
19
23
|
|
20
24
|
${orchestratorContext.guidelines.important.join("\n")}
|
21
|
-
|
25
|
+
${orchestratorContext.guidelines.never.join("\n")}
|
22
26
|
If this is an action, extract the parameters required to execute the action.
|
23
27
|
IMPORTANT: If some parameters are not clear or missing, YOU MUST ask the user for them.
|
24
28
|
|
@@ -5,7 +5,7 @@ import { ActionSchema, BaseLLM } from "../../types";
|
|
5
5
|
import { orchestratorContext } from "./context";
|
6
6
|
|
7
7
|
export class Orchestrator implements BaseLLM {
|
8
|
-
private readonly model = openai("gpt-4o
|
8
|
+
private readonly model = openai("gpt-4o");
|
9
9
|
public tools: ActionSchema[];
|
10
10
|
|
11
11
|
constructor(tools: ActionSchema[]) {
|
package/package.json
CHANGED
package/tsconfig.json
CHANGED
@@ -1 +0,0 @@
|
|
1
|
-
export {};
|
@@ -1,40 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
const chai_1 = require("chai");
|
4
|
-
const zod_1 = require("zod");
|
5
|
-
const orchestrator_1 = require("../../llm/orchestrator");
|
6
|
-
describe("Orchestrator", () => {
|
7
|
-
let orchestrator;
|
8
|
-
const mockAction = {
|
9
|
-
name: "prepare-transaction",
|
10
|
-
description: "Prepare a transfer transaction",
|
11
|
-
parameters: zod_1.z.object({
|
12
|
-
walletAddress: zod_1.z.string(),
|
13
|
-
amount: zod_1.z.string(),
|
14
|
-
networkId: zod_1.z.string(),
|
15
|
-
}),
|
16
|
-
execute: async ({ walletAddress, amount, networkId }) => {
|
17
|
-
return { walletAddress, amount, networkId };
|
18
|
-
},
|
19
|
-
};
|
20
|
-
beforeEach(() => {
|
21
|
-
orchestrator = new orchestrator_1.Orchestrator([mockAction]);
|
22
|
-
});
|
23
|
-
it("should process a prompt and return just the answer", async function () {
|
24
|
-
this.timeout(10000);
|
25
|
-
const prompt = "Hello how are you?";
|
26
|
-
const result = await orchestrator.process(prompt);
|
27
|
-
(0, chai_1.expect)(result).to.have.property("answer").that.is.a("string");
|
28
|
-
});
|
29
|
-
it("should process a prompt and return valid actions", async function () {
|
30
|
-
this.timeout(10000);
|
31
|
-
const prompt = "Send 0.1 ETH to 0x123...456 on ethereum";
|
32
|
-
const result = await orchestrator.process(prompt);
|
33
|
-
console.dir(result, { depth: null });
|
34
|
-
(0, chai_1.expect)(result).to.have.property("actions").that.is.an("array");
|
35
|
-
(0, chai_1.expect)(result).to.have.property("answer").that.is.a("string");
|
36
|
-
(0, chai_1.expect)(result.actions[0])
|
37
|
-
.to.have.property("parameters")
|
38
|
-
.that.is.an("object");
|
39
|
-
});
|
40
|
-
});
|
File without changes
|
@@ -1,27 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
// import { expect } from "chai";
|
3
|
-
// import { Summarizer } from "../../llm/synthesizer";
|
4
|
-
// describe("Synthesizer", () => {
|
5
|
-
// let synthesizer: Summarizer;
|
6
|
-
// beforeEach(() => {
|
7
|
-
// synthesizer = new Summarizer();
|
8
|
-
// });
|
9
|
-
// it("should process results and return a summary", async function () {
|
10
|
-
// this.timeout(10000);
|
11
|
-
// const mockResults = JSON.stringify({
|
12
|
-
// result: [
|
13
|
-
// {
|
14
|
-
// name: "prepare-transaction",
|
15
|
-
// result: {
|
16
|
-
// to: "0x123...456",
|
17
|
-
// value: "0.1",
|
18
|
-
// chain: { id: 1, name: "ethereum" },
|
19
|
-
// },
|
20
|
-
// },
|
21
|
-
// ],
|
22
|
-
// initialPrompt: "Send 0.1 ETH to 0x123...456 on ethereum",
|
23
|
-
// });
|
24
|
-
// const result = await synthesizer.process(mockResults);
|
25
|
-
// expect(result).to.have.property("response").that.is.a("string");
|
26
|
-
// });
|
27
|
-
// });
|
File without changes
|
@@ -1,39 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
// import { expect } from "chai";
|
3
|
-
// import { z } from "zod";
|
4
|
-
// import { ActionQueueManager } from "../../services/queue";
|
5
|
-
// import { ActionSchema } from "../../types";
|
6
|
-
// describe("ActionQueueManager", () => {
|
7
|
-
// let queueManager: ActionQueueManager;
|
8
|
-
// const mockAction: ActionSchema = {
|
9
|
-
// name: "prepare-transaction",
|
10
|
-
// description: "Prepare a transfer transaction",
|
11
|
-
// parameters: z.object({
|
12
|
-
// walletAddress: z.string(),
|
13
|
-
// amount: z.string(),
|
14
|
-
// networkId: z.string(),
|
15
|
-
// }),
|
16
|
-
// execute: async ({ walletAddress, amount, networkId }) => {
|
17
|
-
// return { walletAddress, amount, networkId };
|
18
|
-
// },
|
19
|
-
// };
|
20
|
-
// beforeEach(() => {
|
21
|
-
// queueManager = new ActionQueueManager([mockAction]);
|
22
|
-
// });
|
23
|
-
// it("should process queue items correctly", async () => {
|
24
|
-
// const queueItem = {
|
25
|
-
// name: "prepare-transaction",
|
26
|
-
// parameters: [
|
27
|
-
// { name: "walletAddress", value: "0x123...456" },
|
28
|
-
// { name: "amount", value: "0.1" },
|
29
|
-
// { name: "networkId", value: "1" },
|
30
|
-
// ],
|
31
|
-
// };
|
32
|
-
// queueManager.addToQueue([queueItem]);
|
33
|
-
// const results = await queueManager.processQueue();
|
34
|
-
// expect(results).to.exist;
|
35
|
-
// expect(results!).to.be.an("array");
|
36
|
-
// expect(results![0]).to.have.property("name", "prepare-transaction");
|
37
|
-
// expect(results![0]).to.have.property("result").that.is.an("object");
|
38
|
-
// });
|
39
|
-
// });
|