@aigne/core 0.4.204 → 0.4.205-0
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/lib/cjs/index.js +1 -1
- package/lib/cjs/llm-agent.js +192 -79
- package/lib/cjs/memorable.js +32 -0
- package/lib/cjs/tsconfig.tsbuildinfo +1 -1
- package/lib/cjs/utils/message-utils.js +10 -0
- package/lib/cjs/utils/ordered-map.js +13 -0
- package/lib/cjs/utils/structured-output-schema.js +39 -0
- package/lib/esm/index.js +1 -1
- package/lib/esm/llm-agent.js +190 -80
- package/lib/esm/memorable.js +27 -0
- package/lib/esm/tsconfig.tsbuildinfo +1 -1
- package/lib/esm/utils/message-utils.js +7 -0
- package/lib/esm/utils/ordered-map.js +13 -0
- package/lib/esm/utils/structured-output-schema.js +33 -0
- package/lib/types/context.d.ts +5 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/llm-agent.d.ts +98 -14
- package/lib/types/local-function-agent.d.ts +5 -5
- package/lib/types/memorable.d.ts +182 -0
- package/lib/types/runnable.d.ts +15 -0
- package/lib/types/tsconfig.tsbuildinfo +1 -1
- package/lib/types/utils/message-utils.d.ts +2 -0
- package/lib/types/utils/ordered-map.d.ts +3 -0
- package/lib/types/utils/structured-output-schema.d.ts +3 -0
- package/package.json +1 -1
package/lib/cjs/index.js
CHANGED
|
@@ -27,4 +27,4 @@ __exportStar(require("./function-agent"), exports);
|
|
|
27
27
|
__exportStar(require("./function-runner"), exports);
|
|
28
28
|
__exportStar(require("./llm-decision-agent"), exports);
|
|
29
29
|
__exportStar(require("./local-function-agent"), exports);
|
|
30
|
-
__exportStar(require("./
|
|
30
|
+
__exportStar(require("./memorable"), exports);
|
package/lib/cjs/llm-agent.js
CHANGED
|
@@ -11,83 +11,73 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
11
11
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
12
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
13
|
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
14
17
|
var LLMAgent_1;
|
|
15
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
19
|
exports.LLMAgent = void 0;
|
|
17
20
|
exports.createLLMAgentDefinition = createLLMAgentDefinition;
|
|
18
|
-
const
|
|
21
|
+
const remove_1 = __importDefault(require("lodash/remove"));
|
|
19
22
|
const nanoid_1 = require("nanoid");
|
|
20
23
|
const tsyringe_1 = require("tsyringe");
|
|
21
24
|
const constants_1 = require("./constants");
|
|
22
25
|
const data_type_schema_1 = require("./data-type-schema");
|
|
23
26
|
const llm_model_1 = require("./llm-model");
|
|
27
|
+
const logger_1 = __importDefault(require("./logger"));
|
|
24
28
|
const runnable_1 = require("./runnable");
|
|
25
29
|
const utils_1 = require("./utils");
|
|
30
|
+
const message_utils_1 = require("./utils/message-utils");
|
|
26
31
|
const mustache_utils_1 = require("./utils/mustache-utils");
|
|
27
32
|
const ordered_map_1 = require("./utils/ordered-map");
|
|
33
|
+
const structured_output_schema_1 = require("./utils/structured-output-schema");
|
|
28
34
|
let LLMAgent = LLMAgent_1 = class LLMAgent extends runnable_1.Runnable {
|
|
29
35
|
definition;
|
|
30
36
|
model;
|
|
37
|
+
context;
|
|
31
38
|
static create(options) {
|
|
32
39
|
const definition = createLLMAgentDefinition(options);
|
|
33
40
|
return new LLMAgent_1(definition);
|
|
34
41
|
}
|
|
35
|
-
constructor(definition, model) {
|
|
42
|
+
constructor(definition, model, context) {
|
|
36
43
|
super(definition);
|
|
37
44
|
this.definition = definition;
|
|
38
45
|
this.model = model;
|
|
46
|
+
this.context = context;
|
|
39
47
|
}
|
|
40
48
|
async run(input, options) {
|
|
41
49
|
const { definition, model } = this;
|
|
42
50
|
if (!model)
|
|
43
51
|
throw new Error('LLM model is required');
|
|
44
|
-
const
|
|
45
|
-
if (!messages.length)
|
|
46
|
-
throw new Error('Messages are required');
|
|
47
|
-
// TODO: support comment/image for messages
|
|
52
|
+
const { originalMessages, messagesWithMemory } = await this.prepareMessages(input);
|
|
48
53
|
const llmInputs = {
|
|
49
|
-
messages:
|
|
50
|
-
role,
|
|
51
|
-
content: (0, mustache_utils_1.renderMessage)(content, input),
|
|
52
|
-
})),
|
|
54
|
+
messages: messagesWithMemory,
|
|
53
55
|
modelOptions: definition.modelOptions,
|
|
54
56
|
};
|
|
55
|
-
const
|
|
56
|
-
const textOutput =
|
|
57
|
-
|
|
58
|
-
const outputJsonSchema = jsonOutputs.length ? outputsToJsonSchema(ordered_map_1.OrderedRecord.fromArray(jsonOutputs)) : undefined;
|
|
59
|
-
const jsonOutput = outputJsonSchema
|
|
60
|
-
? model
|
|
61
|
-
.run({
|
|
62
|
-
...llmInputs,
|
|
63
|
-
responseFormat: outputJsonSchema && {
|
|
64
|
-
type: 'json_schema',
|
|
65
|
-
jsonSchema: {
|
|
66
|
-
name: 'output',
|
|
67
|
-
schema: outputJsonSchema,
|
|
68
|
-
strict: true,
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
})
|
|
72
|
-
.then(async (response) => {
|
|
73
|
-
if (!response.$text)
|
|
74
|
-
throw new Error('No text in JSON mode response');
|
|
75
|
-
const json = JSON.parse(response.$text);
|
|
76
|
-
// TODO: validate json with outputJsonSchema
|
|
77
|
-
return json;
|
|
78
|
-
})
|
|
57
|
+
const jsonOutput = this.runWithStructuredOutput(llmInputs);
|
|
58
|
+
const textOutput = ordered_map_1.OrderedRecord.find(definition.outputs, (i) => i.name === constants_1.StreamTextOutputName)
|
|
59
|
+
? await this.runWithTextOutput(llmInputs)
|
|
79
60
|
: undefined;
|
|
61
|
+
const updateMemories = (text, json) => {
|
|
62
|
+
return this.updateMemories([
|
|
63
|
+
...originalMessages,
|
|
64
|
+
{ role: 'assistant', content: (0, mustache_utils_1.renderMessage)('{{text}}\n{{json}}', { text, json }).trim() },
|
|
65
|
+
]);
|
|
66
|
+
};
|
|
80
67
|
if (options?.stream) {
|
|
68
|
+
let $text = '';
|
|
81
69
|
return new ReadableStream({
|
|
82
70
|
start: async (controller) => {
|
|
83
71
|
try {
|
|
84
72
|
if (textOutput) {
|
|
85
|
-
|
|
86
|
-
|
|
73
|
+
for await (const chunk of textOutput) {
|
|
74
|
+
$text += chunk.$text || '';
|
|
87
75
|
controller.enqueue({ $text: chunk.$text });
|
|
88
76
|
}
|
|
89
77
|
}
|
|
90
|
-
|
|
78
|
+
const json = await jsonOutput;
|
|
79
|
+
controller.enqueue({ delta: json });
|
|
80
|
+
await updateMemories($text || undefined, json);
|
|
91
81
|
}
|
|
92
82
|
catch (error) {
|
|
93
83
|
controller.error(error);
|
|
@@ -98,62 +88,185 @@ let LLMAgent = LLMAgent_1 = class LLMAgent extends runnable_1.Runnable {
|
|
|
98
88
|
},
|
|
99
89
|
});
|
|
100
90
|
}
|
|
101
|
-
const text =
|
|
91
|
+
const [$text, json] = await Promise.all([
|
|
92
|
+
textOutput ? (0, utils_1.runnableResponseStreamToObject)(textOutput).then((res) => res.$text || undefined) : undefined,
|
|
93
|
+
jsonOutput,
|
|
94
|
+
]);
|
|
95
|
+
await updateMemories($text, json);
|
|
96
|
+
return { $text, ...json };
|
|
97
|
+
}
|
|
98
|
+
async prepareMessages(input) {
|
|
99
|
+
const { definition } = this;
|
|
100
|
+
const originalMessages = ordered_map_1.OrderedRecord.toArray(definition.messages).map(({ role, content }) => ({
|
|
101
|
+
role,
|
|
102
|
+
// TODO: support use memory variables in message content
|
|
103
|
+
content: typeof content === 'string' ? (0, mustache_utils_1.renderMessage)(content, input) : content,
|
|
104
|
+
}));
|
|
105
|
+
if (!originalMessages.length)
|
|
106
|
+
throw new Error('Messages are required');
|
|
107
|
+
const { primaryMemory, memory } = await this.getMemories(input);
|
|
108
|
+
let messagesWithMemory = [...originalMessages];
|
|
109
|
+
// Add memory to a system message
|
|
110
|
+
if (memory) {
|
|
111
|
+
const message = {
|
|
112
|
+
role: 'system',
|
|
113
|
+
content: `\
|
|
114
|
+
Here are the memories about the user:
|
|
115
|
+
${memory}
|
|
116
|
+
`,
|
|
117
|
+
};
|
|
118
|
+
const lastSystemMessageIndex = messagesWithMemory.findLastIndex((i) => i.role === 'assistant');
|
|
119
|
+
messagesWithMemory.splice(lastSystemMessageIndex + 1, 0, message);
|
|
120
|
+
}
|
|
121
|
+
// Add primary memory to messages
|
|
122
|
+
if (primaryMemory.length)
|
|
123
|
+
messagesWithMemory = (0, message_utils_1.mergeHistoryMessages)(messagesWithMemory, primaryMemory);
|
|
124
|
+
// TODO: support comment/image for messages
|
|
125
|
+
return { originalMessages, messagesWithMemory };
|
|
126
|
+
}
|
|
127
|
+
async runWithStructuredOutput(llmInputs) {
|
|
128
|
+
const jsonOutputs = ordered_map_1.OrderedRecord.filter(this.definition.outputs, (i) => i.name !== constants_1.StreamTextOutputName // ignore `$text` output
|
|
129
|
+
);
|
|
130
|
+
if (!jsonOutputs.length)
|
|
131
|
+
return null;
|
|
132
|
+
const schema = (0, structured_output_schema_1.outputsToJsonSchema)(ordered_map_1.OrderedRecord.fromArray(jsonOutputs));
|
|
133
|
+
const { model } = this;
|
|
134
|
+
if (!model)
|
|
135
|
+
throw new Error('LLM model is required');
|
|
136
|
+
const response = await model.run({
|
|
137
|
+
...llmInputs,
|
|
138
|
+
responseFormat: {
|
|
139
|
+
type: 'json_schema',
|
|
140
|
+
jsonSchema: {
|
|
141
|
+
name: 'output',
|
|
142
|
+
schema: schema,
|
|
143
|
+
strict: true,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
if (!response.$text)
|
|
148
|
+
throw new Error('No text in JSON mode response');
|
|
149
|
+
const json = JSON.parse(response.$text);
|
|
150
|
+
// TODO: validate json with outputJsonSchema
|
|
151
|
+
return json;
|
|
152
|
+
}
|
|
153
|
+
async runWithTextOutput(llmInputs) {
|
|
154
|
+
const { model } = this;
|
|
155
|
+
if (!model)
|
|
156
|
+
throw new Error('LLM model is required');
|
|
157
|
+
return model.run(llmInputs, { stream: true });
|
|
158
|
+
}
|
|
159
|
+
async getMemoryQuery(input, query) {
|
|
160
|
+
if (query?.from === 'variable') {
|
|
161
|
+
const i = ordered_map_1.OrderedRecord.find(this.definition.inputs, (i) => i.id === query.fromVariableId);
|
|
162
|
+
if (!i)
|
|
163
|
+
throw new Error(`Input variable ${query.fromVariableId} not found`);
|
|
164
|
+
const value = input[i.name];
|
|
165
|
+
return (0, mustache_utils_1.renderMessage)('{{value}}', { value });
|
|
166
|
+
}
|
|
167
|
+
return Object.entries(input)
|
|
168
|
+
.map(([key, value]) => `${key} ${value}`)
|
|
169
|
+
.join('\n');
|
|
170
|
+
}
|
|
171
|
+
async getMemories(input) {
|
|
172
|
+
const { memories } = this.definition;
|
|
173
|
+
const { userId, sessionId } = this.context?.state ?? {};
|
|
174
|
+
const list = (await Promise.all(ordered_map_1.OrderedRecord.map(memories, async ({ id, memory, query, options }) => {
|
|
175
|
+
if (!memory) {
|
|
176
|
+
logger_1.default.warn(`Memory is not defined in agent ${this.name || this.id}`);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const q = await this.getMemoryQuery(input, query);
|
|
180
|
+
const { results: memories } = await memory.search(q, { ...options, userId, sessionId });
|
|
181
|
+
return { id, memories };
|
|
182
|
+
}))).filter(utils_1.isNonNullable);
|
|
183
|
+
const primary = (0, remove_1.default)(list, (i) => i.id === this.definition.primaryMemoryId)[0]?.memories || [];
|
|
184
|
+
const primaryMemory = primary
|
|
185
|
+
.map((i) => {
|
|
186
|
+
const content = (0, mustache_utils_1.renderMessage)('{{memory}}', { memory: i.memory }).trim();
|
|
187
|
+
const role = ['user', 'assistant'].includes(i.metadata.role) ? i.metadata.role : undefined;
|
|
188
|
+
if (!role || !content)
|
|
189
|
+
return null;
|
|
190
|
+
return { role, content };
|
|
191
|
+
})
|
|
192
|
+
.filter(utils_1.isNonNullable);
|
|
193
|
+
const memory = list
|
|
194
|
+
.map((i) => i.memories
|
|
195
|
+
.map((j) => (0, mustache_utils_1.renderMessage)('{{memory}}\n{{metadata}}', j).trim() || null)
|
|
196
|
+
.filter(utils_1.isNonNullable)
|
|
197
|
+
.join('\n'))
|
|
198
|
+
.join('\n');
|
|
102
199
|
return {
|
|
103
|
-
|
|
104
|
-
|
|
200
|
+
primaryMemory,
|
|
201
|
+
memory,
|
|
105
202
|
};
|
|
106
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Update memories by user messages and assistant responses.
|
|
206
|
+
* @param messages Messages to be added to memories.
|
|
207
|
+
*/
|
|
208
|
+
async updateMemories(messages) {
|
|
209
|
+
const { memories } = this.definition;
|
|
210
|
+
const { userId, sessionId } = this.context?.state ?? {};
|
|
211
|
+
await Promise.all(ordered_map_1.OrderedRecord.map(memories, async ({ memory }) => {
|
|
212
|
+
if (!memory) {
|
|
213
|
+
logger_1.default.warn(`Memory is not defined in agent ${this.name || this.id}`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
return await memory.add(messages, { userId, sessionId });
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
107
219
|
};
|
|
108
220
|
exports.LLMAgent = LLMAgent;
|
|
109
221
|
exports.LLMAgent = LLMAgent = LLMAgent_1 = __decorate([
|
|
110
222
|
(0, tsyringe_1.injectable)(),
|
|
111
223
|
__param(0, (0, tsyringe_1.inject)(constants_1.TYPES.definition)),
|
|
112
224
|
__param(1, (0, tsyringe_1.inject)(constants_1.TYPES.llmModel)),
|
|
113
|
-
|
|
225
|
+
__param(2, (0, tsyringe_1.inject)(constants_1.TYPES.context)),
|
|
226
|
+
__metadata("design:paramtypes", [Object, llm_model_1.LLMModel, Object])
|
|
114
227
|
], LLMAgent);
|
|
228
|
+
/**
|
|
229
|
+
* Create LLMAgent definition.
|
|
230
|
+
* @param options Options to create LLMAgent.
|
|
231
|
+
* @returns LLMAgent definition.
|
|
232
|
+
*/
|
|
115
233
|
function createLLMAgentDefinition(options) {
|
|
234
|
+
const agentId = options.name || (0, nanoid_1.nanoid)();
|
|
235
|
+
const primaryMemories = options.memories?.filter((i) => i.primary);
|
|
236
|
+
if (primaryMemories && primaryMemories.length > 1) {
|
|
237
|
+
throw new Error('Only one primary memory is allowed');
|
|
238
|
+
}
|
|
239
|
+
const inputs = (0, data_type_schema_1.schemaToDataType)(options.inputs);
|
|
240
|
+
const outputs = (0, data_type_schema_1.schemaToDataType)(options.outputs);
|
|
241
|
+
const memories = ordered_map_1.OrderedRecord.fromArray(options.memories?.map((i) => {
|
|
242
|
+
const { name, memory, query, options } = i;
|
|
243
|
+
const queryFromVariable = query?.fromVariable
|
|
244
|
+
? ordered_map_1.OrderedRecord.find(inputs, (j) => j.name === query.fromVariable)
|
|
245
|
+
: null;
|
|
246
|
+
if (query?.fromVariable && !queryFromVariable)
|
|
247
|
+
throw new Error(`LLMAgent ${agentId} -> Memory ${name} -> Query variable ${query.fromVariable.toString()} not found`);
|
|
248
|
+
return {
|
|
249
|
+
id: name || (0, nanoid_1.nanoid)(),
|
|
250
|
+
name: name,
|
|
251
|
+
memory: memory,
|
|
252
|
+
query: queryFromVariable ? { from: 'variable', fromVariableId: queryFromVariable.id } : undefined,
|
|
253
|
+
options,
|
|
254
|
+
};
|
|
255
|
+
}));
|
|
256
|
+
const messages = ordered_map_1.OrderedRecord.fromArray(options.messages?.map((i) => ({
|
|
257
|
+
id: (0, nanoid_1.nanoid)(),
|
|
258
|
+
role: i.role,
|
|
259
|
+
content: i.content,
|
|
260
|
+
})));
|
|
116
261
|
return {
|
|
117
|
-
id:
|
|
262
|
+
id: agentId,
|
|
118
263
|
name: options.name,
|
|
119
264
|
type: 'llm_agent',
|
|
120
|
-
inputs
|
|
121
|
-
outputs
|
|
265
|
+
inputs,
|
|
266
|
+
outputs,
|
|
267
|
+
primaryMemoryId: primaryMemories?.at(0)?.name,
|
|
268
|
+
memories,
|
|
122
269
|
modelOptions: options.modelOptions,
|
|
123
|
-
messages
|
|
124
|
-
id: (0, nanoid_1.nanoid)(),
|
|
125
|
-
role: i.role,
|
|
126
|
-
content: i.content,
|
|
127
|
-
}))),
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function outputsToJsonSchema(outputs) {
|
|
131
|
-
const outputToSchema = (output) => {
|
|
132
|
-
const properties = output.type === 'object' && output.properties?.$indexes.length
|
|
133
|
-
? ordered_map_1.OrderedRecord.map(output.properties, (property) => {
|
|
134
|
-
if (!property.name)
|
|
135
|
-
return null;
|
|
136
|
-
const schema = outputToSchema(property);
|
|
137
|
-
if (!schema)
|
|
138
|
-
return null;
|
|
139
|
-
return { schema, property };
|
|
140
|
-
}).filter(utils_1.isNonNullable)
|
|
141
|
-
: undefined;
|
|
142
|
-
return (0, lodash_1.omitBy)({
|
|
143
|
-
type: output.type,
|
|
144
|
-
description: output.description,
|
|
145
|
-
properties: properties?.length
|
|
146
|
-
? Object.fromEntries(properties.map((p) => [p.property.name, p.schema]))
|
|
147
|
-
: undefined,
|
|
148
|
-
items: output.type === 'array' && output.items ? outputToSchema(output.items) : undefined,
|
|
149
|
-
additionalProperties: output.type === 'object' ? false : undefined,
|
|
150
|
-
required: properties?.length
|
|
151
|
-
? properties.filter((i) => i.property.required).map((i) => i.property.name)
|
|
152
|
-
: undefined,
|
|
153
|
-
}, (v) => v === undefined);
|
|
270
|
+
messages,
|
|
154
271
|
};
|
|
155
|
-
return outputToSchema({
|
|
156
|
-
type: 'object',
|
|
157
|
-
properties: outputs,
|
|
158
|
-
});
|
|
159
272
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryRunner = exports.Memorable = void 0;
|
|
4
|
+
const lodash_1 = require("lodash");
|
|
5
|
+
const runnable_1 = require("./runnable");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
class Memorable extends runnable_1.Runnable {
|
|
8
|
+
constructor() {
|
|
9
|
+
super({
|
|
10
|
+
id: 'memory',
|
|
11
|
+
type: 'memory',
|
|
12
|
+
name: 'Memory',
|
|
13
|
+
inputs: utils_1.OrderedRecord.fromArray([]),
|
|
14
|
+
outputs: utils_1.OrderedRecord.fromArray([]),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.Memorable = Memorable;
|
|
19
|
+
class MemoryRunner extends runnable_1.Runnable {
|
|
20
|
+
constructor(name) {
|
|
21
|
+
const id = `${(0, lodash_1.camelCase)(name)}_runner`;
|
|
22
|
+
super({
|
|
23
|
+
id,
|
|
24
|
+
type: id,
|
|
25
|
+
name: `${(0, lodash_1.startCase)(name)} Runner`,
|
|
26
|
+
description: `${(0, lodash_1.startCase)(name)} Runner`,
|
|
27
|
+
inputs: utils_1.OrderedRecord.fromArray([]),
|
|
28
|
+
outputs: utils_1.OrderedRecord.fromArray([]),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.MemoryRunner = MemoryRunner;
|