@aigne/core 0.4.194 → 0.4.196
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/constants.js +11 -0
- package/lib/cjs/function-agent.js +81 -0
- package/lib/cjs/function-runner.js +29 -0
- package/lib/cjs/index.js +13 -1
- package/lib/cjs/llm-agent.js +166 -0
- package/lib/cjs/llm-decision-agent.js +169 -0
- package/lib/cjs/llm-model.js +27 -0
- package/lib/cjs/local-function-agent.js +80 -0
- package/lib/cjs/memory.js +32 -0
- package/lib/cjs/pipeline-agent.js +199 -0
- package/lib/cjs/runnable.js +29 -0
- package/lib/cjs/tsconfig.tsbuildinfo +1 -0
- package/lib/cjs/{types → utils}/index.js +4 -2
- package/lib/cjs/utils/is-non-nullable.js +20 -0
- package/lib/cjs/utils/mustache-utils.js +14 -0
- package/lib/cjs/utils/omit.js +2 -0
- package/lib/cjs/utils/ordered-map.js +71 -0
- package/lib/cjs/utils/stream-utils.js +25 -0
- package/lib/esm/constants.js +8 -0
- package/lib/esm/function-agent.js +77 -0
- package/lib/esm/function-runner.js +25 -0
- package/lib/esm/index.js +13 -1
- package/lib/esm/llm-agent.js +162 -0
- package/lib/esm/llm-decision-agent.js +165 -0
- package/lib/esm/llm-model.js +23 -0
- package/lib/esm/local-function-agent.js +76 -0
- package/lib/esm/memory.js +27 -0
- package/lib/esm/pipeline-agent.js +192 -0
- package/lib/esm/runnable.js +23 -0
- package/lib/esm/tsconfig.tsbuildinfo +1 -0
- package/lib/esm/utils/index.js +4 -0
- package/lib/esm/utils/is-non-nullable.js +13 -0
- package/lib/esm/utils/mustache-utils.js +8 -0
- package/lib/esm/utils/omit.js +1 -0
- package/lib/esm/utils/ordered-map.js +68 -0
- package/lib/esm/utils/stream-utils.js +21 -0
- package/lib/types/constants.d.ts +8 -0
- package/lib/types/context.d.ts +5 -0
- package/lib/types/data-type.d.ts +32 -0
- package/lib/types/function-agent.d.ts +36 -0
- package/lib/types/function-runner.d.ts +11 -0
- package/lib/types/index.d.ts +13 -1
- package/lib/types/llm-agent.d.ts +37 -0
- package/lib/types/llm-decision-agent.d.ts +66 -0
- package/lib/types/llm-model.d.ts +79 -0
- package/lib/types/local-function-agent.d.ts +38 -0
- package/lib/types/memory.d.ts +186 -0
- package/lib/types/pipeline-agent.d.ts +69 -0
- package/lib/types/runnable.d.ts +49 -0
- package/lib/types/tsconfig.tsbuildinfo +1 -0
- package/lib/types/utils/index.d.ts +4 -0
- package/lib/types/utils/is-non-nullable.d.ts +2 -0
- package/lib/types/utils/mustache-utils.d.ts +3 -0
- package/lib/types/utils/omit.d.ts +1 -0
- package/lib/types/utils/ordered-map.d.ts +28 -0
- package/lib/types/utils/stream-utils.d.ts +7 -0
- package/package.json +6 -4
- package/tsconfig.json +3 -7
- package/lib/esm/types/index.js +0 -2
- package/lib/types/types/agent.d.ts +0 -5
- package/lib/types/types/index.d.ts +0 -2
- package/lib/types/types/runnable.d.ts +0 -20
- /package/lib/cjs/{types/agent.js → context.js} +0 -0
- /package/lib/cjs/{types/runnable.js → data-type.js} +0 -0
- /package/lib/esm/{types/agent.js → context.js} +0 -0
- /package/lib/esm/{types/runnable.js → data-type.js} +0 -0
|
@@ -14,5 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
18
|
-
__exportStar(require("./
|
|
17
|
+
__exportStar(require("./ordered-map"), exports);
|
|
18
|
+
__exportStar(require("./is-non-nullable"), exports);
|
|
19
|
+
__exportStar(require("./stream-utils"), exports);
|
|
20
|
+
__exportStar(require("./mustache-utils"), exports);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isNonNullable = isNonNullable;
|
|
7
|
+
exports.isPropsNonNullable = isPropsNonNullable;
|
|
8
|
+
const isNil_1 = __importDefault(require("lodash/isNil"));
|
|
9
|
+
function isNonNullable(value) {
|
|
10
|
+
return !(0, isNil_1.default)(value);
|
|
11
|
+
}
|
|
12
|
+
function isPropsNonNullable(...props) {
|
|
13
|
+
return (value) => {
|
|
14
|
+
for (const prop of props.flat()) {
|
|
15
|
+
if ((0, isNil_1.default)(value?.[prop]))
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderMessage = renderMessage;
|
|
7
|
+
const mustache_1 = __importDefault(require("mustache"));
|
|
8
|
+
function renderMessage(template, variables) {
|
|
9
|
+
return mustache_1.default.render(template, variables, undefined, {
|
|
10
|
+
escape: (v) => {
|
|
11
|
+
return typeof v === 'object' ? JSON.stringify(v) : v;
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OrderedRecord = void 0;
|
|
4
|
+
var OrderedRecord;
|
|
5
|
+
(function (OrderedRecord) {
|
|
6
|
+
function iterator(record) {
|
|
7
|
+
return (function* () {
|
|
8
|
+
if (!record)
|
|
9
|
+
return;
|
|
10
|
+
for (const id of record.$indexes) {
|
|
11
|
+
yield record[id];
|
|
12
|
+
}
|
|
13
|
+
})();
|
|
14
|
+
}
|
|
15
|
+
OrderedRecord.iterator = iterator;
|
|
16
|
+
function map(record, fn) {
|
|
17
|
+
if (!record)
|
|
18
|
+
return [];
|
|
19
|
+
const result = new Array(record.$indexes.length);
|
|
20
|
+
for (let i = 0; i < record.$indexes.length; i++) {
|
|
21
|
+
result[i] = fn(record[record.$indexes[i]], i);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
OrderedRecord.map = map;
|
|
26
|
+
function toArray(record) {
|
|
27
|
+
return OrderedRecord.map(record, (value) => value);
|
|
28
|
+
}
|
|
29
|
+
OrderedRecord.toArray = toArray;
|
|
30
|
+
function fromArray(array) {
|
|
31
|
+
const record = { $indexes: [] };
|
|
32
|
+
for (const value of array ?? []) {
|
|
33
|
+
record[value.id] = value;
|
|
34
|
+
record.$indexes.push(value.id);
|
|
35
|
+
}
|
|
36
|
+
return record;
|
|
37
|
+
}
|
|
38
|
+
OrderedRecord.fromArray = fromArray;
|
|
39
|
+
function find(record, predicate) {
|
|
40
|
+
if (!record)
|
|
41
|
+
return undefined;
|
|
42
|
+
for (let i = 0; i < record.$indexes.length; i++) {
|
|
43
|
+
const id = record.$indexes[i];
|
|
44
|
+
const value = record[id];
|
|
45
|
+
if (predicate(value, i))
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
OrderedRecord.find = find;
|
|
51
|
+
function push(record, ...items) {
|
|
52
|
+
for (const item of items) {
|
|
53
|
+
if (record[item.id])
|
|
54
|
+
throw new Error(`Item with id ${item.id} already exists`);
|
|
55
|
+
record.$indexes.push(item.id);
|
|
56
|
+
record[item.id] = item;
|
|
57
|
+
}
|
|
58
|
+
return record;
|
|
59
|
+
}
|
|
60
|
+
OrderedRecord.push = push;
|
|
61
|
+
function pushOrUpdate(record, ...items) {
|
|
62
|
+
for (const item of items) {
|
|
63
|
+
if (!record[item.id]) {
|
|
64
|
+
record.$indexes.push(item.id);
|
|
65
|
+
}
|
|
66
|
+
record[item.id] = item;
|
|
67
|
+
}
|
|
68
|
+
return record;
|
|
69
|
+
}
|
|
70
|
+
OrderedRecord.pushOrUpdate = pushOrUpdate;
|
|
71
|
+
})(OrderedRecord || (exports.OrderedRecord = OrderedRecord = {}));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.objectToRunnableResponseStream = objectToRunnableResponseStream;
|
|
4
|
+
exports.runnableResponseStreamToObject = runnableResponseStreamToObject;
|
|
5
|
+
const runnable_1 = require("../runnable");
|
|
6
|
+
function objectToRunnableResponseStream(obj) {
|
|
7
|
+
return new ReadableStream({
|
|
8
|
+
start(controller) {
|
|
9
|
+
controller.enqueue({ delta: obj });
|
|
10
|
+
controller.close();
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
async function runnableResponseStreamToObject(stream) {
|
|
15
|
+
let $text = '';
|
|
16
|
+
const lastValue = {};
|
|
17
|
+
for await (const value of stream) {
|
|
18
|
+
if ((0, runnable_1.isRunnableResponseDelta)(value)) {
|
|
19
|
+
$text += value.$text || '';
|
|
20
|
+
Object.assign(lastValue, value.delta);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
Object.assign(lastValue, { $text: lastValue.$text || $text || undefined });
|
|
24
|
+
return lastValue;
|
|
25
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const TYPES = {
|
|
2
|
+
context: Symbol.for('AIGNE_CONTEXT'),
|
|
3
|
+
definition: Symbol.for('AIGNE_DEFINITION'),
|
|
4
|
+
llmModel: Symbol.for('AIGNE_LLM_MODEL'),
|
|
5
|
+
llmModelConfiguration: Symbol.for('AIGNE_LLM_MODEL_CONFIGURATION'),
|
|
6
|
+
functionRunner: Symbol.for('AIGNE_FUNCTION_RUNNER'),
|
|
7
|
+
};
|
|
8
|
+
export const StreamTextOutputName = '$text';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var FunctionAgent_1;
|
|
14
|
+
import { nanoid } from 'nanoid';
|
|
15
|
+
import { inject, injectable } from 'tsyringe';
|
|
16
|
+
import { TYPES } from './constants';
|
|
17
|
+
import { FunctionRunner } from './function-runner';
|
|
18
|
+
import { Runnable, } from './runnable';
|
|
19
|
+
import { OrderedRecord, objectToRunnableResponseStream } from './utils';
|
|
20
|
+
let FunctionAgent = FunctionAgent_1 = class FunctionAgent extends Runnable {
|
|
21
|
+
definition;
|
|
22
|
+
runner;
|
|
23
|
+
static create(options) {
|
|
24
|
+
const definition = createFunctionAgentDefinition(options);
|
|
25
|
+
return new FunctionAgent_1(definition);
|
|
26
|
+
}
|
|
27
|
+
constructor(definition, runner) {
|
|
28
|
+
super(definition);
|
|
29
|
+
this.definition = definition;
|
|
30
|
+
this.runner = runner;
|
|
31
|
+
}
|
|
32
|
+
async run(input, options) {
|
|
33
|
+
const { definition: { language, code, ...definition }, runner, } = this;
|
|
34
|
+
if (!runner)
|
|
35
|
+
throw new Error('Function runner is required');
|
|
36
|
+
if (!language || !code)
|
|
37
|
+
throw new Error('Language and code are required');
|
|
38
|
+
const result = await runner.run({
|
|
39
|
+
name: definition.name || definition.id,
|
|
40
|
+
language,
|
|
41
|
+
code,
|
|
42
|
+
arguments: input,
|
|
43
|
+
});
|
|
44
|
+
// TODO: validate the result against the definition.outputs
|
|
45
|
+
return options?.stream ? objectToRunnableResponseStream(result) : result;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
FunctionAgent = FunctionAgent_1 = __decorate([
|
|
49
|
+
injectable(),
|
|
50
|
+
__param(0, inject(TYPES.definition)),
|
|
51
|
+
__param(1, inject(TYPES.functionRunner)),
|
|
52
|
+
__metadata("design:paramtypes", [Object, FunctionRunner])
|
|
53
|
+
], FunctionAgent);
|
|
54
|
+
export { FunctionAgent };
|
|
55
|
+
export function createFunctionAgentDefinition(options) {
|
|
56
|
+
const inputs = OrderedRecord.fromArray(options.inputs?.map((i) => ({
|
|
57
|
+
id: nanoid(),
|
|
58
|
+
name: i.name,
|
|
59
|
+
type: i.type,
|
|
60
|
+
required: i.required,
|
|
61
|
+
})));
|
|
62
|
+
const outputs = OrderedRecord.fromArray(options.outputs?.map((i) => ({
|
|
63
|
+
id: nanoid(),
|
|
64
|
+
name: i.name,
|
|
65
|
+
type: i.type,
|
|
66
|
+
required: i.required,
|
|
67
|
+
})));
|
|
68
|
+
return {
|
|
69
|
+
id: options.id || options.name || nanoid(),
|
|
70
|
+
name: options.name,
|
|
71
|
+
type: 'function_agent',
|
|
72
|
+
inputs,
|
|
73
|
+
outputs,
|
|
74
|
+
language: options.language,
|
|
75
|
+
code: options.code,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Runnable } from './runnable';
|
|
2
|
+
import { OrderedRecord } from './utils';
|
|
3
|
+
export class FunctionRunner extends Runnable {
|
|
4
|
+
constructor() {
|
|
5
|
+
super({
|
|
6
|
+
id: 'function_runner',
|
|
7
|
+
type: 'function_runner',
|
|
8
|
+
name: 'Function Runner',
|
|
9
|
+
description: 'Run a function',
|
|
10
|
+
inputs: OrderedRecord.fromArray([
|
|
11
|
+
{ id: 'name', name: 'name', type: 'string', required: true },
|
|
12
|
+
{ id: 'language', name: 'language', type: 'string', required: true },
|
|
13
|
+
{ id: 'code', name: 'code', type: 'string', required: true },
|
|
14
|
+
{ id: 'arguments', name: 'arguments', type: 'object', required: false },
|
|
15
|
+
]),
|
|
16
|
+
outputs: OrderedRecord.fromArray([
|
|
17
|
+
{
|
|
18
|
+
id: 'result',
|
|
19
|
+
name: 'result',
|
|
20
|
+
type: 'object',
|
|
21
|
+
},
|
|
22
|
+
]),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
package/lib/esm/index.js
CHANGED
|
@@ -1 +1,13 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './utils';
|
|
2
|
+
export * from './constants';
|
|
3
|
+
export * from './data-type';
|
|
4
|
+
export * from './context';
|
|
5
|
+
export * from './runnable';
|
|
6
|
+
export * from './pipeline-agent';
|
|
7
|
+
export * from './llm-agent';
|
|
8
|
+
export * from './llm-model';
|
|
9
|
+
export * from './function-agent';
|
|
10
|
+
export * from './function-runner';
|
|
11
|
+
export * from './llm-decision-agent';
|
|
12
|
+
export * from './local-function-agent';
|
|
13
|
+
export * from './memory';
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var LLMAgent_1;
|
|
14
|
+
import { omitBy } from 'lodash';
|
|
15
|
+
import { nanoid } from 'nanoid';
|
|
16
|
+
import { inject, injectable } from 'tsyringe';
|
|
17
|
+
import { StreamTextOutputName, TYPES } from './constants';
|
|
18
|
+
import { LLMModel } from './llm-model';
|
|
19
|
+
import { Runnable, } from './runnable';
|
|
20
|
+
import { isNonNullable, isPropsNonNullable } from './utils';
|
|
21
|
+
import { renderMessage } from './utils/mustache-utils';
|
|
22
|
+
import { OrderedRecord } from './utils/ordered-map';
|
|
23
|
+
let LLMAgent = LLMAgent_1 = class LLMAgent extends Runnable {
|
|
24
|
+
definition;
|
|
25
|
+
model;
|
|
26
|
+
static create(options) {
|
|
27
|
+
const definition = createLLMAgentDefinition(options);
|
|
28
|
+
return new LLMAgent_1(definition);
|
|
29
|
+
}
|
|
30
|
+
constructor(definition, model) {
|
|
31
|
+
super(definition);
|
|
32
|
+
this.definition = definition;
|
|
33
|
+
this.model = model;
|
|
34
|
+
}
|
|
35
|
+
async run(input, options) {
|
|
36
|
+
const { definition, model } = this;
|
|
37
|
+
if (!model)
|
|
38
|
+
throw new Error('LLM model is required');
|
|
39
|
+
const messages = OrderedRecord.toArray(definition.messages);
|
|
40
|
+
if (!messages.length)
|
|
41
|
+
throw new Error('Messages are required');
|
|
42
|
+
// TODO: support comment/image for messages
|
|
43
|
+
const llmInputs = {
|
|
44
|
+
messages: messages.map(({ role, content }) => ({
|
|
45
|
+
role,
|
|
46
|
+
content: renderMessage(content, input),
|
|
47
|
+
})),
|
|
48
|
+
modelOptions: definition.modelOptions,
|
|
49
|
+
};
|
|
50
|
+
const outputs = OrderedRecord.toArray(definition.outputs).filter(isPropsNonNullable('name'));
|
|
51
|
+
const textOutput = outputs.find((i) => i.name === StreamTextOutputName);
|
|
52
|
+
const jsonOutputs = outputs.filter((i) => i.name !== StreamTextOutputName);
|
|
53
|
+
const outputJsonSchema = jsonOutputs.length ? outputsToJsonSchema(OrderedRecord.fromArray(jsonOutputs)) : undefined;
|
|
54
|
+
const jsonOutput = outputJsonSchema
|
|
55
|
+
? model
|
|
56
|
+
.run({
|
|
57
|
+
...llmInputs,
|
|
58
|
+
responseFormat: outputJsonSchema && {
|
|
59
|
+
type: 'json_schema',
|
|
60
|
+
jsonSchema: {
|
|
61
|
+
name: 'output',
|
|
62
|
+
schema: outputJsonSchema,
|
|
63
|
+
strict: true,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
.then(async (response) => {
|
|
68
|
+
if (!response.$text)
|
|
69
|
+
throw new Error('No text in JSON mode response');
|
|
70
|
+
const json = JSON.parse(response.$text);
|
|
71
|
+
// TODO: validate json with outputJsonSchema
|
|
72
|
+
return json;
|
|
73
|
+
})
|
|
74
|
+
: undefined;
|
|
75
|
+
if (options?.stream) {
|
|
76
|
+
return new ReadableStream({
|
|
77
|
+
start: async (controller) => {
|
|
78
|
+
try {
|
|
79
|
+
if (textOutput) {
|
|
80
|
+
const textStreamOutput = await model.run(llmInputs, { stream: true });
|
|
81
|
+
for await (const chunk of textStreamOutput) {
|
|
82
|
+
controller.enqueue({ $text: chunk.$text });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
controller.enqueue({ delta: await jsonOutput });
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
controller.error(error);
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
controller.close();
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const text = textOutput ? await model.run(llmInputs) : undefined;
|
|
97
|
+
return {
|
|
98
|
+
$text: text,
|
|
99
|
+
...(await jsonOutput),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
LLMAgent = LLMAgent_1 = __decorate([
|
|
104
|
+
injectable(),
|
|
105
|
+
__param(0, inject(TYPES.definition)),
|
|
106
|
+
__param(1, inject(TYPES.llmModel)),
|
|
107
|
+
__metadata("design:paramtypes", [Object, LLMModel])
|
|
108
|
+
], LLMAgent);
|
|
109
|
+
export { LLMAgent };
|
|
110
|
+
export function createLLMAgentDefinition(options) {
|
|
111
|
+
const inputs = OrderedRecord.fromArray(options.inputs?.map((i) => ({
|
|
112
|
+
id: nanoid(),
|
|
113
|
+
name: i.name,
|
|
114
|
+
type: i.type,
|
|
115
|
+
required: i.required,
|
|
116
|
+
})));
|
|
117
|
+
const outputs = OrderedRecord.fromArray(options.outputs?.map((i) => ({ ...i, id: nanoid() })));
|
|
118
|
+
const messages = OrderedRecord.fromArray(options.messages?.map((i) => ({
|
|
119
|
+
id: nanoid(),
|
|
120
|
+
role: i.role,
|
|
121
|
+
content: i.content,
|
|
122
|
+
})));
|
|
123
|
+
return {
|
|
124
|
+
id: options.id || options.name || nanoid(),
|
|
125
|
+
name: options.name,
|
|
126
|
+
type: 'llm_agent',
|
|
127
|
+
inputs,
|
|
128
|
+
outputs,
|
|
129
|
+
messages,
|
|
130
|
+
modelOptions: options.modelOptions,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function outputsToJsonSchema(outputs) {
|
|
134
|
+
const outputToSchema = (output) => {
|
|
135
|
+
const properties = output.type === 'object' && output.properties?.$indexes.length
|
|
136
|
+
? OrderedRecord.map(output.properties, (property) => {
|
|
137
|
+
if (!property.name)
|
|
138
|
+
return null;
|
|
139
|
+
const schema = outputToSchema(property);
|
|
140
|
+
if (!schema)
|
|
141
|
+
return null;
|
|
142
|
+
return { schema, property };
|
|
143
|
+
}).filter(isNonNullable)
|
|
144
|
+
: undefined;
|
|
145
|
+
return omitBy({
|
|
146
|
+
type: output.type,
|
|
147
|
+
description: output.description,
|
|
148
|
+
properties: properties?.length
|
|
149
|
+
? Object.fromEntries(properties.map((p) => [p.property.name, p.schema]))
|
|
150
|
+
: undefined,
|
|
151
|
+
items: output.type === 'array' && output.items ? outputToSchema(output.items) : undefined,
|
|
152
|
+
additionalProperties: output.type === 'object' ? false : undefined,
|
|
153
|
+
required: properties?.length
|
|
154
|
+
? properties.filter((i) => i.property.required).map((i) => i.property.name)
|
|
155
|
+
: undefined,
|
|
156
|
+
}, (v) => v === undefined);
|
|
157
|
+
};
|
|
158
|
+
return outputToSchema({
|
|
159
|
+
type: 'object',
|
|
160
|
+
properties: outputs,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var LLMDecisionAgent_1;
|
|
14
|
+
import { get, isNil } from 'lodash';
|
|
15
|
+
import { nanoid } from 'nanoid';
|
|
16
|
+
import { inject, injectable } from 'tsyringe';
|
|
17
|
+
import { TYPES } from './constants';
|
|
18
|
+
import { LLMModel } from './llm-model';
|
|
19
|
+
import { Runnable, } from './runnable';
|
|
20
|
+
import { OrderedRecord, isNonNullable, renderMessage } from './utils';
|
|
21
|
+
let LLMDecisionAgent = LLMDecisionAgent_1 = class LLMDecisionAgent extends Runnable {
|
|
22
|
+
definition;
|
|
23
|
+
model;
|
|
24
|
+
context;
|
|
25
|
+
static create(options) {
|
|
26
|
+
const definition = createLLMDecisionAgentDefinition(options);
|
|
27
|
+
return new LLMDecisionAgent_1(definition);
|
|
28
|
+
}
|
|
29
|
+
constructor(definition, model, context) {
|
|
30
|
+
super(definition);
|
|
31
|
+
this.definition = definition;
|
|
32
|
+
this.model = model;
|
|
33
|
+
this.context = context;
|
|
34
|
+
}
|
|
35
|
+
async run(input, options) {
|
|
36
|
+
const { definition, context, model } = this;
|
|
37
|
+
if (!model)
|
|
38
|
+
throw new Error('LLM model is required');
|
|
39
|
+
if (!context)
|
|
40
|
+
throw new Error('Context is required');
|
|
41
|
+
const messages = OrderedRecord.toArray(definition.messages);
|
|
42
|
+
if (!messages.length)
|
|
43
|
+
throw new Error('Messages are required');
|
|
44
|
+
const cases = await Promise.all(OrderedRecord.map(definition.cases, async (t) => {
|
|
45
|
+
if (!t.runnable?.id)
|
|
46
|
+
throw new Error('Runnable is required');
|
|
47
|
+
const runnable = await context.resolve(t.runnable.id);
|
|
48
|
+
// TODO: auto generate name by llm model if needed
|
|
49
|
+
const name = t.name || runnable.name;
|
|
50
|
+
if (!name)
|
|
51
|
+
throw new Error('Case name is required');
|
|
52
|
+
return { name, description: t.description, runnable, input: t.input };
|
|
53
|
+
}));
|
|
54
|
+
const llmInputs = {
|
|
55
|
+
messages: messages.map(({ role, content }) => ({
|
|
56
|
+
role,
|
|
57
|
+
content: typeof content === 'string' ? renderMessage(content, input) : content,
|
|
58
|
+
})),
|
|
59
|
+
modelOptions: definition.modelOptions,
|
|
60
|
+
tools: cases.map((t) => {
|
|
61
|
+
// TODO: auto generate parameters by llm model if needed
|
|
62
|
+
return {
|
|
63
|
+
type: 'function',
|
|
64
|
+
function: {
|
|
65
|
+
name: t.name,
|
|
66
|
+
description: t.description,
|
|
67
|
+
parameters: {},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}),
|
|
71
|
+
toolChoice: 'required',
|
|
72
|
+
};
|
|
73
|
+
const { toolCalls } = await model.run(llmInputs);
|
|
74
|
+
// TODO: support run multiple calls
|
|
75
|
+
const functionNameToCall = toolCalls?.[0].function?.name;
|
|
76
|
+
if (!functionNameToCall)
|
|
77
|
+
throw new Error('No any runnable called');
|
|
78
|
+
const caseToCall = cases.find((i) => i.name === functionNameToCall);
|
|
79
|
+
if (!caseToCall)
|
|
80
|
+
throw new Error('Case not found');
|
|
81
|
+
// NOTE: 将 input 转换为 variables,其中 key 为 inputId,value 为 input 的值
|
|
82
|
+
const variables = Object.fromEntries(OrderedRecord.map(this.definition.inputs, (i) => {
|
|
83
|
+
const value = input[i.name || i.id];
|
|
84
|
+
if (isNil(value))
|
|
85
|
+
return null;
|
|
86
|
+
return [i.id, value];
|
|
87
|
+
}).filter(isNonNullable));
|
|
88
|
+
const inputForCase = Object.fromEntries(Object.entries(caseToCall.input ?? {})
|
|
89
|
+
.map(([inputId, { from, fromVariableId, fromVariablePropPath }]) => {
|
|
90
|
+
const targetInput = OrderedRecord.find(caseToCall.runnable.definition.inputs, (i) => i.id === inputId);
|
|
91
|
+
if (!targetInput?.name)
|
|
92
|
+
return null;
|
|
93
|
+
if (from !== 'variable' || !fromVariableId)
|
|
94
|
+
return null;
|
|
95
|
+
const v = variables[fromVariableId];
|
|
96
|
+
const value = fromVariablePropPath?.length ? get(v, fromVariablePropPath) : v;
|
|
97
|
+
return [targetInput.name, value];
|
|
98
|
+
})
|
|
99
|
+
.filter(isNonNullable));
|
|
100
|
+
// TODO: check result structure and omit undefined values
|
|
101
|
+
return (await caseToCall.runnable.run(inputForCase, options));
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
LLMDecisionAgent = LLMDecisionAgent_1 = __decorate([
|
|
105
|
+
injectable(),
|
|
106
|
+
__param(0, inject(TYPES.definition)),
|
|
107
|
+
__param(1, inject(TYPES.llmModel)),
|
|
108
|
+
__param(2, inject(TYPES.context)),
|
|
109
|
+
__metadata("design:paramtypes", [Object, LLMModel, Object])
|
|
110
|
+
], LLMDecisionAgent);
|
|
111
|
+
export { LLMDecisionAgent };
|
|
112
|
+
export function createLLMDecisionAgentDefinition(options) {
|
|
113
|
+
const inputs = OrderedRecord.fromArray(options.inputs?.map((i) => ({
|
|
114
|
+
id: nanoid(),
|
|
115
|
+
name: i.name,
|
|
116
|
+
type: i.type,
|
|
117
|
+
required: i.required,
|
|
118
|
+
})));
|
|
119
|
+
const messages = OrderedRecord.fromArray([
|
|
120
|
+
{
|
|
121
|
+
id: nanoid(),
|
|
122
|
+
role: 'system',
|
|
123
|
+
content: options.messages,
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
const cases = OrderedRecord.fromArray(options.cases.map((c) => ({
|
|
127
|
+
id: nanoid(),
|
|
128
|
+
name: c.name || c.runnable.name,
|
|
129
|
+
description: c.description,
|
|
130
|
+
runnable: { id: c.runnable.id },
|
|
131
|
+
// TODO: pass input from decision to case runnable
|
|
132
|
+
input: Object.fromEntries(OrderedRecord.map(c.runnable.definition.inputs, (inputOfCase) => {
|
|
133
|
+
const i = c.input?.[inputOfCase.name || inputOfCase.id];
|
|
134
|
+
if (!i) {
|
|
135
|
+
if (inputOfCase.required) {
|
|
136
|
+
throw new Error(`Input ${inputOfCase.name || inputOfCase.id} for case ${c.runnable.name || c.runnable.id} is required`);
|
|
137
|
+
}
|
|
138
|
+
// ignore optional input
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
const inputFromDecision = OrderedRecord.find(inputs, (input) => input.name === i.fromVariable);
|
|
142
|
+
if (!inputFromDecision)
|
|
143
|
+
throw new Error(`Input ${i.fromVariable} not found`);
|
|
144
|
+
return [
|
|
145
|
+
inputOfCase.id,
|
|
146
|
+
{
|
|
147
|
+
from: 'variable',
|
|
148
|
+
fromVariableId: inputFromDecision.id,
|
|
149
|
+
fromVariablePropPath: i.fromVariablePropPath,
|
|
150
|
+
},
|
|
151
|
+
];
|
|
152
|
+
}).filter(isNonNullable)),
|
|
153
|
+
})));
|
|
154
|
+
return {
|
|
155
|
+
id: options.id || options.name || nanoid(),
|
|
156
|
+
name: options.name,
|
|
157
|
+
type: 'llm_decision_agent',
|
|
158
|
+
inputs,
|
|
159
|
+
// TODO: decision agent outputs should be the union of all case outputs
|
|
160
|
+
outputs: OrderedRecord.fromArray([]),
|
|
161
|
+
messages,
|
|
162
|
+
modelOptions: options.modelOptions,
|
|
163
|
+
cases,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Runnable } from './runnable';
|
|
2
|
+
import { OrderedRecord } from './utils';
|
|
3
|
+
export class LLMModel extends Runnable {
|
|
4
|
+
constructor() {
|
|
5
|
+
super({
|
|
6
|
+
id: 'llm_model',
|
|
7
|
+
type: 'llm_model',
|
|
8
|
+
name: 'LLM Model',
|
|
9
|
+
description: 'Run a LLM model',
|
|
10
|
+
inputs: OrderedRecord.fromArray([
|
|
11
|
+
{ id: 'messages', name: 'messages', type: 'array', required: true },
|
|
12
|
+
{ id: 'responseFormat', name: 'responseFormat', type: 'object' },
|
|
13
|
+
{ id: 'tools', name: 'tools', type: 'array' },
|
|
14
|
+
{ id: 'toolChoice', name: 'toolChoice', type: 'object' },
|
|
15
|
+
{ id: 'modelOptions', name: 'modelOptions', type: 'object' },
|
|
16
|
+
]),
|
|
17
|
+
outputs: OrderedRecord.fromArray([
|
|
18
|
+
{ id: '$text', name: '$text', type: 'string' },
|
|
19
|
+
{ id: 'toolCalls', name: 'toolCalls', type: 'object' },
|
|
20
|
+
]),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|