@ai.ntellect/core 0.0.31 → 0.0.33
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/agent/index.ts +65 -46
- package/llm/evaluator/context.ts +4 -3
- package/llm/evaluator/index.ts +3 -7
- package/llm/orchestrator/context.ts +2 -3
- package/llm/orchestrator/index.ts +0 -4
- package/memory/index.ts +119 -65
- package/package.json +1 -1
- package/test.ts +146 -0
- package/types.ts +3 -3
package/agent/index.ts
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import EventEmitter from "events";
|
2
1
|
import { Evaluator } from "../llm/evaluator";
|
3
2
|
import { Orchestrator } from "../llm/orchestrator";
|
4
3
|
import { Summarizer } from "../llm/synthesizer";
|
@@ -11,27 +10,40 @@ export class Agent {
|
|
11
10
|
private readonly SIMILARITY_THRESHOLD = 95;
|
12
11
|
private readonly MAX_RESULTS = 1;
|
13
12
|
private readonly actionHandler: ActionHandler;
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
13
|
+
private readonly user: User;
|
14
|
+
private readonly orchestrator: Orchestrator;
|
15
|
+
private readonly memoryCache: MemoryCache | undefined;
|
16
|
+
private readonly stream: boolean;
|
17
|
+
private readonly maxEvaluatorIteration: number;
|
18
|
+
private readonly evaluatorIteration = 0;
|
19
|
+
|
20
|
+
constructor({
|
21
|
+
user,
|
22
|
+
orchestrator,
|
23
|
+
memoryCache,
|
24
|
+
stream,
|
25
|
+
maxEvaluatorIteration = 1,
|
26
|
+
}: {
|
27
|
+
user: User;
|
28
|
+
orchestrator: Orchestrator;
|
29
|
+
memoryCache?: MemoryCache;
|
30
|
+
stream: boolean;
|
31
|
+
maxEvaluatorIteration: number;
|
32
|
+
}) {
|
33
|
+
this.user = user;
|
34
|
+
this.orchestrator = orchestrator;
|
35
|
+
this.memoryCache = memoryCache;
|
36
|
+
this.stream = stream;
|
37
|
+
this.maxEvaluatorIteration = maxEvaluatorIteration;
|
24
38
|
this.actionHandler = new ActionHandler();
|
25
39
|
}
|
26
40
|
|
27
|
-
async
|
41
|
+
async process(
|
28
42
|
prompt: string,
|
29
43
|
contextualizedPrompt: string,
|
30
44
|
events: AgentEvent
|
31
45
|
): Promise<any> {
|
32
|
-
const request = await this.
|
33
|
-
contextualizedPrompt
|
34
|
-
);
|
46
|
+
const request = await this.orchestrator.process(contextualizedPrompt);
|
35
47
|
|
36
48
|
events.onMessage?.(request);
|
37
49
|
|
@@ -60,36 +72,39 @@ export class Agent {
|
|
60
72
|
events: AgentEvent
|
61
73
|
): Promise<any> {
|
62
74
|
const similarActions = await this.findSimilarActions(initialPrompt);
|
63
|
-
const
|
64
|
-
const callbacks = {
|
65
|
-
onQueueStart: events.onQueueStart,
|
66
|
-
onActionStart: events.onActionStart,
|
67
|
-
onActionComplete: events.onActionComplete,
|
68
|
-
onQueueComplete: events.onQueueComplete,
|
69
|
-
onConfirmationRequired: events.onConfirmationRequired,
|
70
|
-
};
|
75
|
+
const queueItems = this.transformActions(actions, similarActions);
|
71
76
|
|
72
77
|
const actionsResult = await this.actionHandler.executeActions(
|
73
|
-
|
74
|
-
this.
|
75
|
-
|
78
|
+
queueItems,
|
79
|
+
this.orchestrator.tools,
|
80
|
+
{
|
81
|
+
onQueueStart: events.onQueueStart,
|
82
|
+
onActionStart: events.onActionStart,
|
83
|
+
onActionComplete: events.onActionComplete,
|
84
|
+
onQueueComplete: events.onQueueComplete,
|
85
|
+
onConfirmationRequired: events.onConfirmationRequired,
|
86
|
+
}
|
76
87
|
);
|
77
88
|
|
78
|
-
|
89
|
+
if (this.evaluatorIteration >= this.maxEvaluatorIteration) {
|
90
|
+
return this.handleActionResults({ ...actionsResult, initialPrompt });
|
91
|
+
}
|
92
|
+
|
93
|
+
const evaluator = new Evaluator(this.orchestrator.tools);
|
79
94
|
const evaluation = await evaluator.process(
|
80
95
|
initialPrompt,
|
81
96
|
contextualizedPrompt,
|
82
97
|
JSON.stringify(actionsResult.data)
|
83
98
|
);
|
84
|
-
|
99
|
+
|
85
100
|
events.onMessage?.(evaluation);
|
86
101
|
|
87
|
-
if (evaluation.
|
102
|
+
if (evaluation.nextActions.length > 0) {
|
88
103
|
return this.handleActions(
|
89
104
|
{
|
90
105
|
initialPrompt: contextualizedPrompt,
|
91
106
|
contextualizedPrompt: initialPrompt,
|
92
|
-
actions: evaluation.
|
107
|
+
actions: evaluation.nextActions,
|
93
108
|
},
|
94
109
|
events
|
95
110
|
);
|
@@ -105,8 +120,27 @@ export class Agent {
|
|
105
120
|
return this.handleActionResults({ ...actionsResult, initialPrompt });
|
106
121
|
}
|
107
122
|
|
123
|
+
private async handleActionResults(actionsResult: {
|
124
|
+
data: any;
|
125
|
+
initialPrompt: string;
|
126
|
+
}) {
|
127
|
+
const summarizer = new Summarizer();
|
128
|
+
const summaryData = JSON.stringify({
|
129
|
+
result: actionsResult.data,
|
130
|
+
initialPrompt: actionsResult.initialPrompt,
|
131
|
+
});
|
132
|
+
|
133
|
+
return this.stream
|
134
|
+
? (await summarizer.streamProcess(summaryData)).toDataStreamResponse()
|
135
|
+
: await summarizer.process(summaryData);
|
136
|
+
}
|
137
|
+
|
108
138
|
private async findSimilarActions(prompt: string) {
|
109
|
-
|
139
|
+
if (!this.memoryCache) {
|
140
|
+
return [];
|
141
|
+
}
|
142
|
+
|
143
|
+
return this.memoryCache.findBestMatches(prompt, {
|
110
144
|
similarityThreshold: this.SIMILARITY_THRESHOLD,
|
111
145
|
maxResults: this.MAX_RESULTS,
|
112
146
|
userId: this.user.id,
|
@@ -125,19 +159,4 @@ export class Agent {
|
|
125
159
|
|
126
160
|
return predefinedActions;
|
127
161
|
}
|
128
|
-
|
129
|
-
private async handleActionResults(actionsResult: {
|
130
|
-
data: any;
|
131
|
-
initialPrompt: string;
|
132
|
-
}) {
|
133
|
-
const summarizer = new Summarizer();
|
134
|
-
const summaryData = JSON.stringify({
|
135
|
-
result: actionsResult.data,
|
136
|
-
initialPrompt: actionsResult.initialPrompt,
|
137
|
-
});
|
138
|
-
|
139
|
-
return this.stream
|
140
|
-
? (await summarizer.streamProcess(summaryData)).toDataStreamResponse()
|
141
|
-
: await summarizer.process(summaryData);
|
142
|
-
}
|
143
162
|
}
|
package/llm/evaluator/context.ts
CHANGED
@@ -8,7 +8,6 @@ export const evaluatorContext = {
|
|
8
8
|
"IMPORTANT: Verify if all required actions were executed successfully",
|
9
9
|
"IMPORTANT: Check if the results match the initial goal",
|
10
10
|
"IMPORTANT: Identify any missing or incomplete information",
|
11
|
-
"IMPORTANT: Use the same language as the initial request",
|
12
11
|
],
|
13
12
|
never: [
|
14
13
|
"NEVER modify the results directly",
|
@@ -22,9 +21,10 @@ export const evaluatorContext = {
|
|
22
21
|
|
23
22
|
${evaluatorContext.guidelines.important.join("\n")}
|
24
23
|
${evaluatorContext.guidelines.never.join("\n")}
|
24
|
+
|
25
|
+
ACTIONS COMPLETED: ${results}
|
25
26
|
|
26
|
-
Initial Goal: ${goal}
|
27
|
-
What was done: ${results}
|
27
|
+
Initial Goal: ${goal} (You must use the same language)
|
28
28
|
|
29
29
|
The actions available are: ${tools.map((action) => {
|
30
30
|
const parameters = action.parameters as z.ZodObject<any>;
|
@@ -36,6 +36,7 @@ export const evaluatorContext = {
|
|
36
36
|
Evaluate if the goal has been achieved and provide:
|
37
37
|
1. Success status with explanation (no action needed)
|
38
38
|
2. Next actions needed (if any)
|
39
|
+
3. Why you are doing the next actions or why you are not doing them
|
39
40
|
`;
|
40
41
|
},
|
41
42
|
};
|
package/llm/evaluator/index.ts
CHANGED
@@ -17,7 +17,7 @@ export class Evaluator {
|
|
17
17
|
const response = await generateObject({
|
18
18
|
model: this.model,
|
19
19
|
schema: z.object({
|
20
|
-
|
20
|
+
nextActions: z.array(
|
21
21
|
z.object({
|
22
22
|
name: z.string(),
|
23
23
|
parameters: z.object({
|
@@ -26,7 +26,7 @@ export class Evaluator {
|
|
26
26
|
}),
|
27
27
|
})
|
28
28
|
),
|
29
|
-
|
29
|
+
why: z.string(),
|
30
30
|
}),
|
31
31
|
prompt: prompt,
|
32
32
|
system: evaluatorContext.compose(goal, results, this.tools),
|
@@ -34,19 +34,15 @@ export class Evaluator {
|
|
34
34
|
|
35
35
|
const validatedResponse = {
|
36
36
|
...response.object,
|
37
|
-
|
37
|
+
nextActions: response.object.nextActions.map((action) => ({
|
38
38
|
...action,
|
39
39
|
parameters: action.parameters || {},
|
40
40
|
})),
|
41
41
|
};
|
42
42
|
|
43
|
-
console.dir(validatedResponse, { depth: null });
|
44
|
-
|
45
43
|
return validatedResponse;
|
46
44
|
} catch (error: any) {
|
47
45
|
if (error) {
|
48
|
-
console.log("Error in Orchestrator", error.message);
|
49
|
-
console.dir(error.value, { depth: null });
|
50
46
|
return {
|
51
47
|
...error.value,
|
52
48
|
};
|
@@ -2,12 +2,11 @@ import { z } from "zod";
|
|
2
2
|
import { ActionSchema } from "../../types";
|
3
3
|
|
4
4
|
export const orchestratorContext = {
|
5
|
-
role: "You are the
|
5
|
+
role: "You are the first agent to be called. You are the one who will decide if the user request is clear and if it's possible to achieve the goal.",
|
6
6
|
guidelines: {
|
7
7
|
important: [
|
8
8
|
"IMPORTANT: If there is no action to do, you must answer in the 'answer' field.",
|
9
|
-
"IMPORTANT: If
|
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).",
|
9
|
+
"IMPORTANT: If user ask for a analysis of the market or a cryptocurrency, use the maximum of useful tools to have for understanding the market.",
|
11
10
|
"IMPORTANT: If user ask for an action on chain, use only the necessary tools to do the action.",
|
12
11
|
"IMPORTANT: You allow to provide an analysis without providing any financial advice.",
|
13
12
|
"IMPORTANT: ALWAYS use the same language as user request. (If it's English, use English, if it's French, use French, etc.)",
|
@@ -40,13 +40,9 @@ export class Orchestrator implements BaseLLM {
|
|
40
40
|
})),
|
41
41
|
};
|
42
42
|
|
43
|
-
console.dir(validatedResponse, { depth: null });
|
44
|
-
|
45
43
|
return validatedResponse;
|
46
44
|
} catch (error: any) {
|
47
45
|
if (error) {
|
48
|
-
console.log("Error in Orchestrator", error.message);
|
49
|
-
console.dir(error.value, { depth: null });
|
50
46
|
return {
|
51
47
|
...error.value,
|
52
48
|
};
|
package/memory/index.ts
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
import { openai } from "@ai-sdk/openai";
|
2
|
-
import { cosineSimilarity, embed,
|
3
|
-
import { createClient } from
|
2
|
+
import { cosineSimilarity, embed, generateObject } from "ai";
|
3
|
+
import { createClient } from "redis";
|
4
4
|
import { z } from "zod";
|
5
|
-
import {
|
5
|
+
import {
|
6
|
+
CreateMemoryInput,
|
7
|
+
MatchOptions,
|
8
|
+
Memory,
|
9
|
+
MemoryCacheOptions,
|
10
|
+
MemoryScope,
|
11
|
+
MemoryType,
|
12
|
+
} from "../types";
|
6
13
|
|
7
14
|
export class MemoryCache {
|
8
15
|
private redis;
|
@@ -12,29 +19,29 @@ export class MemoryCache {
|
|
12
19
|
constructor(options: MemoryCacheOptions = {}) {
|
13
20
|
const ttlInHours = options.cacheTTL ?? 1;
|
14
21
|
this.CACHE_TTL = ttlInHours * 60 * 60;
|
15
|
-
this.CACHE_PREFIX = options.cachePrefix ??
|
16
|
-
|
22
|
+
this.CACHE_PREFIX = options.cachePrefix ?? "memory:";
|
23
|
+
|
17
24
|
this.redis = createClient({
|
18
25
|
url: options.redisUrl || process.env.REDIS_URL,
|
19
26
|
socket: {
|
20
27
|
tls: true,
|
21
|
-
rejectUnauthorized: true
|
22
|
-
}
|
28
|
+
rejectUnauthorized: true,
|
29
|
+
},
|
23
30
|
});
|
24
31
|
this.initRedis();
|
25
32
|
}
|
26
33
|
|
27
34
|
private async initRedis() {
|
28
|
-
this.redis.on(
|
29
|
-
console.error(
|
35
|
+
this.redis.on("error", (err) => {
|
36
|
+
console.error("Redis Client Error:", err);
|
30
37
|
// Implement retry logic if needed
|
31
38
|
});
|
32
|
-
|
39
|
+
|
33
40
|
try {
|
34
41
|
await this.redis.connect();
|
35
|
-
console.log(
|
42
|
+
console.log("Successfully connected to Redis");
|
36
43
|
} catch (error) {
|
37
|
-
console.error(
|
44
|
+
console.error("Failed to connect to Redis:", error);
|
38
45
|
// Handle connection failure
|
39
46
|
}
|
40
47
|
}
|
@@ -50,66 +57,76 @@ export class MemoryCache {
|
|
50
57
|
const prefix = this.getMemoryKey(memory.scope, memory.userId);
|
51
58
|
const key = `${prefix}${memory.id}`;
|
52
59
|
await this.redis.set(key, JSON.stringify(memory), {
|
53
|
-
EX: this.CACHE_TTL
|
60
|
+
EX: this.CACHE_TTL,
|
54
61
|
});
|
55
62
|
}
|
56
63
|
|
57
64
|
async findBestMatches(
|
58
|
-
query: string,
|
65
|
+
query: string,
|
59
66
|
options: MatchOptions & { userId?: string; scope?: MemoryScope } = {}
|
60
|
-
): Promise<
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
67
|
+
): Promise<
|
68
|
+
{
|
69
|
+
data: any;
|
70
|
+
similarityPercentage: number;
|
71
|
+
purpose: string;
|
72
|
+
}[]
|
73
|
+
> {
|
65
74
|
console.log("\n🔍 Searching for query:", query);
|
66
75
|
|
67
76
|
const { embedding } = await embed({
|
68
77
|
model: openai.embedding("text-embedding-3-small"),
|
69
|
-
value: query
|
78
|
+
value: query,
|
70
79
|
});
|
71
80
|
|
72
81
|
const memories = await this.getAllMemories(options.scope, options.userId);
|
73
82
|
console.log("\n📚 Found", memories.length, "memories to compare with");
|
74
83
|
|
75
84
|
const matches = memories
|
76
|
-
.map(memory => {
|
77
|
-
const
|
78
|
-
|
79
|
-
|
80
|
-
return (similarity + 1) * 50; // Convert to percentage
|
81
|
-
});
|
82
|
-
|
83
|
-
const maxSimilarity = Math.max(...similarities);
|
85
|
+
.map((memory) => {
|
86
|
+
const similarity = cosineSimilarity(embedding, memory.embedding);
|
87
|
+
const similarityPercentage = (similarity + 1) * 50; // Conversion en pourcentage
|
88
|
+
|
84
89
|
console.log(`\n📊 Memory "${memory.purpose}":
|
85
|
-
|
86
|
-
|
90
|
+
- Similarity: ${similarityPercentage.toFixed(2)}%
|
91
|
+
- Query: ${memory.query}`);
|
87
92
|
|
88
93
|
return {
|
89
94
|
data: memory.data,
|
90
|
-
similarityPercentage
|
95
|
+
similarityPercentage,
|
91
96
|
purpose: memory.purpose,
|
97
|
+
// Optionnel : ajouter des métadonnées utiles
|
98
|
+
memoryId: memory.id,
|
92
99
|
};
|
93
100
|
})
|
94
|
-
.filter(
|
101
|
+
.filter(
|
102
|
+
(match) =>
|
103
|
+
match.similarityPercentage >= (options.similarityThreshold ?? 70)
|
104
|
+
)
|
95
105
|
.sort((a, b) => b.similarityPercentage - a.similarityPercentage);
|
96
106
|
|
97
|
-
const results = options.maxResults
|
98
|
-
|
107
|
+
const results = options.maxResults
|
108
|
+
? matches.slice(0, options.maxResults)
|
109
|
+
: matches;
|
110
|
+
|
99
111
|
if (results.length > 0) {
|
100
|
-
|
101
|
-
results.forEach(match => {
|
102
|
-
console.log(
|
112
|
+
console.log("\n✨ Best matches found:");
|
113
|
+
results.forEach((match) => {
|
114
|
+
console.log(
|
115
|
+
`- ${match.purpose} (${match.similarityPercentage.toFixed(2)}%)`
|
116
|
+
);
|
103
117
|
});
|
104
118
|
} else {
|
105
119
|
console.log("No matches found");
|
106
120
|
}
|
107
121
|
|
108
|
-
console.dir({results});
|
122
|
+
console.dir({ results });
|
109
123
|
return results;
|
110
124
|
}
|
111
125
|
|
112
|
-
private async getAllMemories(
|
126
|
+
private async getAllMemories(
|
127
|
+
scope?: MemoryScope,
|
128
|
+
userId?: string
|
129
|
+
): Promise<Memory[]> {
|
113
130
|
let patterns: Memory[] = [];
|
114
131
|
|
115
132
|
if (!scope || scope === MemoryScope.GLOBAL) {
|
@@ -140,34 +157,31 @@ export class MemoryCache {
|
|
140
157
|
return memories;
|
141
158
|
}
|
142
159
|
|
143
|
-
public async createMemory(
|
144
|
-
|
145
|
-
|
146
|
-
value: input.content
|
147
|
-
});
|
148
|
-
|
160
|
+
public async createMemory(
|
161
|
+
input: CreateMemoryInput
|
162
|
+
): Promise<string | undefined> {
|
149
163
|
const existingPattern = await this.findBestMatches(input.content, {
|
150
164
|
similarityThreshold: 95,
|
151
165
|
userId: input.userId,
|
152
|
-
scope: input.scope
|
166
|
+
scope: input.scope,
|
153
167
|
});
|
154
168
|
|
155
169
|
if (existingPattern.length > 0) {
|
156
170
|
console.log("\n🔍 Similar memory found:");
|
157
|
-
|
158
|
-
|
159
|
-
|
171
|
+
existingPattern.forEach((match) => {
|
172
|
+
console.log(
|
173
|
+
`- ${match.purpose} (${match.similarityPercentage.toFixed(2)}%)`
|
174
|
+
);
|
160
175
|
});
|
161
176
|
return;
|
162
177
|
}
|
163
178
|
|
179
|
+
// Générer les variations via GPT-4
|
164
180
|
const variations = await generateObject({
|
165
181
|
model: openai("gpt-4"),
|
166
182
|
schema: z.object({
|
167
183
|
request: z.string().describe("The request to be performed"),
|
168
|
-
queries: z.array(z.object({
|
169
|
-
text: z.string()
|
170
|
-
}))
|
184
|
+
queries: z.array(z.object({ text: z.string() })),
|
171
185
|
}),
|
172
186
|
prompt: `For this input: "${input.content}"
|
173
187
|
Generate similar variations that should match the same context.
|
@@ -175,27 +189,67 @@ export class MemoryCache {
|
|
175
189
|
Data: ${JSON.stringify(input.data)}
|
176
190
|
- Keep variations natural and human-like
|
177
191
|
- 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)
|
192
|
+
- Add 3-5 variations`,
|
184
193
|
});
|
185
194
|
|
186
|
-
|
195
|
+
await this.createSingleMemory({
|
187
196
|
id: crypto.randomUUID(),
|
197
|
+
content: input.content,
|
188
198
|
type: input.type,
|
189
199
|
data: input.data,
|
190
200
|
purpose: variations.object.request,
|
191
|
-
queries: [input.content, ...variations.object.queries.map(q => q.text)],
|
192
|
-
embeddings: [embedding, ...embeddingResults.embeddings],
|
193
201
|
userId: input.userId,
|
194
|
-
scope: input.scope
|
195
|
-
|
202
|
+
scope: input.scope,
|
203
|
+
});
|
204
|
+
|
205
|
+
const variationPromises = variations.object.queries.map(
|
206
|
+
async (variation) => {
|
207
|
+
if (variation.text !== input.content) {
|
208
|
+
await this.createSingleMemory({
|
209
|
+
id: crypto.randomUUID(),
|
210
|
+
content: variation.text,
|
211
|
+
type: input.type,
|
212
|
+
data: input.data,
|
213
|
+
purpose: variations.object.request,
|
214
|
+
userId: input.userId,
|
215
|
+
scope: input.scope,
|
216
|
+
});
|
217
|
+
}
|
218
|
+
}
|
219
|
+
);
|
220
|
+
|
221
|
+
await Promise.all(variationPromises);
|
222
|
+
return variations.object.request;
|
223
|
+
}
|
224
|
+
|
225
|
+
private async createSingleMemory(params: {
|
226
|
+
id: string;
|
227
|
+
content: string;
|
228
|
+
type: MemoryType;
|
229
|
+
data: any;
|
230
|
+
purpose: string;
|
231
|
+
userId?: string;
|
232
|
+
scope?: MemoryScope;
|
233
|
+
}): Promise<Memory> {
|
234
|
+
const { embedding } = await embed({
|
235
|
+
model: openai.embedding("text-embedding-3-small"),
|
236
|
+
value: params.content,
|
237
|
+
});
|
238
|
+
|
239
|
+
const memory: Memory = {
|
240
|
+
id: params.id,
|
241
|
+
type: params.type,
|
242
|
+
data: params.data,
|
243
|
+
purpose: params.purpose,
|
244
|
+
query: params.content,
|
245
|
+
embedding,
|
246
|
+
userId: params.userId,
|
247
|
+
scope:
|
248
|
+
params.scope || (params.userId ? MemoryScope.USER : MemoryScope.GLOBAL),
|
249
|
+
createdAt: new Date(),
|
196
250
|
};
|
197
|
-
|
251
|
+
|
198
252
|
await this.storeMemory(memory);
|
199
|
-
return
|
253
|
+
return memory;
|
200
254
|
}
|
201
255
|
}
|
package/package.json
CHANGED
package/test.ts
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
import Parser from "rss-parser";
|
2
|
+
import { z } from "zod";
|
3
|
+
import { Agent } from "./agent";
|
4
|
+
import { Orchestrator } from "./llm/orchestrator";
|
5
|
+
import { MemoryCache } from "./memory";
|
6
|
+
|
7
|
+
interface ChainTVL {
|
8
|
+
name: string;
|
9
|
+
tvl: number;
|
10
|
+
tokenSymbol: string | null;
|
11
|
+
chainId: number | string | null;
|
12
|
+
gecko_id: string | null;
|
13
|
+
cmcId: string | null;
|
14
|
+
}
|
15
|
+
|
16
|
+
export const getChainsTVL = {
|
17
|
+
name: "get_chains_tvl",
|
18
|
+
description:
|
19
|
+
"Get current TVL (Total Value Locked) of all chains from DeFiLlama",
|
20
|
+
parameters: z.object({
|
21
|
+
limit: z
|
22
|
+
.number()
|
23
|
+
.optional()
|
24
|
+
.default(10)
|
25
|
+
.describe("Number of top chains to return (default: 10)"),
|
26
|
+
}),
|
27
|
+
execute: async ({ limit }: { limit: number }) => {
|
28
|
+
try {
|
29
|
+
const response = await fetch("https://api.llama.fi/v2/chains", {
|
30
|
+
headers: { accept: "*/*" },
|
31
|
+
});
|
32
|
+
|
33
|
+
if (!response.ok) {
|
34
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
35
|
+
}
|
36
|
+
|
37
|
+
const chains = (await response.json()) as ChainTVL[];
|
38
|
+
|
39
|
+
// Sort chains by TVL in descending order and take top N
|
40
|
+
const topChains = chains
|
41
|
+
.sort((a, b) => b.tvl - a.tvl)
|
42
|
+
.slice(0, limit)
|
43
|
+
.map((chain) => ({
|
44
|
+
name: chain.name,
|
45
|
+
tvl: chain.tvl,
|
46
|
+
tokenSymbol: chain.tokenSymbol,
|
47
|
+
}));
|
48
|
+
|
49
|
+
const totalTVL = chains.reduce((sum, chain) => sum + chain.tvl, 0);
|
50
|
+
|
51
|
+
return {
|
52
|
+
summary: {
|
53
|
+
totalTVL,
|
54
|
+
numberOfChains: chains.length,
|
55
|
+
},
|
56
|
+
topChains,
|
57
|
+
};
|
58
|
+
} catch (error) {
|
59
|
+
console.error("Error retrieving chains TVL data:", error);
|
60
|
+
throw new Error(
|
61
|
+
`Failed to fetch chains TVL data: ${(error as Error).message}`
|
62
|
+
);
|
63
|
+
}
|
64
|
+
},
|
65
|
+
};
|
66
|
+
|
67
|
+
const RSS_FEEDS = ["https://www.investing.com/rss/news_301.rss"];
|
68
|
+
|
69
|
+
const parser = new Parser();
|
70
|
+
|
71
|
+
function stripHtmlTags(content: string): string {
|
72
|
+
if (!content) return "";
|
73
|
+
return content
|
74
|
+
.replace(/<[^>]*>/g, "")
|
75
|
+
.replace(/\n/g, "")
|
76
|
+
.replace(" ", "");
|
77
|
+
}
|
78
|
+
|
79
|
+
export const getRssNews = {
|
80
|
+
name: "get-news-rss",
|
81
|
+
description: "Get latest news about on website",
|
82
|
+
parameters: z.object({}),
|
83
|
+
execute: async () => {
|
84
|
+
const itemsPerSource = 5;
|
85
|
+
|
86
|
+
try {
|
87
|
+
const feedPromises = RSS_FEEDS.map((url) => parser.parseURL(url));
|
88
|
+
const results = await Promise.allSettled(feedPromises);
|
89
|
+
const successfulFeeds = results
|
90
|
+
.filter(
|
91
|
+
(result): result is PromiseFulfilledResult<Parser.Output<any>> => {
|
92
|
+
return (
|
93
|
+
result.status === "fulfilled" && result.value?.items?.length > 0
|
94
|
+
);
|
95
|
+
}
|
96
|
+
)
|
97
|
+
.map((result) => result.value);
|
98
|
+
const allItems = successfulFeeds
|
99
|
+
.flatMap((feed) => feed.items.slice(0, itemsPerSource))
|
100
|
+
.sort((a, b) => {
|
101
|
+
const dateA = a.pubDate ? new Date(a.pubDate).getTime() : 0;
|
102
|
+
const dateB = b.pubDate ? new Date(b.pubDate).getTime() : 0;
|
103
|
+
return dateB - dateA;
|
104
|
+
})
|
105
|
+
.slice(0, 5)
|
106
|
+
.map((item) => ({
|
107
|
+
title: item.title,
|
108
|
+
content: stripHtmlTags(item.content),
|
109
|
+
link: item.link,
|
110
|
+
date: item.pubDate,
|
111
|
+
source: item.creator || new URL(item.link).hostname,
|
112
|
+
}));
|
113
|
+
|
114
|
+
const result = {
|
115
|
+
status: "success",
|
116
|
+
items: allItems,
|
117
|
+
};
|
118
|
+
return result;
|
119
|
+
} catch (error: any) {
|
120
|
+
throw error;
|
121
|
+
}
|
122
|
+
},
|
123
|
+
};
|
124
|
+
|
125
|
+
(async () => {
|
126
|
+
const orchestrator = new Orchestrator([getRssNews, getChainsTVL]);
|
127
|
+
const agent = new Agent({
|
128
|
+
user: {
|
129
|
+
id: "1",
|
130
|
+
},
|
131
|
+
orchestrator,
|
132
|
+
memoryCache: new MemoryCache(),
|
133
|
+
stream: false,
|
134
|
+
maxEvaluatorIteration: 1,
|
135
|
+
});
|
136
|
+
|
137
|
+
const prompt = "Analyze le marché des crypto";
|
138
|
+
const context = prompt;
|
139
|
+
|
140
|
+
const result = await agent.process(prompt, context, {
|
141
|
+
onMessage: (message) => {
|
142
|
+
console.log({ message });
|
143
|
+
},
|
144
|
+
});
|
145
|
+
console.log(result.text);
|
146
|
+
})();
|
package/types.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { StreamTextResult } from "ai";
|
1
|
+
import { Embedding, StreamTextResult } from "ai";
|
2
2
|
import { z } from "zod";
|
3
3
|
|
4
4
|
export interface BaseLLM {
|
@@ -138,8 +138,8 @@ export interface Memory {
|
|
138
138
|
type: MemoryType;
|
139
139
|
data: any;
|
140
140
|
purpose: string;
|
141
|
-
|
142
|
-
|
141
|
+
query: string;
|
142
|
+
embedding: Embedding;
|
143
143
|
userId?: string;
|
144
144
|
scope: MemoryScope;
|
145
145
|
createdAt: Date;
|