@aigne/core 0.4.205-0 → 0.4.205-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.
Files changed (54) hide show
  1. package/lib/cjs/agent.js +60 -0
  2. package/lib/cjs/definitions/data-type-schema.js +46 -0
  3. package/lib/cjs/definitions/memory.js +21 -0
  4. package/lib/cjs/function-agent.js +1 -1
  5. package/lib/cjs/function-runner.js +2 -2
  6. package/lib/cjs/index.js +1 -1
  7. package/lib/cjs/llm-agent.js +19 -126
  8. package/lib/cjs/llm-decision-agent.js +40 -33
  9. package/lib/cjs/llm-model.js +2 -2
  10. package/lib/cjs/local-function-agent.js +11 -9
  11. package/lib/cjs/pipeline-agent.js +2 -4
  12. package/lib/cjs/runnable.js +3 -1
  13. package/lib/cjs/tsconfig.tsbuildinfo +1 -1
  14. package/lib/cjs/utils/message-utils.js +62 -0
  15. package/lib/cjs/utils/nullable.js +2 -0
  16. package/lib/cjs/utils/ordered-map.js +12 -0
  17. package/lib/cjs/utils/stream-utils.js +43 -5
  18. package/lib/esm/agent.js +53 -0
  19. package/lib/esm/definitions/data-type-schema.js +43 -0
  20. package/lib/esm/definitions/memory.js +18 -0
  21. package/lib/esm/function-agent.js +1 -1
  22. package/lib/esm/function-runner.js +2 -2
  23. package/lib/esm/index.js +1 -1
  24. package/lib/esm/llm-agent.js +21 -125
  25. package/lib/esm/llm-decision-agent.js +41 -34
  26. package/lib/esm/llm-model.js +2 -2
  27. package/lib/esm/local-function-agent.js +11 -9
  28. package/lib/esm/pipeline-agent.js +2 -4
  29. package/lib/esm/runnable.js +3 -1
  30. package/lib/esm/tsconfig.tsbuildinfo +1 -1
  31. package/lib/esm/utils/message-utils.js +57 -0
  32. package/lib/esm/utils/nullable.js +1 -0
  33. package/lib/esm/utils/ordered-map.js +12 -0
  34. package/lib/esm/utils/stream-utils.js +42 -5
  35. package/lib/types/agent.d.ts +42 -0
  36. package/lib/types/definitions/data-type-schema.d.ts +40 -0
  37. package/lib/types/definitions/memory.d.ts +40 -0
  38. package/lib/types/function-agent.d.ts +1 -1
  39. package/lib/types/function-runner.d.ts +2 -1
  40. package/lib/types/index.d.ts +1 -1
  41. package/lib/types/llm-agent.d.ts +34 -63
  42. package/lib/types/llm-decision-agent.d.ts +54 -30
  43. package/lib/types/llm-model.d.ts +2 -1
  44. package/lib/types/local-function-agent.d.ts +50 -19
  45. package/lib/types/memorable.d.ts +2 -1
  46. package/lib/types/pipeline-agent.d.ts +2 -3
  47. package/lib/types/runnable.d.ts +8 -2
  48. package/lib/types/tsconfig.tsbuildinfo +1 -1
  49. package/lib/types/utils/message-utils.d.ts +18 -0
  50. package/lib/types/utils/nullable.d.ts +7 -0
  51. package/lib/types/utils/ordered-map.d.ts +3 -0
  52. package/lib/types/utils/stream-utils.d.ts +12 -1
  53. package/lib/types/utils/union.d.ts +1 -1
  54. package/package.json +1 -1
@@ -1,6 +1,15 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.mergeHistoryMessages = mergeHistoryMessages;
7
+ exports.memoriesToMessages = memoriesToMessages;
8
+ exports.prepareMessages = prepareMessages;
9
+ const omit_1 = __importDefault(require("lodash/omit"));
10
+ const is_non_nullable_1 = require("./is-non-nullable");
11
+ const mustache_utils_1 = require("./mustache-utils");
12
+ const ordered_map_1 = require("./ordered-map");
4
13
  function mergeHistoryMessages(messages, history) {
5
14
  const firstUserMessageIndex = messages.findIndex((m) => m.role === 'user');
6
15
  if (firstUserMessageIndex >= 0) {
@@ -8,3 +17,56 @@ function mergeHistoryMessages(messages, history) {
8
17
  }
9
18
  return [...history, ...messages];
10
19
  }
20
+ function memoriesToMessages(memories, { primaryMemoryName } = {}) {
21
+ const primary = (primaryMemoryName && memories[primaryMemoryName]) || [];
22
+ const otherMemories = primaryMemoryName ? (0, omit_1.default)(memories, primaryMemoryName) : memories;
23
+ const primaryMemory = primary
24
+ .map((i) => {
25
+ const content = (0, mustache_utils_1.renderMessage)('{{memory}}', { memory: i.memory }).trim();
26
+ const role = ['user', 'assistant'].includes(i.metadata.role) ? i.metadata.role : undefined;
27
+ if (!role || !content)
28
+ return null;
29
+ return { role, content };
30
+ })
31
+ .filter(is_non_nullable_1.isNonNullable);
32
+ const memory = Object.values(otherMemories)
33
+ .map((i) => i
34
+ .map((j) => (0, mustache_utils_1.renderMessage)('{{memory}}\n{{metadata}}', j).trim() || null)
35
+ .filter(is_non_nullable_1.isNonNullable)
36
+ .join('\n'))
37
+ .join('\n');
38
+ return {
39
+ primaryMemory,
40
+ memory,
41
+ };
42
+ }
43
+ function prepareMessages(definition, input, memories) {
44
+ const variables = { ...input, ...memories };
45
+ const originalMessages = ordered_map_1.OrderedRecord.toArray(definition.messages).map(({ role, content }) => ({
46
+ role,
47
+ content: typeof content === 'string' ? (0, mustache_utils_1.renderMessage)(content, variables) : content,
48
+ }));
49
+ if (!originalMessages.length)
50
+ throw new Error('Messages are required');
51
+ const { primaryMemory, memory } = memoriesToMessages(memories, {
52
+ primaryMemoryName: ordered_map_1.OrderedRecord.find(definition.memories, (i) => i.id === definition.primaryMemoryId)?.name,
53
+ });
54
+ let messagesWithMemory = [...originalMessages];
55
+ // Add memory to a system message
56
+ if (memory) {
57
+ const message = {
58
+ role: 'system',
59
+ content: `\
60
+ Here are the memories about the user:
61
+ ${memory}
62
+ `,
63
+ };
64
+ const lastSystemMessageIndex = messagesWithMemory.findLastIndex((i) => i.role === 'assistant');
65
+ messagesWithMemory.splice(lastSystemMessageIndex + 1, 0, message);
66
+ }
67
+ // Add primary memory to messages
68
+ if (primaryMemory.length)
69
+ messagesWithMemory = mergeHistoryMessages(messagesWithMemory, primaryMemory);
70
+ // TODO: support comment/image for messages
71
+ return { originalMessages, messagesWithMemory };
72
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -78,6 +78,18 @@ var OrderedRecord;
78
78
  return record;
79
79
  }
80
80
  OrderedRecord.push = push;
81
+ function merge(...records) {
82
+ const result = { $indexes: [] };
83
+ for (const record of records) {
84
+ for (const id of record.$indexes) {
85
+ if (!result[id])
86
+ result.$indexes.push(id);
87
+ result[id] = record[id];
88
+ }
89
+ }
90
+ return result;
91
+ }
92
+ OrderedRecord.merge = merge;
81
93
  function pushOrUpdate(record, ...items) {
82
94
  for (const item of items) {
83
95
  if (!record[item.id]) {
@@ -2,24 +2,62 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.objectToRunnableResponseStream = objectToRunnableResponseStream;
4
4
  exports.runnableResponseStreamToObject = runnableResponseStreamToObject;
5
+ exports.extractOutputsFromRunnableOutput = extractOutputsFromRunnableOutput;
5
6
  const runnable_1 = require("../runnable");
6
7
  function objectToRunnableResponseStream(obj) {
7
8
  return new ReadableStream({
8
9
  start(controller) {
9
- controller.enqueue({ delta: obj });
10
+ controller.enqueue({ $text: obj['$text'] || undefined, delta: obj });
10
11
  controller.close();
11
12
  },
12
13
  });
13
14
  }
14
15
  async function runnableResponseStreamToObject(stream) {
15
16
  let $text = '';
16
- const lastValue = {};
17
+ const result = {};
17
18
  for await (const value of stream) {
18
19
  if ((0, runnable_1.isRunnableResponseDelta)(value)) {
19
20
  $text += value.$text || '';
20
- Object.assign(lastValue, value.delta);
21
+ Object.assign(result, value.delta);
21
22
  }
22
23
  }
23
- Object.assign(lastValue, { $text: lastValue.$text || $text || undefined });
24
- return lastValue;
24
+ Object.assign(result, { $text: result.$text || $text || undefined });
25
+ return result;
26
+ }
27
+ /**
28
+ * Extracts the outputs from a runnable output stream and run the
29
+ * resolve function on the result before the stream closes. It can be
30
+ * used to update the memories of an agent.
31
+ * @param output The runnable output stream or object
32
+ * @param resolve The function to run on the result
33
+ * @returns The runnable output stream or object
34
+ */
35
+ async function extractOutputsFromRunnableOutput(output, resolve) {
36
+ if (!(output instanceof ReadableStream)) {
37
+ await resolve(output);
38
+ return output;
39
+ }
40
+ return new ReadableStream({
41
+ async start(controller) {
42
+ try {
43
+ let result = {};
44
+ let $text = '';
45
+ for await (const value of output) {
46
+ if ((0, runnable_1.isRunnableResponseDelta)(value)) {
47
+ $text += value.$text || '';
48
+ Object.assign(result, value.delta);
49
+ }
50
+ }
51
+ Object.assign(result, { $text: result.$text || $text || undefined });
52
+ controller.enqueue({ $text: result.$text, delta: result });
53
+ await resolve(result);
54
+ }
55
+ catch (error) {
56
+ controller.error(error);
57
+ }
58
+ finally {
59
+ controller.close();
60
+ }
61
+ },
62
+ });
25
63
  }
@@ -0,0 +1,53 @@
1
+ import logger from './logger';
2
+ import { Runnable } from './runnable';
3
+ import { OrderedRecord, isNonNullable, renderMessage } from './utils';
4
+ export class Agent extends Runnable {
5
+ async getMemoryQuery(input, query) {
6
+ if (query?.from === 'variable') {
7
+ const i = OrderedRecord.find(this.definition.inputs, (i) => i.id === query.fromVariableId);
8
+ if (!i)
9
+ throw new Error(`Input variable ${query.fromVariableId} not found`);
10
+ const value = input[i.name];
11
+ return renderMessage('{{value}}', { value });
12
+ }
13
+ return Object.entries(input)
14
+ .map(([key, value]) => `${key} ${value}`)
15
+ .join('\n');
16
+ }
17
+ /**
18
+ * Load memories that are defined in the agent definition.
19
+ * @param input The agent input.
20
+ * @param context The AIGNE context.
21
+ * @returns A dictionary of memories, where the key is the memory name and the value is an array of memory items.
22
+ */
23
+ async loadMemories(input, context) {
24
+ const { memories } = this.definition;
25
+ const { userId, sessionId } = context?.state ?? {};
26
+ return Object.fromEntries((await Promise.all(OrderedRecord.map(memories, async ({ name, memory, query, options }) => {
27
+ if (!name || !memory)
28
+ return null;
29
+ const q = await this.getMemoryQuery(input, query);
30
+ const { results: memories } = await memory.search(q, { ...options, userId, sessionId });
31
+ return [name, memories];
32
+ }))).filter(isNonNullable));
33
+ }
34
+ /**
35
+ * Update memories by user messages and assistant responses.
36
+ * @param messages Messages to be added to memories.
37
+ */
38
+ async updateMemories(messages) {
39
+ const { memories } = this.definition;
40
+ const { userId, sessionId } = this.context?.state ?? {};
41
+ await Promise.all(OrderedRecord.map(memories, async ({ memory }) => {
42
+ if (!memory) {
43
+ logger.warn(`Memory is not defined in agent ${this.name || this.id}`);
44
+ return;
45
+ }
46
+ return await memory.add(messages, { userId, sessionId });
47
+ }));
48
+ }
49
+ async run(input, options) {
50
+ const memories = await this.loadMemories(input, this.context);
51
+ return this.process(input, { ...options, memories });
52
+ }
53
+ }
@@ -0,0 +1,43 @@
1
+ import { nanoid } from 'nanoid';
2
+ import { OrderedRecord } from '../utils';
3
+ export function schemaToDataType(dataType) {
4
+ return OrderedRecord.fromArray(Object.entries(dataType).map(([name, schema]) => {
5
+ const base = {
6
+ ...schema,
7
+ id: nanoid(),
8
+ name,
9
+ };
10
+ switch (schema.type) {
11
+ case 'string':
12
+ return {
13
+ ...base,
14
+ type: 'string',
15
+ };
16
+ case 'number':
17
+ return {
18
+ ...base,
19
+ type: 'number',
20
+ };
21
+ case 'boolean':
22
+ return {
23
+ ...base,
24
+ type: 'boolean',
25
+ };
26
+ case 'object':
27
+ return {
28
+ ...base,
29
+ type: 'object',
30
+ properties: schemaToDataType(schema.properties),
31
+ };
32
+ case 'array':
33
+ return {
34
+ ...base,
35
+ type: 'array',
36
+ items: OrderedRecord.find(schemaToDataType({ items: schema.items }), (i) => i.name === 'items'),
37
+ };
38
+ default: {
39
+ throw new Error(`Unknown data type: ${schema.type}`);
40
+ }
41
+ }
42
+ }));
43
+ }
@@ -0,0 +1,18 @@
1
+ import { nanoid } from 'nanoid';
2
+ import { OrderedRecord } from '../utils';
3
+ export function toRunnableMemories(agentName, inputs, memories) {
4
+ return OrderedRecord.fromArray(Object.entries(memories).map(([name, { memory, query, options }]) => {
5
+ const queryFromVariable = query?.fromVariable
6
+ ? OrderedRecord.find(inputs, (j) => j.name === query.fromVariable)
7
+ : null;
8
+ if (query?.fromVariable && !queryFromVariable)
9
+ throw new Error(`LLMAgent ${agentName} -> Memory ${name} -> Query variable ${query.fromVariable.toString()} not found`);
10
+ return {
11
+ id: name || nanoid(),
12
+ name: name,
13
+ memory: memory,
14
+ query: queryFromVariable ? { from: 'variable', fromVariableId: queryFromVariable.id } : undefined,
15
+ options,
16
+ };
17
+ }));
18
+ }
@@ -14,7 +14,7 @@ var FunctionAgent_1;
14
14
  import { nanoid } from 'nanoid';
15
15
  import { inject, injectable } from 'tsyringe';
16
16
  import { TYPES } from './constants';
17
- import { schemaToDataType } from './data-type-schema';
17
+ import { schemaToDataType } from './definitions/data-type-schema';
18
18
  import { FunctionRunner } from './function-runner';
19
19
  import { Runnable } from './runnable';
20
20
  import { objectToRunnableResponseStream } from './utils';
@@ -1,7 +1,7 @@
1
1
  import { Runnable } from './runnable';
2
2
  import { OrderedRecord } from './utils';
3
3
  export class FunctionRunner extends Runnable {
4
- constructor() {
4
+ constructor(context) {
5
5
  super({
6
6
  id: 'function_runner',
7
7
  type: 'function_runner',
@@ -20,6 +20,6 @@ export class FunctionRunner extends Runnable {
20
20
  type: 'object',
21
21
  },
22
22
  ]),
23
- });
23
+ }, context);
24
24
  }
25
25
  }
package/lib/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export * from './utils';
2
2
  export * from './constants';
3
3
  export * from './data-type';
4
- export * from './data-type-schema';
4
+ export * from './definitions/data-type-schema';
5
5
  export * from './context';
6
6
  export * from './runnable';
7
7
  export * from './pipeline-agent';
@@ -11,38 +11,35 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
11
  return function (target, key) { decorator(target, key, paramIndex); }
12
12
  };
13
13
  var LLMAgent_1;
14
- import remove from 'lodash/remove';
15
14
  import { nanoid } from 'nanoid';
16
15
  import { inject, injectable } from 'tsyringe';
16
+ import { Agent } from './agent';
17
17
  import { StreamTextOutputName, TYPES } from './constants';
18
- import { schemaToDataType } from './data-type-schema';
18
+ import { schemaToDataType } from './definitions/data-type-schema';
19
+ import { toRunnableMemories } from './definitions/memory';
19
20
  import { LLMModel } from './llm-model';
20
- import logger from './logger';
21
- import { Runnable, } from './runnable';
22
- import { isNonNullable, runnableResponseStreamToObject } from './utils';
23
- import { mergeHistoryMessages } from './utils/message-utils';
21
+ import { runnableResponseStreamToObject } from './utils';
22
+ import { prepareMessages } from './utils/message-utils';
24
23
  import { renderMessage } from './utils/mustache-utils';
25
24
  import { OrderedRecord } from './utils/ordered-map';
26
25
  import { outputsToJsonSchema } from './utils/structured-output-schema';
27
- let LLMAgent = LLMAgent_1 = class LLMAgent extends Runnable {
26
+ let LLMAgent = LLMAgent_1 = class LLMAgent extends Agent {
28
27
  definition;
29
28
  model;
30
- context;
31
29
  static create(options) {
32
30
  const definition = createLLMAgentDefinition(options);
33
31
  return new LLMAgent_1(definition);
34
32
  }
35
- constructor(definition, model, context) {
36
- super(definition);
33
+ constructor(definition, context, model) {
34
+ super(definition, context);
37
35
  this.definition = definition;
38
36
  this.model = model;
39
- this.context = context;
40
37
  }
41
- async run(input, options) {
38
+ async process(input, options) {
42
39
  const { definition, model } = this;
43
40
  if (!model)
44
41
  throw new Error('LLM model is required');
45
- const { originalMessages, messagesWithMemory } = await this.prepareMessages(input);
42
+ const { originalMessages, messagesWithMemory } = prepareMessages(definition, input, options.memories);
46
43
  const llmInputs = {
47
44
  messages: messagesWithMemory,
48
45
  modelOptions: definition.modelOptions,
@@ -88,35 +85,6 @@ let LLMAgent = LLMAgent_1 = class LLMAgent extends Runnable {
88
85
  await updateMemories($text, json);
89
86
  return { $text, ...json };
90
87
  }
91
- async prepareMessages(input) {
92
- const { definition } = this;
93
- const originalMessages = OrderedRecord.toArray(definition.messages).map(({ role, content }) => ({
94
- role,
95
- // TODO: support use memory variables in message content
96
- content: typeof content === 'string' ? renderMessage(content, input) : content,
97
- }));
98
- if (!originalMessages.length)
99
- throw new Error('Messages are required');
100
- const { primaryMemory, memory } = await this.getMemories(input);
101
- let messagesWithMemory = [...originalMessages];
102
- // Add memory to a system message
103
- if (memory) {
104
- const message = {
105
- role: 'system',
106
- content: `\
107
- Here are the memories about the user:
108
- ${memory}
109
- `,
110
- };
111
- const lastSystemMessageIndex = messagesWithMemory.findLastIndex((i) => i.role === 'assistant');
112
- messagesWithMemory.splice(lastSystemMessageIndex + 1, 0, message);
113
- }
114
- // Add primary memory to messages
115
- if (primaryMemory.length)
116
- messagesWithMemory = mergeHistoryMessages(messagesWithMemory, primaryMemory);
117
- // TODO: support comment/image for messages
118
- return { originalMessages, messagesWithMemory };
119
- }
120
88
  async runWithStructuredOutput(llmInputs) {
121
89
  const jsonOutputs = OrderedRecord.filter(this.definition.outputs, (i) => i.name !== StreamTextOutputName // ignore `$text` output
122
90
  );
@@ -149,73 +117,13 @@ let LLMAgent = LLMAgent_1 = class LLMAgent extends Runnable {
149
117
  throw new Error('LLM model is required');
150
118
  return model.run(llmInputs, { stream: true });
151
119
  }
152
- async getMemoryQuery(input, query) {
153
- if (query?.from === 'variable') {
154
- const i = OrderedRecord.find(this.definition.inputs, (i) => i.id === query.fromVariableId);
155
- if (!i)
156
- throw new Error(`Input variable ${query.fromVariableId} not found`);
157
- const value = input[i.name];
158
- return renderMessage('{{value}}', { value });
159
- }
160
- return Object.entries(input)
161
- .map(([key, value]) => `${key} ${value}`)
162
- .join('\n');
163
- }
164
- async getMemories(input) {
165
- const { memories } = this.definition;
166
- const { userId, sessionId } = this.context?.state ?? {};
167
- const list = (await Promise.all(OrderedRecord.map(memories, async ({ id, memory, query, options }) => {
168
- if (!memory) {
169
- logger.warn(`Memory is not defined in agent ${this.name || this.id}`);
170
- return null;
171
- }
172
- const q = await this.getMemoryQuery(input, query);
173
- const { results: memories } = await memory.search(q, { ...options, userId, sessionId });
174
- return { id, memories };
175
- }))).filter(isNonNullable);
176
- const primary = remove(list, (i) => i.id === this.definition.primaryMemoryId)[0]?.memories || [];
177
- const primaryMemory = primary
178
- .map((i) => {
179
- const content = renderMessage('{{memory}}', { memory: i.memory }).trim();
180
- const role = ['user', 'assistant'].includes(i.metadata.role) ? i.metadata.role : undefined;
181
- if (!role || !content)
182
- return null;
183
- return { role, content };
184
- })
185
- .filter(isNonNullable);
186
- const memory = list
187
- .map((i) => i.memories
188
- .map((j) => renderMessage('{{memory}}\n{{metadata}}', j).trim() || null)
189
- .filter(isNonNullable)
190
- .join('\n'))
191
- .join('\n');
192
- return {
193
- primaryMemory,
194
- memory,
195
- };
196
- }
197
- /**
198
- * Update memories by user messages and assistant responses.
199
- * @param messages Messages to be added to memories.
200
- */
201
- async updateMemories(messages) {
202
- const { memories } = this.definition;
203
- const { userId, sessionId } = this.context?.state ?? {};
204
- await Promise.all(OrderedRecord.map(memories, async ({ memory }) => {
205
- if (!memory) {
206
- logger.warn(`Memory is not defined in agent ${this.name || this.id}`);
207
- return;
208
- }
209
- return await memory.add(messages, { userId, sessionId });
210
- }));
211
- }
212
120
  };
213
121
  LLMAgent = LLMAgent_1 = __decorate([
214
122
  injectable(),
215
123
  __param(0, inject(TYPES.definition)),
216
- __param(1, inject(TYPES.llmModel)),
217
- __param(2, inject(TYPES.context)),
218
- __metadata("design:paramtypes", [Object, LLMModel, Object])
124
+ __param(1, inject(TYPES.context)),
125
+ __param(2, inject(TYPES.llmModel)),
126
+ __metadata("design:paramtypes", [Object, Object, LLMModel])
219
127
  ], LLMAgent);
220
128
  export { LLMAgent };
221
129
  /**
@@ -225,27 +133,15 @@ export { LLMAgent };
225
133
  */
226
134
  export function createLLMAgentDefinition(options) {
227
135
  const agentId = options.name || nanoid();
228
- const primaryMemories = options.memories?.filter((i) => i.primary);
229
- if (primaryMemories && primaryMemories.length > 1) {
230
- throw new Error('Only one primary memory is allowed');
231
- }
232
136
  const inputs = schemaToDataType(options.inputs);
233
137
  const outputs = schemaToDataType(options.outputs);
234
- const memories = OrderedRecord.fromArray(options.memories?.map((i) => {
235
- const { name, memory, query, options } = i;
236
- const queryFromVariable = query?.fromVariable
237
- ? OrderedRecord.find(inputs, (j) => j.name === query.fromVariable)
238
- : null;
239
- if (query?.fromVariable && !queryFromVariable)
240
- throw new Error(`LLMAgent ${agentId} -> Memory ${name} -> Query variable ${query.fromVariable.toString()} not found`);
241
- return {
242
- id: name || nanoid(),
243
- name: name,
244
- memory: memory,
245
- query: queryFromVariable ? { from: 'variable', fromVariableId: queryFromVariable.id } : undefined,
246
- options,
247
- };
248
- }));
138
+ const memories = toRunnableMemories(agentId, inputs, options.memories ?? {});
139
+ const primaryMemoryNames = Object.entries(options.memories ?? {})
140
+ .filter(([, i]) => i.primary)
141
+ .map(([name]) => name);
142
+ if (primaryMemoryNames && primaryMemoryNames.length > 1) {
143
+ throw new Error('Only one primary memory is allowed');
144
+ }
249
145
  const messages = OrderedRecord.fromArray(options.messages?.map((i) => ({
250
146
  id: nanoid(),
251
147
  role: i.role,
@@ -257,7 +153,7 @@ export function createLLMAgentDefinition(options) {
257
153
  type: 'llm_agent',
258
154
  inputs,
259
155
  outputs,
260
- primaryMemoryId: primaryMemories?.at(0)?.name,
156
+ primaryMemoryId: primaryMemoryNames?.at(0),
261
157
  memories,
262
158
  modelOptions: options.modelOptions,
263
159
  messages,
@@ -13,33 +13,31 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
13
13
  var LLMDecisionAgent_1;
14
14
  import { nanoid } from 'nanoid';
15
15
  import { inject, injectable } from 'tsyringe';
16
+ import { Agent } from './agent';
16
17
  import { TYPES } from './constants';
18
+ import { toRunnableMemories } from './definitions/memory';
17
19
  import { LLMModel } from './llm-model';
18
- import { Runnable } from './runnable';
19
- import { OrderedRecord, renderMessage } from './utils';
20
- let LLMDecisionAgent = LLMDecisionAgent_1 = class LLMDecisionAgent extends Runnable {
20
+ import { OrderedRecord, extractOutputsFromRunnableOutput, renderMessage } from './utils';
21
+ import { prepareMessages } from './utils/message-utils';
22
+ let LLMDecisionAgent = LLMDecisionAgent_1 = class LLMDecisionAgent extends Agent {
21
23
  definition;
22
24
  model;
23
- context;
24
25
  static create(options) {
25
26
  const definition = createLLMDecisionAgentDefinition(options);
26
27
  return new LLMDecisionAgent_1(definition);
27
28
  }
28
- constructor(definition, model, context) {
29
- super(definition);
29
+ constructor(definition, context, model) {
30
+ super(definition, context);
30
31
  this.definition = definition;
31
32
  this.model = model;
32
- this.context = context;
33
33
  }
34
- async run(input, options) {
34
+ async process(input, options) {
35
35
  const { definition, context, model } = this;
36
36
  if (!model)
37
37
  throw new Error('LLM model is required');
38
38
  if (!context)
39
39
  throw new Error('Context is required');
40
- const messages = OrderedRecord.toArray(definition.messages);
41
- if (!messages.length)
42
- throw new Error('Messages are required');
40
+ const { originalMessages, messagesWithMemory } = prepareMessages(definition, input, options.memories);
43
41
  const cases = await Promise.all(OrderedRecord.map(definition.cases, async (t) => {
44
42
  if (!t.runnable?.id)
45
43
  throw new Error('Runnable is required');
@@ -48,13 +46,10 @@ let LLMDecisionAgent = LLMDecisionAgent_1 = class LLMDecisionAgent extends Runna
48
46
  const name = t.name || runnable.name;
49
47
  if (!name)
50
48
  throw new Error('Case name is required');
51
- return { name, description: t.description, runnable, input: t.input };
49
+ return { name, description: t.description, runnable };
52
50
  }));
53
51
  const llmInputs = {
54
- messages: messages.map(({ role, content }) => ({
55
- role,
56
- content: typeof content === 'string' ? renderMessage(content, input) : content,
57
- })),
52
+ messages: messagesWithMemory,
58
53
  modelOptions: definition.modelOptions,
59
54
  tools: cases.map((t) => {
60
55
  // TODO: auto generate parameters by llm model if needed
@@ -78,40 +73,52 @@ let LLMDecisionAgent = LLMDecisionAgent_1 = class LLMDecisionAgent extends Runna
78
73
  if (!caseToCall)
79
74
  throw new Error('Case not found');
80
75
  // TODO: check result structure and omit undefined values
81
- return (await caseToCall.runnable.run(input, options));
76
+ const output = (await caseToCall.runnable.run(input, options));
77
+ return extractOutputsFromRunnableOutput(output, ({ $text, ...json }) => this.updateMemories([
78
+ ...originalMessages,
79
+ { role: 'assistant', content: renderMessage('{{$text}}\n{{json}}', { $text, json }).trim() },
80
+ ]));
82
81
  }
83
82
  };
84
83
  LLMDecisionAgent = LLMDecisionAgent_1 = __decorate([
85
84
  injectable(),
86
85
  __param(0, inject(TYPES.definition)),
87
- __param(1, inject(TYPES.llmModel)),
88
- __param(2, inject(TYPES.context)),
89
- __metadata("design:paramtypes", [Object, LLMModel, Object])
86
+ __param(1, inject(TYPES.context)),
87
+ __param(2, inject(TYPES.llmModel)),
88
+ __metadata("design:paramtypes", [Object, Object, LLMModel])
90
89
  ], LLMDecisionAgent);
91
90
  export { LLMDecisionAgent };
92
91
  export function createLLMDecisionAgentDefinition(options) {
93
- const messages = OrderedRecord.fromArray([
94
- {
95
- id: nanoid(),
96
- role: 'system',
97
- content: options.messages,
98
- },
99
- ]);
100
- const cases = OrderedRecord.fromArray(options.cases.map((c) => ({
92
+ const agentId = options.name || nanoid();
93
+ const cases = OrderedRecord.fromArray(Object.entries(options.cases).map(([name, c]) => ({
101
94
  id: nanoid(),
102
- name: c.name || c.runnable.name,
95
+ name: name || c.runnable.name,
103
96
  description: c.description,
104
97
  runnable: { id: c.runnable.id },
105
98
  })));
99
+ const inputs = OrderedRecord.merge(...Object.values(options.cases).map((i) => i.runnable.definition.inputs));
100
+ const outputs = OrderedRecord.fromArray(OrderedRecord.map(OrderedRecord.merge(...Object.values(options.cases).map((i) => i.runnable.definition.outputs)), (o) => ({ ...o, required: false })));
101
+ const memories = toRunnableMemories(agentId, inputs, options.memories ?? {});
102
+ const primaryMemoryNames = Object.entries(options.memories ?? {})
103
+ .filter(([, i]) => i.primary)
104
+ .map(([name]) => name);
105
+ if (primaryMemoryNames && primaryMemoryNames.length > 1) {
106
+ throw new Error('Only one primary memory is allowed');
107
+ }
108
+ const messages = OrderedRecord.fromArray(options.messages?.map((i) => ({
109
+ id: nanoid(),
110
+ role: i.role,
111
+ content: i.content,
112
+ })));
106
113
  return {
107
- id: options.id || options.name || nanoid(),
114
+ id: agentId,
108
115
  name: options.name,
109
116
  type: 'llm_decision_agent',
110
- // TODO: decision agent inputs should be the intersection of all case inputs
111
- inputs: OrderedRecord.fromArray([]),
112
- // TODO: decision agent outputs should be the union of all case outputs
113
- outputs: OrderedRecord.fromArray([]),
117
+ inputs,
118
+ outputs,
114
119
  messages,
120
+ primaryMemoryId: primaryMemoryNames?.at(0),
121
+ memories,
115
122
  modelOptions: options.modelOptions,
116
123
  cases,
117
124
  };
@@ -1,7 +1,7 @@
1
1
  import { Runnable } from './runnable';
2
2
  import { OrderedRecord } from './utils';
3
3
  export class LLMModel extends Runnable {
4
- constructor() {
4
+ constructor(context) {
5
5
  super({
6
6
  id: 'llm_model',
7
7
  type: 'llm_model',
@@ -18,6 +18,6 @@ export class LLMModel extends Runnable {
18
18
  { id: '$text', name: '$text', type: 'string' },
19
19
  { id: 'toolCalls', name: 'toolCalls', type: 'object' },
20
20
  ]),
21
- });
21
+ }, context);
22
22
  }
23
23
  }