@aigne/core 1.55.1 → 1.57.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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.57.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.56.0...core-v1.57.0) (2025-08-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * **cli:** add searchable checkbox component with dynamic filtering ([#426](https://github.com/AIGNE-io/aigne-framework/issues/426)) ([1a76fe7](https://github.com/AIGNE-io/aigne-framework/commit/1a76fe7c2f7d91bc4041dfcd73850b39a18a036b))
9
+
10
+ ## [1.56.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.55.1...core-v1.56.0) (2025-08-27)
11
+
12
+
13
+ ### Features
14
+
15
+ * **models:** add retry mechanism for network errors and structured output validation errors ([#418](https://github.com/AIGNE-io/aigne-framework/issues/418)) ([52bc9ee](https://github.com/AIGNE-io/aigne-framework/commit/52bc9eec5f4f4fa3c3f26881c405f4f89dad01c9))
16
+
3
17
  ## [1.55.1](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.55.0...core-v1.55.1) (2025-08-26)
4
18
 
5
19
 
@@ -122,6 +122,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
122
122
  */
123
123
  maxRetrieveMemoryCount?: number;
124
124
  hooks?: AgentHooks<I, O> | AgentHooks<I, O>[];
125
+ retryOnError?: Agent<I, O>["retryOnError"] | boolean;
125
126
  }
126
127
  export declare const agentOptionsSchema: ZodObject<{
127
128
  [key in keyof AgentOptions]: ZodType<AgentOptions[key]>;
@@ -217,6 +218,13 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
217
218
  * {@includeCode ../../test/agents/agent.test.ts#example-agent-hooks}
218
219
  */
219
220
  readonly hooks: AgentHooks<I, O>[];
221
+ retryOnError?: {
222
+ retries?: number;
223
+ minTimeout?: number;
224
+ factor?: number;
225
+ randomize?: boolean;
226
+ shouldRetry?: (error: Error) => boolean | Promise<boolean>;
227
+ };
220
228
  /**
221
229
  * List of GuideRail agents applied to this agent
222
230
  *
@@ -57,6 +57,9 @@ const type_utils_js_1 = require("../utils/type-utils.js");
57
57
  const types_js_1 = require("./types.js");
58
58
  __exportStar(require("./types.js"), exports);
59
59
  exports.DEFAULT_INPUT_ACTION_GET = "$get";
60
+ const DEFAULT_RETRIES = 3;
61
+ const DEFAULT_RETRY_MIN_TIMEOUT = 1000;
62
+ const DEFAULT_RETRY_FACTOR = 2;
60
63
  const hooksSchema = zod_1.z.object({
61
64
  onStart: zod_1.z.custom().optional(),
62
65
  onEnd: zod_1.z.custom().optional(),
@@ -82,6 +85,18 @@ exports.agentOptionsSchema = zod_1.z.object({
82
85
  maxRetrieveMemoryCount: zod_1.z.number().optional(),
83
86
  hooks: zod_1.z.union([zod_1.z.array(hooksSchema), hooksSchema]).optional(),
84
87
  guideRails: zod_1.z.array(zod_1.z.custom()).optional(),
88
+ retryOnError: zod_1.z
89
+ .union([
90
+ zod_1.z.boolean(),
91
+ zod_1.z.object({
92
+ retries: zod_1.z.number().int().min(0),
93
+ minTimeout: zod_1.z.number().min(0).optional(),
94
+ factor: zod_1.z.number().min(1).optional(),
95
+ randomize: zod_1.z.boolean().optional(),
96
+ shouldRetry: zod_1.z.custom().optional(),
97
+ }),
98
+ ])
99
+ .optional(),
85
100
  });
86
101
  /**
87
102
  * Agent is the base class for all agents.
@@ -107,6 +122,7 @@ exports.agentOptionsSchema = zod_1.z.object({
107
122
  */
108
123
  class Agent {
109
124
  constructor(options = {}) {
125
+ (0, type_utils_js_1.checkArguments)("Agent options", exports.agentOptionsSchema, options);
110
126
  const { inputSchema, outputSchema } = options;
111
127
  this.name = options.name || this.constructor.name;
112
128
  this.alias = options.alias;
@@ -135,6 +151,12 @@ class Agent {
135
151
  this.asyncMemoryRecord = options.asyncMemoryRecord;
136
152
  this.maxRetrieveMemoryCount = options.maxRetrieveMemoryCount;
137
153
  this.hooks = (0, type_utils_js_1.flat)(options.hooks);
154
+ this.retryOnError =
155
+ options.retryOnError === false
156
+ ? undefined
157
+ : options.retryOnError === true
158
+ ? { retries: DEFAULT_RETRIES }
159
+ : options.retryOnError;
138
160
  this.guideRails = options.guideRails;
139
161
  }
140
162
  /**
@@ -158,6 +180,7 @@ class Agent {
158
180
  * {@includeCode ../../test/agents/agent.test.ts#example-agent-hooks}
159
181
  */
160
182
  hooks;
183
+ retryOnError;
161
184
  /**
162
185
  * List of GuideRail agents applied to this agent
163
186
  *
@@ -387,6 +410,7 @@ class Agent {
387
410
  }
388
411
  async *processStreamingAndRetry(input, options) {
389
412
  let output = {};
413
+ let attempt = 0;
390
414
  for (;;) {
391
415
  // Reset output to avoid accumulating old data
392
416
  const resetOutput = Object.fromEntries(Object.entries(output).map(([key]) => [key, null]));
@@ -415,6 +439,15 @@ class Agent {
415
439
  break;
416
440
  }
417
441
  catch (error) {
442
+ if (this.retryOnError?.retries) {
443
+ const { retries, minTimeout = DEFAULT_RETRY_MIN_TIMEOUT, factor = DEFAULT_RETRY_FACTOR, randomize = false, shouldRetry, } = this.retryOnError;
444
+ if (attempt++ < retries && (!shouldRetry || (await shouldRetry(error)))) {
445
+ const timeout = minTimeout * factor ** (attempt - 1) * (randomize ? 1 + Math.random() : 1);
446
+ logger_js_1.logger.warn(`Agent ${this.name} attempt ${attempt} of ${retries} failed with error: ${error}. Retrying in ${timeout}ms...`);
447
+ await new Promise((resolve) => setTimeout(resolve, timeout));
448
+ continue;
449
+ }
450
+ }
418
451
  const res = await this.processAgentError(input, error, options);
419
452
  if (!res.retry)
420
453
  throw res.error ?? error;
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
- import type { PromiseOrValue } from "../utils/type-utils.js";
2
+ import { type PromiseOrValue } from "../utils/type-utils.js";
3
3
  import { Agent, type AgentInvokeOptions, type AgentOptions, type AgentProcessResult, type Message } from "./agent.js";
4
+ export declare class StructuredOutputError extends Error {
5
+ }
4
6
  /**
5
7
  * ChatModel is an abstract base class for interacting with Large Language Models (LLMs).
6
8
  *
@@ -1,8 +1,21 @@
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
- exports.chatModelOutputUsageSchema = exports.ChatModel = void 0;
6
+ exports.chatModelOutputUsageSchema = exports.ChatModel = exports.StructuredOutputError = void 0;
7
+ const ajv_1 = require("ajv");
8
+ const is_network_error_1 = __importDefault(require("is-network-error"));
4
9
  const zod_1 = require("zod");
10
+ const type_utils_js_1 = require("../utils/type-utils.js");
5
11
  const agent_js_1 = require("./agent.js");
12
+ const CHAT_MODEL_DEFAULT_RETRY_OPTIONS = {
13
+ retries: 3,
14
+ shouldRetry: (error) => error instanceof StructuredOutputError || (0, is_network_error_1.default)(error),
15
+ };
16
+ class StructuredOutputError extends Error {
17
+ }
18
+ exports.StructuredOutputError = StructuredOutputError;
6
19
  /**
7
20
  * ChatModel is an abstract base class for interacting with Large Language Models (LLMs).
8
21
  *
@@ -29,10 +42,21 @@ const agent_js_1 = require("./agent.js");
29
42
  class ChatModel extends agent_js_1.Agent {
30
43
  tag = "ChatModelAgent";
31
44
  constructor(options) {
45
+ if (options)
46
+ (0, type_utils_js_1.checkArguments)("ChatModel", agent_js_1.agentOptionsSchema, options);
47
+ const retryOnError = options?.retryOnError === false
48
+ ? false
49
+ : options?.retryOnError === true
50
+ ? CHAT_MODEL_DEFAULT_RETRY_OPTIONS
51
+ : {
52
+ ...CHAT_MODEL_DEFAULT_RETRY_OPTIONS,
53
+ ...options?.retryOnError,
54
+ };
32
55
  super({
33
56
  ...options,
34
57
  inputSchema: chatModelInputSchema,
35
58
  outputSchema: chatModelOutputSchema,
59
+ retryOnError,
36
60
  });
37
61
  }
38
62
  get credential() {
@@ -137,6 +161,14 @@ class ChatModel extends agent_js_1.Agent {
137
161
  }
138
162
  }
139
163
  }
164
+ if (input.responseFormat?.type === "json_schema" &&
165
+ // NOTE: Should not validate if there are tool calls
166
+ !output.toolCalls?.length) {
167
+ const ajv = new ajv_1.Ajv();
168
+ if (!ajv.validate(input.responseFormat.jsonSchema.schema, output.json)) {
169
+ throw new StructuredOutputError(`Output JSON does not conform to the provided JSON schema: ${ajv.errorsText()}`);
170
+ }
171
+ }
140
172
  super.postprocess(input, output, options);
141
173
  const { usage } = output;
142
174
  if (usage) {
@@ -122,6 +122,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
122
122
  */
123
123
  maxRetrieveMemoryCount?: number;
124
124
  hooks?: AgentHooks<I, O> | AgentHooks<I, O>[];
125
+ retryOnError?: Agent<I, O>["retryOnError"] | boolean;
125
126
  }
126
127
  export declare const agentOptionsSchema: ZodObject<{
127
128
  [key in keyof AgentOptions]: ZodType<AgentOptions[key]>;
@@ -217,6 +218,13 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
217
218
  * {@includeCode ../../test/agents/agent.test.ts#example-agent-hooks}
218
219
  */
219
220
  readonly hooks: AgentHooks<I, O>[];
221
+ retryOnError?: {
222
+ retries?: number;
223
+ minTimeout?: number;
224
+ factor?: number;
225
+ randomize?: boolean;
226
+ shouldRetry?: (error: Error) => boolean | Promise<boolean>;
227
+ };
220
228
  /**
221
229
  * List of GuideRail agents applied to this agent
222
230
  *
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
- import type { PromiseOrValue } from "../utils/type-utils.js";
2
+ import { type PromiseOrValue } from "../utils/type-utils.js";
3
3
  import { Agent, type AgentInvokeOptions, type AgentOptions, type AgentProcessResult, type Message } from "./agent.js";
4
+ export declare class StructuredOutputError extends Error {
5
+ }
4
6
  /**
5
7
  * ChatModel is an abstract base class for interacting with Large Language Models (LLMs).
6
8
  *
@@ -122,6 +122,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
122
122
  */
123
123
  maxRetrieveMemoryCount?: number;
124
124
  hooks?: AgentHooks<I, O> | AgentHooks<I, O>[];
125
+ retryOnError?: Agent<I, O>["retryOnError"] | boolean;
125
126
  }
126
127
  export declare const agentOptionsSchema: ZodObject<{
127
128
  [key in keyof AgentOptions]: ZodType<AgentOptions[key]>;
@@ -217,6 +218,13 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
217
218
  * {@includeCode ../../test/agents/agent.test.ts#example-agent-hooks}
218
219
  */
219
220
  readonly hooks: AgentHooks<I, O>[];
221
+ retryOnError?: {
222
+ retries?: number;
223
+ minTimeout?: number;
224
+ factor?: number;
225
+ randomize?: boolean;
226
+ shouldRetry?: (error: Error) => boolean | Promise<boolean>;
227
+ };
220
228
  /**
221
229
  * List of GuideRail agents applied to this agent
222
230
  *
@@ -9,6 +9,9 @@ import { checkArguments, createAccessorArray, flat, isEmpty, isNil, isNonNullabl
9
9
  import { replaceTransferAgentToName, transferToAgentOutput, } from "./types.js";
10
10
  export * from "./types.js";
11
11
  export const DEFAULT_INPUT_ACTION_GET = "$get";
12
+ const DEFAULT_RETRIES = 3;
13
+ const DEFAULT_RETRY_MIN_TIMEOUT = 1000;
14
+ const DEFAULT_RETRY_FACTOR = 2;
12
15
  const hooksSchema = z.object({
13
16
  onStart: z.custom().optional(),
14
17
  onEnd: z.custom().optional(),
@@ -34,6 +37,18 @@ export const agentOptionsSchema = z.object({
34
37
  maxRetrieveMemoryCount: z.number().optional(),
35
38
  hooks: z.union([z.array(hooksSchema), hooksSchema]).optional(),
36
39
  guideRails: z.array(z.custom()).optional(),
40
+ retryOnError: z
41
+ .union([
42
+ z.boolean(),
43
+ z.object({
44
+ retries: z.number().int().min(0),
45
+ minTimeout: z.number().min(0).optional(),
46
+ factor: z.number().min(1).optional(),
47
+ randomize: z.boolean().optional(),
48
+ shouldRetry: z.custom().optional(),
49
+ }),
50
+ ])
51
+ .optional(),
37
52
  });
38
53
  /**
39
54
  * Agent is the base class for all agents.
@@ -59,6 +74,7 @@ export const agentOptionsSchema = z.object({
59
74
  */
60
75
  export class Agent {
61
76
  constructor(options = {}) {
77
+ checkArguments("Agent options", agentOptionsSchema, options);
62
78
  const { inputSchema, outputSchema } = options;
63
79
  this.name = options.name || this.constructor.name;
64
80
  this.alias = options.alias;
@@ -87,6 +103,12 @@ export class Agent {
87
103
  this.asyncMemoryRecord = options.asyncMemoryRecord;
88
104
  this.maxRetrieveMemoryCount = options.maxRetrieveMemoryCount;
89
105
  this.hooks = flat(options.hooks);
106
+ this.retryOnError =
107
+ options.retryOnError === false
108
+ ? undefined
109
+ : options.retryOnError === true
110
+ ? { retries: DEFAULT_RETRIES }
111
+ : options.retryOnError;
90
112
  this.guideRails = options.guideRails;
91
113
  }
92
114
  /**
@@ -110,6 +132,7 @@ export class Agent {
110
132
  * {@includeCode ../../test/agents/agent.test.ts#example-agent-hooks}
111
133
  */
112
134
  hooks;
135
+ retryOnError;
113
136
  /**
114
137
  * List of GuideRail agents applied to this agent
115
138
  *
@@ -339,6 +362,7 @@ export class Agent {
339
362
  }
340
363
  async *processStreamingAndRetry(input, options) {
341
364
  let output = {};
365
+ let attempt = 0;
342
366
  for (;;) {
343
367
  // Reset output to avoid accumulating old data
344
368
  const resetOutput = Object.fromEntries(Object.entries(output).map(([key]) => [key, null]));
@@ -367,6 +391,15 @@ export class Agent {
367
391
  break;
368
392
  }
369
393
  catch (error) {
394
+ if (this.retryOnError?.retries) {
395
+ const { retries, minTimeout = DEFAULT_RETRY_MIN_TIMEOUT, factor = DEFAULT_RETRY_FACTOR, randomize = false, shouldRetry, } = this.retryOnError;
396
+ if (attempt++ < retries && (!shouldRetry || (await shouldRetry(error)))) {
397
+ const timeout = minTimeout * factor ** (attempt - 1) * (randomize ? 1 + Math.random() : 1);
398
+ logger.warn(`Agent ${this.name} attempt ${attempt} of ${retries} failed with error: ${error}. Retrying in ${timeout}ms...`);
399
+ await new Promise((resolve) => setTimeout(resolve, timeout));
400
+ continue;
401
+ }
402
+ }
370
403
  const res = await this.processAgentError(input, error, options);
371
404
  if (!res.retry)
372
405
  throw res.error ?? error;
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
- import type { PromiseOrValue } from "../utils/type-utils.js";
2
+ import { type PromiseOrValue } from "../utils/type-utils.js";
3
3
  import { Agent, type AgentInvokeOptions, type AgentOptions, type AgentProcessResult, type Message } from "./agent.js";
4
+ export declare class StructuredOutputError extends Error {
5
+ }
4
6
  /**
5
7
  * ChatModel is an abstract base class for interacting with Large Language Models (LLMs).
6
8
  *
@@ -1,5 +1,14 @@
1
+ import { Ajv } from "ajv";
2
+ import isNetworkError from "is-network-error";
1
3
  import { z } from "zod";
2
- import { Agent, } from "./agent.js";
4
+ import { checkArguments } from "../utils/type-utils.js";
5
+ import { Agent, agentOptionsSchema, } from "./agent.js";
6
+ const CHAT_MODEL_DEFAULT_RETRY_OPTIONS = {
7
+ retries: 3,
8
+ shouldRetry: (error) => error instanceof StructuredOutputError || isNetworkError(error),
9
+ };
10
+ export class StructuredOutputError extends Error {
11
+ }
3
12
  /**
4
13
  * ChatModel is an abstract base class for interacting with Large Language Models (LLMs).
5
14
  *
@@ -26,10 +35,21 @@ import { Agent, } from "./agent.js";
26
35
  export class ChatModel extends Agent {
27
36
  tag = "ChatModelAgent";
28
37
  constructor(options) {
38
+ if (options)
39
+ checkArguments("ChatModel", agentOptionsSchema, options);
40
+ const retryOnError = options?.retryOnError === false
41
+ ? false
42
+ : options?.retryOnError === true
43
+ ? CHAT_MODEL_DEFAULT_RETRY_OPTIONS
44
+ : {
45
+ ...CHAT_MODEL_DEFAULT_RETRY_OPTIONS,
46
+ ...options?.retryOnError,
47
+ };
29
48
  super({
30
49
  ...options,
31
50
  inputSchema: chatModelInputSchema,
32
51
  outputSchema: chatModelOutputSchema,
52
+ retryOnError,
33
53
  });
34
54
  }
35
55
  get credential() {
@@ -134,6 +154,14 @@ export class ChatModel extends Agent {
134
154
  }
135
155
  }
136
156
  }
157
+ if (input.responseFormat?.type === "json_schema" &&
158
+ // NOTE: Should not validate if there are tool calls
159
+ !output.toolCalls?.length) {
160
+ const ajv = new Ajv();
161
+ if (!ajv.validate(input.responseFormat.jsonSchema.schema, output.json)) {
162
+ throw new StructuredOutputError(`Output JSON does not conform to the provided JSON schema: ${ajv.errorsText()}`);
163
+ }
164
+ }
137
165
  super.postprocess(input, output, options);
138
166
  const { usage } = output;
139
167
  if (usage) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.55.1",
3
+ "version": "1.57.0",
4
4
  "description": "The functional core of agentic AI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -66,11 +66,12 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@aigne/json-schema-to-zod": "^1.3.3",
69
- "@inquirer/prompts": "^7.6.0",
69
+ "@inquirer/prompts": "^7.8.4",
70
70
  "@modelcontextprotocol/sdk": "^1.15.0",
71
71
  "@opentelemetry/api": "^1.9.0",
72
72
  "@opentelemetry/sdk-trace-base": "^2.0.1",
73
73
  "@types/debug": "^4.1.12",
74
+ "ajv": "^8.17.1",
74
75
  "camelize-ts": "^3.0.0",
75
76
  "content-type": "^1.0.5",
76
77
  "debug": "^4.4.1",
@@ -78,6 +79,7 @@
78
79
  "fast-deep-equal": "^3.1.3",
79
80
  "fastq": "^1.19.1",
80
81
  "immer": "^10.1.1",
82
+ "is-network-error": "^1.1.0",
81
83
  "jaison": "^2.0.2",
82
84
  "jsonata": "^2.0.6",
83
85
  "nunjucks": "^3.2.4",