@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.
- package/lib/cjs/agent.js +60 -0
- package/lib/cjs/definitions/data-type-schema.js +46 -0
- package/lib/cjs/definitions/memory.js +21 -0
- package/lib/cjs/function-agent.js +1 -1
- package/lib/cjs/function-runner.js +2 -2
- package/lib/cjs/index.js +1 -1
- package/lib/cjs/llm-agent.js +19 -126
- package/lib/cjs/llm-decision-agent.js +40 -33
- package/lib/cjs/llm-model.js +2 -2
- package/lib/cjs/local-function-agent.js +11 -9
- package/lib/cjs/pipeline-agent.js +2 -4
- package/lib/cjs/runnable.js +3 -1
- package/lib/cjs/tsconfig.tsbuildinfo +1 -1
- package/lib/cjs/utils/message-utils.js +62 -0
- package/lib/cjs/utils/nullable.js +2 -0
- package/lib/cjs/utils/ordered-map.js +12 -0
- package/lib/cjs/utils/stream-utils.js +43 -5
- package/lib/esm/agent.js +53 -0
- package/lib/esm/definitions/data-type-schema.js +43 -0
- package/lib/esm/definitions/memory.js +18 -0
- package/lib/esm/function-agent.js +1 -1
- package/lib/esm/function-runner.js +2 -2
- package/lib/esm/index.js +1 -1
- package/lib/esm/llm-agent.js +21 -125
- package/lib/esm/llm-decision-agent.js +41 -34
- package/lib/esm/llm-model.js +2 -2
- package/lib/esm/local-function-agent.js +11 -9
- package/lib/esm/pipeline-agent.js +2 -4
- package/lib/esm/runnable.js +3 -1
- package/lib/esm/tsconfig.tsbuildinfo +1 -1
- package/lib/esm/utils/message-utils.js +57 -0
- package/lib/esm/utils/nullable.js +1 -0
- package/lib/esm/utils/ordered-map.js +12 -0
- package/lib/esm/utils/stream-utils.js +42 -5
- package/lib/types/agent.d.ts +42 -0
- package/lib/types/definitions/data-type-schema.d.ts +40 -0
- package/lib/types/definitions/memory.d.ts +40 -0
- package/lib/types/function-agent.d.ts +1 -1
- package/lib/types/function-runner.d.ts +2 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/llm-agent.d.ts +34 -63
- package/lib/types/llm-decision-agent.d.ts +54 -30
- package/lib/types/llm-model.d.ts +2 -1
- package/lib/types/local-function-agent.d.ts +50 -19
- package/lib/types/memorable.d.ts +2 -1
- package/lib/types/pipeline-agent.d.ts +2 -3
- package/lib/types/runnable.d.ts +8 -2
- package/lib/types/tsconfig.tsbuildinfo +1 -1
- package/lib/types/utils/message-utils.d.ts +18 -0
- package/lib/types/utils/nullable.d.ts +7 -0
- package/lib/types/utils/ordered-map.d.ts +3 -0
- package/lib/types/utils/stream-utils.d.ts +12 -1
- package/lib/types/utils/union.d.ts +1 -1
- 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
|
+
}
|
|
@@ -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
|
|
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(
|
|
21
|
+
Object.assign(result, value.delta);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
|
-
Object.assign(
|
|
24
|
-
return
|
|
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
|
}
|
package/lib/esm/agent.js
ADDED
|
@@ -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';
|
package/lib/esm/llm-agent.js
CHANGED
|
@@ -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
|
|
21
|
-
import {
|
|
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
|
|
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,
|
|
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
|
|
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 } =
|
|
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.
|
|
217
|
-
__param(2, inject(TYPES.
|
|
218
|
-
__metadata("design:paramtypes", [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 =
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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:
|
|
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 {
|
|
19
|
-
import {
|
|
20
|
-
let LLMDecisionAgent = LLMDecisionAgent_1 = class LLMDecisionAgent extends
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
49
|
+
return { name, description: t.description, runnable };
|
|
52
50
|
}));
|
|
53
51
|
const llmInputs = {
|
|
54
|
-
messages:
|
|
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
|
-
|
|
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.
|
|
88
|
-
__param(2, inject(TYPES.
|
|
89
|
-
__metadata("design:paramtypes", [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
|
|
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:
|
|
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:
|
|
114
|
+
id: agentId,
|
|
108
115
|
name: options.name,
|
|
109
116
|
type: 'llm_decision_agent',
|
|
110
|
-
|
|
111
|
-
|
|
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
|
};
|
package/lib/esm/llm-model.js
CHANGED
|
@@ -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
|
}
|