@aigne/core 1.63.0-beta.7 → 1.63.0-beta.9

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,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.63.0-beta.9](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.63.0-beta.8...core-v1.63.0-beta.9) (2025-10-16)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * allow custom user messages and prevent duplicate user content ([#632](https://github.com/AIGNE-io/aigne-framework/issues/632)) ([6c883b2](https://github.com/AIGNE-io/aigne-framework/commit/6c883b2d57a65e9b46232cece91fc6aa1de03aba))
9
+ * **models:** auto retry when got emtpy response from gemini ([#636](https://github.com/AIGNE-io/aigne-framework/issues/636)) ([9367cef](https://github.com/AIGNE-io/aigne-framework/commit/9367cef49ea4c0c87b8a36b454deb2efaee6886f))
10
+
11
+
12
+ ### Dependencies
13
+
14
+ * The following workspace dependencies were updated
15
+ * dependencies
16
+ * @aigne/observability-api bumped to 0.11.2-beta.4
17
+
18
+ ## [1.63.0-beta.8](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.63.0-beta.7...core-v1.63.0-beta.8) (2025-10-16)
19
+
20
+
21
+ ### Dependencies
22
+
23
+ * The following workspace dependencies were updated
24
+ * dependencies
25
+ * @aigne/observability-api bumped to 0.11.2-beta.3
26
+
3
27
  ## [1.63.0-beta.7](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.63.0-beta.6...core-v1.63.0-beta.7) (2025-10-15)
4
28
 
5
29
 
@@ -20,7 +20,8 @@ export declare const DEFAULT_INPUT_ACTION_GET = "$get";
20
20
  */
21
21
  export interface Message extends Record<string, unknown> {
22
22
  $meta?: {
23
- usage: ContextUsage;
23
+ usage?: ContextUsage;
24
+ [key: string]: unknown;
24
25
  };
25
26
  }
26
27
  /**
@@ -451,7 +451,10 @@ class Agent {
451
451
  (0, stream_utils_js_1.mergeAgentResponseChunk)(output, chunk);
452
452
  yield chunk;
453
453
  }
454
- const result = await this.processAgentOutput(input, output, options);
454
+ let result = await this.processAgentOutput(input, output, options);
455
+ if (attempt > 0) {
456
+ result = { ...result, $meta: { ...result.$meta, retries: attempt } };
457
+ }
455
458
  if (result && !(0, fast_deep_equal_1.default)(result, output)) {
456
459
  yield { delta: { json: result } };
457
460
  }
@@ -461,7 +464,8 @@ class Agent {
461
464
  catch (error) {
462
465
  if (this.retryOnError?.retries) {
463
466
  const { retries, minTimeout = DEFAULT_RETRY_MIN_TIMEOUT, factor = DEFAULT_RETRY_FACTOR, randomize = false, shouldRetry, } = this.retryOnError;
464
- if (attempt++ < retries && (!shouldRetry || (await shouldRetry(error)))) {
467
+ if (attempt < retries && (!shouldRetry || (await shouldRetry(error)))) {
468
+ attempt++;
465
469
  const timeout = minTimeout * factor ** (attempt - 1) * (randomize ? 1 + Math.random() : 1);
466
470
  logger_js_1.logger.warn(`Agent ${this.name} attempt ${attempt} of ${retries} failed with error: ${error}. Retrying in ${timeout}ms...`);
467
471
  await new Promise((resolve) => setTimeout(resolve, timeout));
@@ -469,8 +473,12 @@ class Agent {
469
473
  }
470
474
  }
471
475
  const res = await this.processAgentError(input, error, options);
472
- if (!res.retry)
473
- throw res.error ?? error;
476
+ if (!res.retry) {
477
+ const e = res.error ?? error;
478
+ if (attempt > 0)
479
+ e.message += ` (after ${attempt} retries)`;
480
+ throw e;
481
+ }
474
482
  }
475
483
  }
476
484
  }
@@ -42,7 +42,7 @@ const type_utils_js_1 = require("../utils/type-utils.js");
42
42
  const agent_js_1 = require("./agent.js");
43
43
  const model_js_1 = require("./model.js");
44
44
  const CHAT_MODEL_DEFAULT_RETRY_OPTIONS = {
45
- retries: 3,
45
+ retries: 10,
46
46
  shouldRetry: async (error) => error instanceof StructuredOutputError || (await Promise.resolve().then(() => __importStar(require("is-network-error")))).default(error),
47
47
  };
48
48
  class StructuredOutputError extends Error {
@@ -87,9 +87,9 @@ class PromptBuilder {
87
87
  const { input } = options;
88
88
  const inputKey = options.agent?.inputKey;
89
89
  const message = inputKey && typeof input?.[inputKey] === "string" ? input[inputKey] : undefined;
90
- const messages = (await (typeof this.instructions === "string"
90
+ const [messages, otherCustomMessages] = (0, type_utils_js_1.partition)((await (typeof this.instructions === "string"
91
91
  ? template_js_1.ChatMessagesTemplate.from([template_js_1.SystemMessageTemplate.from(this.instructions)])
92
- : this.instructions)?.format(options.input, { workingDir: this.workingDir })) ?? [];
92
+ : this.instructions)?.format(options.input, { workingDir: this.workingDir })) ?? [], (i) => i.role === "system");
93
93
  const inputFileKey = options.agent?.inputFileKey;
94
94
  const files = (0, type_utils_js_1.flat)(inputFileKey
95
95
  ? (0, type_utils_js_1.checkArguments)("Check input files", (0, schema_js_1.optionalize)(model_js_1.fileUnionContentsSchema), input?.[inputFileKey])
@@ -102,7 +102,7 @@ class PromptBuilder {
102
102
  memories.push(...options.context.memories);
103
103
  }
104
104
  if (options.agent?.afs) {
105
- messages.push(template_js_1.SystemMessageTemplate.from(await (0, afs_builtin_prompt_js_1.getAFSSystemPrompt)(options.agent.afs)));
105
+ messages.push(await template_js_1.SystemMessageTemplate.from(await (0, afs_builtin_prompt_js_1.getAFSSystemPrompt)(options.agent.afs)).format({}));
106
106
  if (options.agent.afsConfig?.injectHistory) {
107
107
  const history = await options.agent.afs.list(afs_1.AFSHistory.Path, {
108
108
  limit: options.agent.afsConfig.historyWindowSize || 10,
@@ -132,12 +132,21 @@ class PromptBuilder {
132
132
  }
133
133
  if (message || files.length) {
134
134
  const content = [];
135
- if (message)
135
+ if (message &&
136
+ // avoid duplicate user messages: developer may have already included the message in the custom user messages
137
+ !otherCustomMessages.some((i) => i.role === "user" &&
138
+ (typeof i.content === "string"
139
+ ? i.content.includes(message)
140
+ : i.content?.some((c) => c.type === "text" && c.text.includes(message))))) {
136
141
  content.push({ type: "text", text: message });
142
+ }
137
143
  if (files.length)
138
144
  content.push(...files);
139
- messages.push({ role: "user", content });
145
+ if (content.length) {
146
+ messages.push({ role: "user", content });
147
+ }
140
148
  }
149
+ messages.push(...otherCustomMessages);
141
150
  return this.refineMessages(options, messages);
142
151
  }
143
152
  refineMessages(options, messages) {
@@ -20,7 +20,8 @@ export declare const DEFAULT_INPUT_ACTION_GET = "$get";
20
20
  */
21
21
  export interface Message extends Record<string, unknown> {
22
22
  $meta?: {
23
- usage: ContextUsage;
23
+ usage?: ContextUsage;
24
+ [key: string]: unknown;
24
25
  };
25
26
  }
26
27
  /**
@@ -20,7 +20,8 @@ export declare const DEFAULT_INPUT_ACTION_GET = "$get";
20
20
  */
21
21
  export interface Message extends Record<string, unknown> {
22
22
  $meta?: {
23
- usage: ContextUsage;
23
+ usage?: ContextUsage;
24
+ [key: string]: unknown;
24
25
  };
25
26
  }
26
27
  /**
@@ -403,7 +403,10 @@ export class Agent {
403
403
  mergeAgentResponseChunk(output, chunk);
404
404
  yield chunk;
405
405
  }
406
- const result = await this.processAgentOutput(input, output, options);
406
+ let result = await this.processAgentOutput(input, output, options);
407
+ if (attempt > 0) {
408
+ result = { ...result, $meta: { ...result.$meta, retries: attempt } };
409
+ }
407
410
  if (result && !equal(result, output)) {
408
411
  yield { delta: { json: result } };
409
412
  }
@@ -413,7 +416,8 @@ export class Agent {
413
416
  catch (error) {
414
417
  if (this.retryOnError?.retries) {
415
418
  const { retries, minTimeout = DEFAULT_RETRY_MIN_TIMEOUT, factor = DEFAULT_RETRY_FACTOR, randomize = false, shouldRetry, } = this.retryOnError;
416
- if (attempt++ < retries && (!shouldRetry || (await shouldRetry(error)))) {
419
+ if (attempt < retries && (!shouldRetry || (await shouldRetry(error)))) {
420
+ attempt++;
417
421
  const timeout = minTimeout * factor ** (attempt - 1) * (randomize ? 1 + Math.random() : 1);
418
422
  logger.warn(`Agent ${this.name} attempt ${attempt} of ${retries} failed with error: ${error}. Retrying in ${timeout}ms...`);
419
423
  await new Promise((resolve) => setTimeout(resolve, timeout));
@@ -421,8 +425,12 @@ export class Agent {
421
425
  }
422
426
  }
423
427
  const res = await this.processAgentError(input, error, options);
424
- if (!res.retry)
425
- throw res.error ?? error;
428
+ if (!res.retry) {
429
+ const e = res.error ?? error;
430
+ if (attempt > 0)
431
+ e.message += ` (after ${attempt} retries)`;
432
+ throw e;
433
+ }
426
434
  }
427
435
  }
428
436
  }
@@ -6,7 +6,7 @@ import { checkArguments, isNil, omitByDeep } from "../utils/type-utils.js";
6
6
  import { agentOptionsSchema, } from "./agent.js";
7
7
  import { fileContentSchema, fileUnionContentSchema, localContentSchema, Model, urlContentSchema, } from "./model.js";
8
8
  const CHAT_MODEL_DEFAULT_RETRY_OPTIONS = {
9
- retries: 3,
9
+ retries: 10,
10
10
  shouldRetry: async (error) => error instanceof StructuredOutputError || (await import("is-network-error")).default(error),
11
11
  };
12
12
  export class StructuredOutputError extends Error {
@@ -84,9 +84,9 @@ export class PromptBuilder {
84
84
  const { input } = options;
85
85
  const inputKey = options.agent?.inputKey;
86
86
  const message = inputKey && typeof input?.[inputKey] === "string" ? input[inputKey] : undefined;
87
- const messages = (await (typeof this.instructions === "string"
87
+ const [messages, otherCustomMessages] = partition((await (typeof this.instructions === "string"
88
88
  ? ChatMessagesTemplate.from([SystemMessageTemplate.from(this.instructions)])
89
- : this.instructions)?.format(options.input, { workingDir: this.workingDir })) ?? [];
89
+ : this.instructions)?.format(options.input, { workingDir: this.workingDir })) ?? [], (i) => i.role === "system");
90
90
  const inputFileKey = options.agent?.inputFileKey;
91
91
  const files = flat(inputFileKey
92
92
  ? checkArguments("Check input files", optionalize(fileUnionContentsSchema), input?.[inputFileKey])
@@ -99,7 +99,7 @@ export class PromptBuilder {
99
99
  memories.push(...options.context.memories);
100
100
  }
101
101
  if (options.agent?.afs) {
102
- messages.push(SystemMessageTemplate.from(await getAFSSystemPrompt(options.agent.afs)));
102
+ messages.push(await SystemMessageTemplate.from(await getAFSSystemPrompt(options.agent.afs)).format({}));
103
103
  if (options.agent.afsConfig?.injectHistory) {
104
104
  const history = await options.agent.afs.list(AFSHistory.Path, {
105
105
  limit: options.agent.afsConfig.historyWindowSize || 10,
@@ -129,12 +129,21 @@ export class PromptBuilder {
129
129
  }
130
130
  if (message || files.length) {
131
131
  const content = [];
132
- if (message)
132
+ if (message &&
133
+ // avoid duplicate user messages: developer may have already included the message in the custom user messages
134
+ !otherCustomMessages.some((i) => i.role === "user" &&
135
+ (typeof i.content === "string"
136
+ ? i.content.includes(message)
137
+ : i.content?.some((c) => c.type === "text" && c.text.includes(message))))) {
133
138
  content.push({ type: "text", text: message });
139
+ }
134
140
  if (files.length)
135
141
  content.push(...files);
136
- messages.push({ role: "user", content });
142
+ if (content.length) {
143
+ messages.push({ role: "user", content });
144
+ }
137
145
  }
146
+ messages.push(...otherCustomMessages);
138
147
  return this.refineMessages(options, messages);
139
148
  }
140
149
  refineMessages(options, messages) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.63.0-beta.7",
3
+ "version": "1.63.0-beta.9",
4
4
  "description": "The functional core of agentic AI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -92,8 +92,8 @@
92
92
  "zod": "^3.25.67",
93
93
  "zod-from-json-schema": "^0.0.5",
94
94
  "zod-to-json-schema": "^3.24.6",
95
- "@aigne/observability-api": "^0.11.2-beta.2",
96
95
  "@aigne/afs": "^1.1.0-beta",
96
+ "@aigne/observability-api": "^0.11.2-beta.4",
97
97
  "@aigne/platform-helpers": "^0.6.3"
98
98
  },
99
99
  "devDependencies": {