@aigne/agent-library 1.9.0 → 1.11.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/CHANGELOG.md +19 -2
- package/lib/cjs/data-mapper/agents/mapper.d.ts +14 -0
- package/lib/cjs/data-mapper/agents/mapper.js +63 -0
- package/lib/cjs/data-mapper/agents/reviewer.d.ts +19 -0
- package/lib/cjs/data-mapper/agents/reviewer.js +47 -0
- package/lib/cjs/data-mapper/index.d.ts +18 -0
- package/lib/cjs/data-mapper/index.js +29 -0
- package/lib/cjs/data-mapper/prompts.d.ts +1 -0
- package/lib/cjs/data-mapper/prompts.js +94 -0
- package/lib/cjs/data-mapper/tools.d.ts +17 -0
- package/lib/cjs/data-mapper/tools.js +177 -0
- package/lib/cjs/fs-memory/index.js +4 -4
- package/lib/cjs/orchestrator/index.d.ts +3 -3
- package/lib/cjs/orchestrator/index.js +6 -6
- package/lib/dts/data-mapper/agents/mapper.d.ts +14 -0
- package/lib/dts/data-mapper/agents/reviewer.d.ts +19 -0
- package/lib/dts/data-mapper/index.d.ts +18 -0
- package/lib/dts/data-mapper/prompts.d.ts +1 -0
- package/lib/dts/data-mapper/tools.d.ts +17 -0
- package/lib/dts/orchestrator/index.d.ts +3 -3
- package/lib/esm/data-mapper/agents/mapper.d.ts +14 -0
- package/lib/esm/data-mapper/agents/mapper.js +61 -0
- package/lib/esm/data-mapper/agents/reviewer.d.ts +19 -0
- package/lib/esm/data-mapper/agents/reviewer.js +45 -0
- package/lib/esm/data-mapper/index.d.ts +18 -0
- package/lib/esm/data-mapper/index.js +21 -0
- package/lib/esm/data-mapper/prompts.d.ts +1 -0
- package/lib/esm/data-mapper/prompts.js +91 -0
- package/lib/esm/data-mapper/tools.d.ts +17 -0
- package/lib/esm/data-mapper/tools.js +167 -0
- package/lib/esm/fs-memory/index.js +4 -4
- package/lib/esm/orchestrator/index.d.ts +3 -3
- package/lib/esm/orchestrator/index.js +6 -6
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
|
-
## [1.
|
|
1
|
+
## [1.11.0](https://github.com/AIGNE-io/aigne-framework/compare/agent-library-v1.10.0...agent-library-v1.11.0) (2025-05-27)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add schema transform ([#35](https://github.com/AIGNE-io/aigne-framework/issues/35)) ([c7d9a2c](https://github.com/AIGNE-io/aigne-framework/commit/c7d9a2c9fcab8d384d4198db5ff6ba4603846cdf))
|
|
7
|
+
|
|
8
|
+
## [1.10.0](https://github.com/AIGNE-io/aigne-framework/compare/agent-library-v1.9.0...agent-library-v1.10.0) (2025-05-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add user context support ([#131](https://github.com/AIGNE-io/aigne-framework/issues/131)) ([4dd9d20](https://github.com/AIGNE-io/aigne-framework/commit/4dd9d20953f6ac33933723db56efd9b44bafeb02))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Dependencies
|
|
17
|
+
|
|
18
|
+
* The following workspace dependencies were updated
|
|
19
|
+
* dependencies
|
|
20
|
+
* @aigne/core bumped to 1.17.0
|
|
4
21
|
|
|
5
22
|
## [1.9.0](https://github.com/AIGNE-io/aigne-framework/compare/agent-library-v1.8.1...agent-library-v1.9.0) (2025-05-23)
|
|
6
23
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AIAgent } from "@aigne/core";
|
|
2
|
+
declare const mapper: AIAgent<{
|
|
3
|
+
sourceData: string;
|
|
4
|
+
responseSchema: string;
|
|
5
|
+
sourceSchema?: string | undefined;
|
|
6
|
+
instruction?: string | undefined;
|
|
7
|
+
responseData?: string | undefined;
|
|
8
|
+
feedback?: string | undefined;
|
|
9
|
+
}, {
|
|
10
|
+
jsonata: string;
|
|
11
|
+
confidence: number;
|
|
12
|
+
confidenceReasoning: string;
|
|
13
|
+
}>;
|
|
14
|
+
export default mapper;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@aigne/core");
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const prompts_js_1 = require("../prompts.js");
|
|
6
|
+
const mapper = core_1.AIAgent.from({
|
|
7
|
+
subscribeTopic: [core_1.UserInputTopic, "mapping_request"],
|
|
8
|
+
publishTopic: "review_request",
|
|
9
|
+
inputSchema: zod_1.z.object({
|
|
10
|
+
sourceData: zod_1.z.string(),
|
|
11
|
+
sourceSchema: zod_1.z.string().optional(),
|
|
12
|
+
responseSchema: zod_1.z.string(),
|
|
13
|
+
instruction: zod_1.z.string().optional(),
|
|
14
|
+
responseData: zod_1.z.string().optional(),
|
|
15
|
+
feedback: zod_1.z.string().optional(),
|
|
16
|
+
}),
|
|
17
|
+
outputSchema: zod_1.z.object({
|
|
18
|
+
jsonata: zod_1.z.string().describe("JSONata expression"),
|
|
19
|
+
confidence: zod_1.z.number().describe(`Confidence score for the JSONata expression between 0 and 100.
|
|
20
|
+
Give a low confidence score if there are missing fields in the source data.
|
|
21
|
+
Give a low confidence score if there are multiple options for a field and it is unclear which one to choose.`),
|
|
22
|
+
confidenceReasoning: zod_1.z.string().describe("Reasoning for the confidence score"),
|
|
23
|
+
}),
|
|
24
|
+
includeInputInOutput: true,
|
|
25
|
+
instructions: core_1.PromptBuilder.from({
|
|
26
|
+
messages: [
|
|
27
|
+
{
|
|
28
|
+
role: "assistant",
|
|
29
|
+
content: {
|
|
30
|
+
type: "text",
|
|
31
|
+
text: prompts_js_1.PROMPT_MAPPING,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
role: "user",
|
|
36
|
+
content: {
|
|
37
|
+
type: "text",
|
|
38
|
+
text: `Given a source data and structure, create a jsonata expression in JSON FORMAT.
|
|
39
|
+
Important: The output should be a jsonata expression creating an object that matches the following schema:
|
|
40
|
+
{{responseSchema}}
|
|
41
|
+
|
|
42
|
+
Pay special attention to the requirements in field descriptions
|
|
43
|
+
|
|
44
|
+
The instruction from the user is: {{instruction}}
|
|
45
|
+
|
|
46
|
+
------
|
|
47
|
+
|
|
48
|
+
Source Data Structure:
|
|
49
|
+
{{sourceSchema}}
|
|
50
|
+
|
|
51
|
+
Source data Sample:
|
|
52
|
+
{{sourceData}}
|
|
53
|
+
|
|
54
|
+
------
|
|
55
|
+
|
|
56
|
+
Previous feedback:
|
|
57
|
+
{{feedback}}`,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
exports.default = mapper;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FunctionAgent } from "@aigne/core";
|
|
2
|
+
declare const reviewer: FunctionAgent<{
|
|
3
|
+
sourceData: string;
|
|
4
|
+
responseSchema: string;
|
|
5
|
+
jsonata: string;
|
|
6
|
+
}, {
|
|
7
|
+
success: boolean;
|
|
8
|
+
data: null;
|
|
9
|
+
feedback: string;
|
|
10
|
+
} | {
|
|
11
|
+
success: boolean;
|
|
12
|
+
data: unknown;
|
|
13
|
+
feedback?: undefined;
|
|
14
|
+
} | {
|
|
15
|
+
success: false;
|
|
16
|
+
data: unknown;
|
|
17
|
+
feedback: string | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export default reviewer;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@aigne/core");
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tools_js_1 = require("../tools.js");
|
|
6
|
+
const reviewer = core_1.FunctionAgent.from({
|
|
7
|
+
name: "check_mapping",
|
|
8
|
+
description: "Check the mapping result",
|
|
9
|
+
subscribeTopic: ["review_request"],
|
|
10
|
+
publishTopic: (output) => (output.success ? core_1.UserOutputTopic : "mapping_request"),
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
sourceData: zod_1.z.string(),
|
|
13
|
+
jsonata: zod_1.z.string(),
|
|
14
|
+
responseSchema: zod_1.z.string(),
|
|
15
|
+
}),
|
|
16
|
+
process: async ({ sourceData, jsonata, responseSchema, }) => {
|
|
17
|
+
let parsedSourceData = null;
|
|
18
|
+
let parsedResponseSchema = null;
|
|
19
|
+
try {
|
|
20
|
+
parsedSourceData = sourceData ? JSON.parse(sourceData) : null;
|
|
21
|
+
parsedResponseSchema = responseSchema ? JSON.parse(responseSchema) : null;
|
|
22
|
+
}
|
|
23
|
+
catch (parseError) {
|
|
24
|
+
// input data is not valid JSON, return success
|
|
25
|
+
return {
|
|
26
|
+
success: true,
|
|
27
|
+
data: null,
|
|
28
|
+
feedback: `JSON parsing failed: ${parseError.message}`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const transformation = await (0, tools_js_1.applyJsonataWithValidation)(parsedSourceData, jsonata, parsedResponseSchema);
|
|
32
|
+
// if transformation is successful, return success
|
|
33
|
+
if (transformation.success) {
|
|
34
|
+
return {
|
|
35
|
+
success: true,
|
|
36
|
+
data: transformation.data,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
success: transformation.success,
|
|
41
|
+
data: transformation.data,
|
|
42
|
+
feedback: transformation.error,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
includeInputInOutput: true,
|
|
46
|
+
});
|
|
47
|
+
exports.default = reviewer;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ChatModel } from "@aigne/core";
|
|
2
|
+
export interface TransformInput {
|
|
3
|
+
responseSchema: string;
|
|
4
|
+
responseSampleData?: string;
|
|
5
|
+
sourceData?: string;
|
|
6
|
+
sourceSchema?: string;
|
|
7
|
+
instruction?: string;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
export declare function generateMapping({ input, model, }: {
|
|
11
|
+
input: TransformInput;
|
|
12
|
+
model: ChatModel;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
jsonata: string;
|
|
15
|
+
confidence: number;
|
|
16
|
+
confidenceReasoning: string;
|
|
17
|
+
} | null>;
|
|
18
|
+
export { applyJsonata } from "./tools.js";
|
|
@@ -0,0 +1,29 @@
|
|
|
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.applyJsonata = void 0;
|
|
7
|
+
exports.generateMapping = generateMapping;
|
|
8
|
+
const core_1 = require("@aigne/core");
|
|
9
|
+
const mapper_js_1 = __importDefault(require("./agents/mapper.js"));
|
|
10
|
+
const reviewer_js_1 = __importDefault(require("./agents/reviewer.js"));
|
|
11
|
+
const tools_js_1 = require("./tools.js");
|
|
12
|
+
async function generateMapping({ input, model, }) {
|
|
13
|
+
if (!model)
|
|
14
|
+
throw new Error("model is required to run data mapper");
|
|
15
|
+
// if sourceSchema is not provided, generate it from sourceData
|
|
16
|
+
if (!input.sourceSchema && input.sourceData) {
|
|
17
|
+
input.sourceSchema = JSON.stringify((0, tools_js_1.getSchemaFromData)(JSON.parse(input.sourceData)));
|
|
18
|
+
}
|
|
19
|
+
const aigne = new core_1.AIGNE({ model, agents: [mapper_js_1.default, reviewer_js_1.default] });
|
|
20
|
+
aigne.publish(core_1.UserInputTopic, input);
|
|
21
|
+
const { message } = await aigne.subscribe(core_1.UserOutputTopic);
|
|
22
|
+
return {
|
|
23
|
+
jsonata: message.jsonata || "",
|
|
24
|
+
confidence: message.confidence || 0,
|
|
25
|
+
confidenceReasoning: message.confidenceReasoning || "",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
var tools_js_2 = require("./tools.js");
|
|
29
|
+
Object.defineProperty(exports, "applyJsonata", { enumerable: true, get: function () { return tools_js_2.applyJsonata; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PROMPT_MAPPING = "You are an AI that generates JSONata mapping expressions to transform source data structures into target structures.\n\nGuidelines for creating JSONata mappings:\n\n1. Source References:\n - Use exact field paths from the source data, e.g. $.merchant_category\n - For accessing fields with names containing spaces, use backticks, e.g. $.`merchant category`\n - Jsonata will automatically extract all the fields from the current context. E.g. if you need all variants from all products, you can use $.products.variants. No need to do nested map reduce operations.\n - $. The variable with no name refers to the context value at any point in the input JSON hierarchy. E.g. if the current context is products.price, then $.currency is products.price.currency\n - %. The parent of the current context value. E.g. if the current context is products.variants.size and you want variant name, use %.name\n\n - When multiple source fields could map to a target, use a maximum of 3 fallbacks:\n GOOD: source1 ? source1 : source2 ? source2 : source3 ? source3 : 'default'\n BAD: source1 ? source1 : source1 ? source1 : source1 (repeated fields)\n\n2. Expression Rules:\n - Avoid unnecessary array/string operations\n - Each mapping should be clear and concise\n - Use proper JSONata syntax for coalesce operations\n - Do not use ~> to execute functions. Use the functions directly with the correct arguments or use $map(arr, $function) to apply a function to each element of an array.\n\n3. Array Handling:\n - For mapping to an array of objects, use the following patterns:\n a) When in array scope, use $.{} to map each object:\n Correct: [$.{\"id\": id, \"name\": name}]\n Incorrect: [{\"id\": $.id}]\n b) When outside array scope, include the source path:\n Correct: [$.items.{\"id\": id, \"name\": name}]\n Incorrect: [{\"id\": $.items.id}]\n c) For nested arrays, chain the array operators:\n Correct: [products.variants.{\"size\": size, \"color\": color}]\n Incorrect: [products.[{\"size\": variants.size}]]\n d) You need to use the square brackets [] to map to an array of objects, otherwise it might return an object and fail the validation.\n Correct: variants: [variants.{\"size\": size, \"color\": color}]\n Incorrect: variants: variants.{\"size\": variants.size}\n - For array elements, use JSONata array operators like [0] for first element, [-1] for last element\n - Square bracket notation [] can be used with predicates, e.g. items[type='book']\n\n4. Field Selection Priority:\n - Prefer variant-specific fields over general fields (e.g., sizeVariants.description over sizes)\n - Choose the most specific/detailed field available (e.g., type=\"shelf\" over category=\"furniture\")\n\n5. Filters:\n - Pay special attention to filter statements in the instruction and the schema description. Add them to the generated jsonata expression.\n Example: Get me all products with SKU 0406654608 or products: {\"type\": \"array\", description: \"only products with SKU 0406654608\"}\n Generated jsonata expression: Account.Order.Product[SKU = \"0406654608\"].{\"Description\": Description}\n - For filtering with arrays, you can use the \"in\" operator. E.g. library.books[\"Steven King\" in authors]\n\n6. Data Integrity:\n - ONLY use fields that exist in the source data structure\n - If no matching source field exists, leave the field undefined\n - Never invent or assume the existence of fields not present in the source data\n\n7. Function Calls:\n - You may use the following functions if prompted:\n $string(arg) - Converts argument to string\n $length(str) - Returns string length\n $substring(str, start[, length]) - Extracts substring\n $substringBefore(str, chars) - Gets substring before specified chars\n $substringAfter(str, chars) - Gets substring after specified chars\n $uppercase(str) - Converts to uppercase\n $lowercase(str) - Converts to lowercase\n $trim(str) - Removes whitespace from both ends\n $pad(str, width[, char]) - Pads string to specified width\n $contains(str, substring) - Tests if string contains substring\n $toMillis(timestamp [, picture]) - Converts ISO 8601 timestamp to milliseconds. E.g. $toMillis(\"2017-11-07T15:07:54.972Z\") => 1510067274972\n $toDate(str | number) - Converts any timestamp string to valid ISO 8601 date string. E.g. $toDate(\"Oct 15, 2024 12:00:00 AM UTC\") => \"2024-10-15T00:00:00.000Z\", $toDate(1728873600000) => \"2024-10-15T00:00:00.000Z\"\n $dateMax(arr) - Returns the maximum date of an array of dates. E.g. $dateMax([\"2017-11-07T15:07:54.972Z\", \"Oct 15, 2012 12:00:00 AM UTC\"]) returns \"2017-11-07T15:07:54.972Z\".\n $dateMin(arr) - Returns the minimum date of an array of dates. E.g. $dateMin($.variants.created_at) returns the minimum created_at date of all variants.\n $dateDiff(date1, date2, unit: \"seconds\" | \"minutes\" | \"hours\" | \"days\") - Returns the difference between two dates in the specified unit. E.g. $dateDiff($.order.created_at, $.order.updated_at, \"days\") returns the number of days between the order created_at and updated_at.\n $now([picture [, timezone]]) - Returns current date and time in ISO 8601 format. E.g. $now() => \"2017-05-15T15:12:59.152Z\"\n $split(str[, separator][, limit]) - Splits string into array\n $join(array[, separator]) - Joins array elements into string\n $match(str, pattern[, limit]) - Returns array of regex matches\n $replace(str, pattern, replacement) - Replaces all occurrences of pattern. E.g. $replace(\"abracadabra\", /a.*?a/, \"*\") returns \"ab*ad*bra\". $replace(\"John Smith\", \"John\", \"Marc\") returns Marc Smith.\n $number(arg) - Converts an argument to a number.\n $min(arr) - Returns minimum number of a number array. E.g. $min($map($.variants.price, $number)) returns the minimum price of all variants.\n $max(arr) - Returns maximum number of a number array. E.g. $max($map($.variants.price, $number)) returns the maximum price of all variants.\n $count(array) - Returns array length\n $sort(array[, function]) - Sorts array\n $distinct(array) - Removes duplicates\n $map(array, function) - Applies function to each element\n $filter(array, function) - Filters array based on predicate\n\n- Error handling:\n - If you get an error like \"is not of a type(s) string/number/object\", try to convert the source field, but also consider that the original field or one of its parent might be null. In this case, add a default value.\n - If the error is something like \"instance is not of a type(s) object\", make sure you REALLY create the target schema with the correct type.\n - If the error is something like \"instance is not of a type(s) array or array/null\". In this case, wrap the source selector in an array to ensure it always returns an array. E.g. \"result\": [$.items]\n - if an object is optional but its fields required, you can add a test and default to {}, but do not set the inner fields to default null.\n\nRemember: The goal is to create valid JSONata expressions that accurately transform the source data structure into the required target structure.";
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROMPT_MAPPING = void 0;
|
|
4
|
+
exports.PROMPT_MAPPING = `You are an AI that generates JSONata mapping expressions to transform source data structures into target structures.
|
|
5
|
+
|
|
6
|
+
Guidelines for creating JSONata mappings:
|
|
7
|
+
|
|
8
|
+
1. Source References:
|
|
9
|
+
- Use exact field paths from the source data, e.g. $.merchant_category
|
|
10
|
+
- For accessing fields with names containing spaces, use backticks, e.g. $.\`merchant category\`
|
|
11
|
+
- Jsonata will automatically extract all the fields from the current context. E.g. if you need all variants from all products, you can use $.products.variants. No need to do nested map reduce operations.
|
|
12
|
+
- $. The variable with no name refers to the context value at any point in the input JSON hierarchy. E.g. if the current context is products.price, then $.currency is products.price.currency
|
|
13
|
+
- %. The parent of the current context value. E.g. if the current context is products.variants.size and you want variant name, use %.name
|
|
14
|
+
|
|
15
|
+
- When multiple source fields could map to a target, use a maximum of 3 fallbacks:
|
|
16
|
+
GOOD: source1 ? source1 : source2 ? source2 : source3 ? source3 : 'default'
|
|
17
|
+
BAD: source1 ? source1 : source1 ? source1 : source1 (repeated fields)
|
|
18
|
+
|
|
19
|
+
2. Expression Rules:
|
|
20
|
+
- Avoid unnecessary array/string operations
|
|
21
|
+
- Each mapping should be clear and concise
|
|
22
|
+
- Use proper JSONata syntax for coalesce operations
|
|
23
|
+
- Do not use ~> to execute functions. Use the functions directly with the correct arguments or use $map(arr, $function) to apply a function to each element of an array.
|
|
24
|
+
|
|
25
|
+
3. Array Handling:
|
|
26
|
+
- For mapping to an array of objects, use the following patterns:
|
|
27
|
+
a) When in array scope, use $.{} to map each object:
|
|
28
|
+
Correct: [$.{"id": id, "name": name}]
|
|
29
|
+
Incorrect: [{"id": $.id}]
|
|
30
|
+
b) When outside array scope, include the source path:
|
|
31
|
+
Correct: [$.items.{"id": id, "name": name}]
|
|
32
|
+
Incorrect: [{"id": $.items.id}]
|
|
33
|
+
c) For nested arrays, chain the array operators:
|
|
34
|
+
Correct: [products.variants.{"size": size, "color": color}]
|
|
35
|
+
Incorrect: [products.[{"size": variants.size}]]
|
|
36
|
+
d) You need to use the square brackets [] to map to an array of objects, otherwise it might return an object and fail the validation.
|
|
37
|
+
Correct: variants: [variants.{"size": size, "color": color}]
|
|
38
|
+
Incorrect: variants: variants.{"size": variants.size}
|
|
39
|
+
- For array elements, use JSONata array operators like [0] for first element, [-1] for last element
|
|
40
|
+
- Square bracket notation [] can be used with predicates, e.g. items[type='book']
|
|
41
|
+
|
|
42
|
+
4. Field Selection Priority:
|
|
43
|
+
- Prefer variant-specific fields over general fields (e.g., sizeVariants.description over sizes)
|
|
44
|
+
- Choose the most specific/detailed field available (e.g., type="shelf" over category="furniture")
|
|
45
|
+
|
|
46
|
+
5. Filters:
|
|
47
|
+
- Pay special attention to filter statements in the instruction and the schema description. Add them to the generated jsonata expression.
|
|
48
|
+
Example: Get me all products with SKU 0406654608 or products: {"type": "array", description: "only products with SKU 0406654608"}
|
|
49
|
+
Generated jsonata expression: Account.Order.Product[SKU = "0406654608"].{"Description": Description}
|
|
50
|
+
- For filtering with arrays, you can use the "in" operator. E.g. library.books["Steven King" in authors]
|
|
51
|
+
|
|
52
|
+
6. Data Integrity:
|
|
53
|
+
- ONLY use fields that exist in the source data structure
|
|
54
|
+
- If no matching source field exists, leave the field undefined
|
|
55
|
+
- Never invent or assume the existence of fields not present in the source data
|
|
56
|
+
|
|
57
|
+
7. Function Calls:
|
|
58
|
+
- You may use the following functions if prompted:
|
|
59
|
+
$string(arg) - Converts argument to string
|
|
60
|
+
$length(str) - Returns string length
|
|
61
|
+
$substring(str, start[, length]) - Extracts substring
|
|
62
|
+
$substringBefore(str, chars) - Gets substring before specified chars
|
|
63
|
+
$substringAfter(str, chars) - Gets substring after specified chars
|
|
64
|
+
$uppercase(str) - Converts to uppercase
|
|
65
|
+
$lowercase(str) - Converts to lowercase
|
|
66
|
+
$trim(str) - Removes whitespace from both ends
|
|
67
|
+
$pad(str, width[, char]) - Pads string to specified width
|
|
68
|
+
$contains(str, substring) - Tests if string contains substring
|
|
69
|
+
$toMillis(timestamp [, picture]) - Converts ISO 8601 timestamp to milliseconds. E.g. $toMillis("2017-11-07T15:07:54.972Z") => 1510067274972
|
|
70
|
+
$toDate(str | number) - Converts any timestamp string to valid ISO 8601 date string. E.g. $toDate("Oct 15, 2024 12:00:00 AM UTC") => "2024-10-15T00:00:00.000Z", $toDate(1728873600000) => "2024-10-15T00:00:00.000Z"
|
|
71
|
+
$dateMax(arr) - Returns the maximum date of an array of dates. E.g. $dateMax(["2017-11-07T15:07:54.972Z", "Oct 15, 2012 12:00:00 AM UTC"]) returns "2017-11-07T15:07:54.972Z".
|
|
72
|
+
$dateMin(arr) - Returns the minimum date of an array of dates. E.g. $dateMin($.variants.created_at) returns the minimum created_at date of all variants.
|
|
73
|
+
$dateDiff(date1, date2, unit: "seconds" | "minutes" | "hours" | "days") - Returns the difference between two dates in the specified unit. E.g. $dateDiff($.order.created_at, $.order.updated_at, "days") returns the number of days between the order created_at and updated_at.
|
|
74
|
+
$now([picture [, timezone]]) - Returns current date and time in ISO 8601 format. E.g. $now() => "2017-05-15T15:12:59.152Z"
|
|
75
|
+
$split(str[, separator][, limit]) - Splits string into array
|
|
76
|
+
$join(array[, separator]) - Joins array elements into string
|
|
77
|
+
$match(str, pattern[, limit]) - Returns array of regex matches
|
|
78
|
+
$replace(str, pattern, replacement) - Replaces all occurrences of pattern. E.g. $replace("abracadabra", /a.*?a/, "*") returns "ab*ad*bra". $replace("John Smith", "John", "Marc") returns Marc Smith.
|
|
79
|
+
$number(arg) - Converts an argument to a number.
|
|
80
|
+
$min(arr) - Returns minimum number of a number array. E.g. $min($map($.variants.price, $number)) returns the minimum price of all variants.
|
|
81
|
+
$max(arr) - Returns maximum number of a number array. E.g. $max($map($.variants.price, $number)) returns the maximum price of all variants.
|
|
82
|
+
$count(array) - Returns array length
|
|
83
|
+
$sort(array[, function]) - Sorts array
|
|
84
|
+
$distinct(array) - Removes duplicates
|
|
85
|
+
$map(array, function) - Applies function to each element
|
|
86
|
+
$filter(array, function) - Filters array based on predicate
|
|
87
|
+
|
|
88
|
+
- Error handling:
|
|
89
|
+
- If you get an error like "is not of a type(s) string/number/object", try to convert the source field, but also consider that the original field or one of its parent might be null. In this case, add a default value.
|
|
90
|
+
- If the error is something like "instance is not of a type(s) object", make sure you REALLY create the target schema with the correct type.
|
|
91
|
+
- If the error is something like "instance is not of a type(s) array or array/null". In this case, wrap the source selector in an array to ensure it always returns an array. E.g. "result": [$.items]
|
|
92
|
+
- if an object is optional but its fields required, you can add a test and default to {}, but do not set the inner fields to default null.
|
|
93
|
+
|
|
94
|
+
Remember: The goal is to create valid JSONata expressions that accurately transform the source data structure into the required target structure.`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import jsonata from "jsonata";
|
|
2
|
+
import type { Schema } from "jsonschema";
|
|
3
|
+
export interface TransformResult {
|
|
4
|
+
success: boolean;
|
|
5
|
+
data?: unknown;
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Extract JSON Schema from data
|
|
10
|
+
* @param data Any data
|
|
11
|
+
* @returns JSON Schema or null
|
|
12
|
+
*/
|
|
13
|
+
export declare function getSchemaFromData(data: unknown): unknown;
|
|
14
|
+
export declare function applyJsonataWithValidation(data: unknown, expr: string, schema: unknown): Promise<TransformResult>;
|
|
15
|
+
export declare function applyJsonata(data: unknown, expr: string): Promise<unknown>;
|
|
16
|
+
export declare function extendJsonata(expr: string): jsonata.Expression;
|
|
17
|
+
export declare function addNullableToOptional(schema: Schema): Schema;
|
|
@@ -0,0 +1,177 @@
|
|
|
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.getSchemaFromData = getSchemaFromData;
|
|
7
|
+
exports.applyJsonataWithValidation = applyJsonataWithValidation;
|
|
8
|
+
exports.applyJsonata = applyJsonata;
|
|
9
|
+
exports.extendJsonata = extendJsonata;
|
|
10
|
+
exports.addNullableToOptional = addNullableToOptional;
|
|
11
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
12
|
+
const jsonata_1 = __importDefault(require("jsonata"));
|
|
13
|
+
const jsonschema_1 = require("jsonschema");
|
|
14
|
+
const to_json_schema_1 = __importDefault(require("to-json-schema"));
|
|
15
|
+
/**
|
|
16
|
+
* Extract JSON Schema from data
|
|
17
|
+
* @param data Any data
|
|
18
|
+
* @returns JSON Schema or null
|
|
19
|
+
*/
|
|
20
|
+
function getSchemaFromData(data) {
|
|
21
|
+
if (!data)
|
|
22
|
+
return null;
|
|
23
|
+
return (0, to_json_schema_1.default)(data);
|
|
24
|
+
}
|
|
25
|
+
async function applyJsonataWithValidation(data, expr, schema) {
|
|
26
|
+
try {
|
|
27
|
+
const result = await applyJsonata(data, expr);
|
|
28
|
+
if (result === null ||
|
|
29
|
+
result === undefined ||
|
|
30
|
+
(Array.isArray(result) && result.length === 0) ||
|
|
31
|
+
(typeof result === "object" && Object.keys(result).length === 0)) {
|
|
32
|
+
return { success: false, error: "Result is empty" };
|
|
33
|
+
}
|
|
34
|
+
const validator = new jsonschema_1.Validator();
|
|
35
|
+
const optionalSchema = addNullableToOptional(schema);
|
|
36
|
+
const validation = validator.validate(result, optionalSchema);
|
|
37
|
+
if (!validation.valid) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: validation.errors
|
|
41
|
+
.map((e) => `${e.stack}. Source: ${e.instance ? JSON.stringify(e.instance) : "undefined"}`)
|
|
42
|
+
.join("\n")
|
|
43
|
+
.slice(0, 5000),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { success: true, data: result };
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return { success: false, error: `Validation failed: ${error.message}` };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function applyJsonata(data, expr) {
|
|
53
|
+
try {
|
|
54
|
+
const expression = extendJsonata(expr);
|
|
55
|
+
const result = await expression.evaluate(data);
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw new Error(`JSONata evaluation failed for expression "${expr}": ${error.message}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function extendJsonata(expr) {
|
|
63
|
+
const expression = (0, jsonata_1.default)(expr);
|
|
64
|
+
expression.registerFunction("max", (arr) => {
|
|
65
|
+
if (Array.isArray(arr)) {
|
|
66
|
+
return Math.max(...arr.map(Number).filter((n) => !Number.isNaN(n)));
|
|
67
|
+
}
|
|
68
|
+
return arr;
|
|
69
|
+
});
|
|
70
|
+
expression.registerFunction("min", (arr) => {
|
|
71
|
+
if (Array.isArray(arr)) {
|
|
72
|
+
return Math.min(...arr.map(Number).filter((n) => !Number.isNaN(n)));
|
|
73
|
+
}
|
|
74
|
+
return arr;
|
|
75
|
+
});
|
|
76
|
+
expression.registerFunction("number", (value) => Number.parseFloat(value));
|
|
77
|
+
expression.registerFunction("substring", (str, start, end) => String(str).substring(start, end));
|
|
78
|
+
expression.registerFunction("replace", (obj, pattern, replacement) => {
|
|
79
|
+
if (Array.isArray(obj)) {
|
|
80
|
+
return obj.map((item) => String(item).replace(pattern, replacement));
|
|
81
|
+
}
|
|
82
|
+
if (typeof obj === "object") {
|
|
83
|
+
return Object.fromEntries(Object.entries(obj || {}).map(([key, value]) => [
|
|
84
|
+
key,
|
|
85
|
+
String(value).replace(pattern, replacement),
|
|
86
|
+
]));
|
|
87
|
+
}
|
|
88
|
+
return String(obj).replace(pattern, replacement);
|
|
89
|
+
});
|
|
90
|
+
expression.registerFunction("toDate", (date) => {
|
|
91
|
+
try {
|
|
92
|
+
// Handle numeric timestamps (milliseconds or seconds)
|
|
93
|
+
if (typeof date === "number" || /^\d+$/.test(date)) {
|
|
94
|
+
const timestamp = typeof date === "number" ? date : Number.parseInt(date, 10);
|
|
95
|
+
// If timestamp is in seconds (typically 10 digits), convert to milliseconds
|
|
96
|
+
const millisTimestamp = timestamp < 10000000000 ? timestamp * 1000 : timestamp;
|
|
97
|
+
return new Date(millisTimestamp).toISOString();
|
|
98
|
+
}
|
|
99
|
+
// Handle date strings in MM/DD/YYYY format
|
|
100
|
+
const match = String(date).match(/^(\d{2})\/(\d{2})\/(\d{4})(?:\s+(\d{2}):(\d{2}):(\d{2}))?$/);
|
|
101
|
+
if (match) {
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
103
|
+
const [_, month, day, year, hours = "00", minutes = "00", seconds = "00"] = match;
|
|
104
|
+
const isoDate = `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.000Z`;
|
|
105
|
+
return new Date(isoDate).toISOString();
|
|
106
|
+
}
|
|
107
|
+
// Default case: try standard Date parsing
|
|
108
|
+
return new Date(date).toISOString();
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
throw new Error(`Invalid date: ${e.message}`);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
expression.registerFunction("dateMax", (dates) => dates.reduce((max, curr) => (new Date(max) > new Date(curr) ? max : curr)));
|
|
115
|
+
expression.registerFunction("dateMin", (dates) => dates.reduce((min, curr) => (new Date(min) < new Date(curr) ? min : curr)));
|
|
116
|
+
expression.registerFunction("dateDiff", (date1, date2, unit = "days") => {
|
|
117
|
+
const d1 = new Date(date1);
|
|
118
|
+
const d2 = new Date(date2);
|
|
119
|
+
const diff = Math.abs(d1.getTime() - d2.getTime());
|
|
120
|
+
switch (unit.toLowerCase()) {
|
|
121
|
+
case "seconds":
|
|
122
|
+
return Math.floor(diff / 1000);
|
|
123
|
+
case "minutes":
|
|
124
|
+
return Math.floor(diff / (1000 * 60));
|
|
125
|
+
case "hours":
|
|
126
|
+
return Math.floor(diff / (1000 * 60 * 60));
|
|
127
|
+
case "days":
|
|
128
|
+
return Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
129
|
+
default:
|
|
130
|
+
return diff; // milliseconds
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
return expression;
|
|
134
|
+
}
|
|
135
|
+
function addNullableToOptional(schema) {
|
|
136
|
+
if (!schema || typeof schema !== "object")
|
|
137
|
+
return schema;
|
|
138
|
+
const newSchema = { ...schema };
|
|
139
|
+
if (schema.type === "object" && schema.properties) {
|
|
140
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
141
|
+
newSchema.properties = Object.entries(schema.properties).reduce((acc, [key, value]) => ({
|
|
142
|
+
// biome-ignore lint/performance/noAccumulatingSpread: <explanation>
|
|
143
|
+
...acc,
|
|
144
|
+
[key]: !required.has(key) ? makeNullable(value) : addNullableToOptional(value),
|
|
145
|
+
}), {});
|
|
146
|
+
}
|
|
147
|
+
if (schema.type === "array" && schema.items) {
|
|
148
|
+
newSchema.items = addNullableToOptional(schema.items);
|
|
149
|
+
}
|
|
150
|
+
return newSchema;
|
|
151
|
+
}
|
|
152
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
153
|
+
function makeNullable(schema) {
|
|
154
|
+
if (!schema || typeof schema !== "object")
|
|
155
|
+
return schema;
|
|
156
|
+
const newSchema = { ...schema };
|
|
157
|
+
if (Array.isArray(schema.type)) {
|
|
158
|
+
if (!schema.type.includes("null")) {
|
|
159
|
+
newSchema.type = [...schema.type, "null"];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (schema.type) {
|
|
163
|
+
newSchema.type = [schema.type, "null"];
|
|
164
|
+
}
|
|
165
|
+
// Recursively process nested properties
|
|
166
|
+
if (schema.properties) {
|
|
167
|
+
newSchema.properties = Object.entries(schema.properties).reduce((acc, [key, value]) => ({
|
|
168
|
+
// biome-ignore lint/performance/noAccumulatingSpread: <explanation>
|
|
169
|
+
...acc,
|
|
170
|
+
[key]: makeNullable(value),
|
|
171
|
+
}), {});
|
|
172
|
+
}
|
|
173
|
+
if (schema.items) {
|
|
174
|
+
newSchema.items = makeNullable(schema.items);
|
|
175
|
+
}
|
|
176
|
+
return newSchema;
|
|
177
|
+
}
|
|
@@ -64,11 +64,11 @@ class FSMemoryRetriever extends index_js_1.MemoryRetriever {
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
agent;
|
|
67
|
-
async process(input,
|
|
67
|
+
async process(input, options) {
|
|
68
68
|
if (!(await (0, fs_js_1.exists)(this.options.memoryFileName)))
|
|
69
69
|
return { memories: [] };
|
|
70
70
|
const allMemory = await (0, promises_1.readFile)(this.options.memoryFileName, "utf-8");
|
|
71
|
-
const { memories } = await context.invoke(this.agent, { ...input, allMemory });
|
|
71
|
+
const { memories } = await options.context.invoke(this.agent, { ...input, allMemory });
|
|
72
72
|
const result = memories.map((memory) => ({
|
|
73
73
|
id: (0, index_js_1.newMemoryId)(),
|
|
74
74
|
content: memory.content,
|
|
@@ -97,11 +97,11 @@ class FSMemoryRecorder extends index_js_1.MemoryRecorder {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
agent;
|
|
100
|
-
async process(input,
|
|
100
|
+
async process(input, options) {
|
|
101
101
|
const allMemory = (await (0, fs_js_1.exists)(this.options.memoryFileName))
|
|
102
102
|
? await (0, promises_1.readFile)(this.options.memoryFileName, "utf-8")
|
|
103
103
|
: "";
|
|
104
|
-
const { memories } = await context.invoke(this.agent, { ...input, allMemory });
|
|
104
|
+
const { memories } = await options.context.invoke(this.agent, { ...input, allMemory });
|
|
105
105
|
const raw = (0, yaml_1.stringify)(memories.map((i) => ({
|
|
106
106
|
content: i.content,
|
|
107
107
|
})));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Agent, type
|
|
1
|
+
import { Agent, type AgentInvokeOptions, type AgentOptions, type Message } from "@aigne/core";
|
|
2
2
|
import { type FullPlanOutput, type StepWithResult } from "./orchestrator-prompts.js";
|
|
3
3
|
/**
|
|
4
4
|
* Re-export orchestrator prompt templates and related types
|
|
@@ -95,10 +95,10 @@ export declare class OrchestratorAgent<I extends Message = Message, O extends Me
|
|
|
95
95
|
* c. Otherwise, execute steps in the plan
|
|
96
96
|
*
|
|
97
97
|
* @param input - Input message containing the objective
|
|
98
|
-
* @param
|
|
98
|
+
* @param options - Agent invocation options
|
|
99
99
|
* @returns Processing result
|
|
100
100
|
*/
|
|
101
|
-
process(input: I,
|
|
101
|
+
process(input: I, options: AgentInvokeOptions): Promise<O>;
|
|
102
102
|
private getFullPlanInput;
|
|
103
103
|
private getFullPlan;
|
|
104
104
|
private synthesizePlanResult;
|
|
@@ -102,11 +102,11 @@ class OrchestratorAgent extends core_1.Agent {
|
|
|
102
102
|
* c. Otherwise, execute steps in the plan
|
|
103
103
|
*
|
|
104
104
|
* @param input - Input message containing the objective
|
|
105
|
-
* @param
|
|
105
|
+
* @param options - Agent invocation options
|
|
106
106
|
* @returns Processing result
|
|
107
107
|
*/
|
|
108
|
-
async process(input,
|
|
109
|
-
const { model } = context;
|
|
108
|
+
async process(input, options) {
|
|
109
|
+
const { model } = options.context;
|
|
110
110
|
if (!model)
|
|
111
111
|
throw new Error("model is required to run OrchestratorAgent");
|
|
112
112
|
const objective = (0, core_1.getMessage)(input);
|
|
@@ -119,13 +119,13 @@ class OrchestratorAgent extends core_1.Agent {
|
|
|
119
119
|
let iterations = 0;
|
|
120
120
|
const maxIterations = this.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
121
121
|
while (iterations++ < maxIterations) {
|
|
122
|
-
const plan = await this.getFullPlan(result, context);
|
|
122
|
+
const plan = await this.getFullPlan(result, options.context);
|
|
123
123
|
result.plan = plan;
|
|
124
124
|
if (plan.isComplete) {
|
|
125
|
-
return this.synthesizePlanResult(result, context);
|
|
125
|
+
return this.synthesizePlanResult(result, options.context);
|
|
126
126
|
}
|
|
127
127
|
for (const step of plan.steps) {
|
|
128
|
-
const stepResult = await this.executeStep(result, step, context);
|
|
128
|
+
const stepResult = await this.executeStep(result, step, options.context);
|
|
129
129
|
result.steps.push(stepResult);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AIAgent } from "@aigne/core";
|
|
2
|
+
declare const mapper: AIAgent<{
|
|
3
|
+
sourceData: string;
|
|
4
|
+
responseSchema: string;
|
|
5
|
+
sourceSchema?: string | undefined;
|
|
6
|
+
instruction?: string | undefined;
|
|
7
|
+
responseData?: string | undefined;
|
|
8
|
+
feedback?: string | undefined;
|
|
9
|
+
}, {
|
|
10
|
+
jsonata: string;
|
|
11
|
+
confidence: number;
|
|
12
|
+
confidenceReasoning: string;
|
|
13
|
+
}>;
|
|
14
|
+
export default mapper;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FunctionAgent } from "@aigne/core";
|
|
2
|
+
declare const reviewer: FunctionAgent<{
|
|
3
|
+
sourceData: string;
|
|
4
|
+
responseSchema: string;
|
|
5
|
+
jsonata: string;
|
|
6
|
+
}, {
|
|
7
|
+
success: boolean;
|
|
8
|
+
data: null;
|
|
9
|
+
feedback: string;
|
|
10
|
+
} | {
|
|
11
|
+
success: boolean;
|
|
12
|
+
data: unknown;
|
|
13
|
+
feedback?: undefined;
|
|
14
|
+
} | {
|
|
15
|
+
success: false;
|
|
16
|
+
data: unknown;
|
|
17
|
+
feedback: string | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export default reviewer;
|