@agentica/core 0.41.2 → 0.41.4

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/index.mjs CHANGED
@@ -966,7 +966,7 @@ const ChatGptTokenUsageAggregator = {
966
966
 
967
967
  const JsonUtil = {
968
968
  parse,
969
- stringifyValidateFailure
969
+ stringifyValidationFailure
970
970
  };
971
971
 
972
972
  const pipe = (...fns) => str => fns.reduce(((acc, fn) => fn(acc)), str);
@@ -976,7 +976,7 @@ function parse(str) {
976
976
  return JSON.parse(str);
977
977
  }
978
978
 
979
- function stringifyValidateFailure(failure) {
979
+ function stringifyValidationFailure(failure) {
980
980
  const usedErrors = new Set;
981
981
  const jsonOutput = stringify({
982
982
  value: failure.data,
@@ -1002,13 +1002,33 @@ function stringify(props) {
1002
1002
  return `${indent}undefined${errorComment}`;
1003
1003
  }
1004
1004
  if (Array.isArray(value)) {
1005
+ const missingElementErrors = getMissingArrayElementErrors(path, errors, usedErrors);
1006
+ const hasMissingElements = missingElementErrors.length > 0;
1005
1007
  if (value.length === 0) {
1008
+ if (hasMissingElements) {
1009
+ const innerIndent = " ".repeat(tab + 1);
1010
+ const lines = [];
1011
+ lines.push(`${indent}[${errorComment}`);
1012
+ missingElementErrors.forEach(((e, idx) => {
1013
+ const errComment = ` // ❌ ${JSON.stringify([ {
1014
+ path: e.path,
1015
+ expected: e.expected,
1016
+ description: e.description
1017
+ } ])}`;
1018
+ const comma = idx < missingElementErrors.length - 1 ? "," : "";
1019
+ lines.push(`${innerIndent}undefined${comma}${errComment}`);
1020
+ }));
1021
+ lines.push(`${indent}]`);
1022
+ return lines.join("\n");
1023
+ }
1006
1024
  return `${indent}[]${errorComment}`;
1007
1025
  }
1008
1026
  const lines = [];
1009
1027
  lines.push(`${indent}[${errorComment}`);
1010
1028
  value.forEach(((item, index) => {
1011
1029
  const itemPath = `${path}[${index}]`;
1030
+ const isLastElement = index === value.length - 1;
1031
+ const needsComma = !isLastElement || hasMissingElements;
1012
1032
  let itemStr = stringify({
1013
1033
  value: item,
1014
1034
  errors,
@@ -1018,7 +1038,7 @@ function stringify(props) {
1018
1038
  inToJson: false,
1019
1039
  usedErrors
1020
1040
  });
1021
- if (index < value.length - 1) {
1041
+ if (needsComma) {
1022
1042
  const itemLines = itemStr.split("\n");
1023
1043
  const lastLine = itemLines[itemLines.length - 1];
1024
1044
  const commentIndex = lastLine.indexOf(" //");
@@ -1031,6 +1051,18 @@ function stringify(props) {
1031
1051
  }
1032
1052
  lines.push(itemStr);
1033
1053
  }));
1054
+ if (hasMissingElements) {
1055
+ const innerIndent = " ".repeat(tab + 1);
1056
+ missingElementErrors.forEach(((e, idx) => {
1057
+ const errComment = ` // ❌ ${JSON.stringify([ {
1058
+ path: e.path,
1059
+ expected: e.expected,
1060
+ description: e.description
1061
+ } ])}`;
1062
+ const comma = idx < missingElementErrors.length - 1 ? "," : "";
1063
+ lines.push(`${innerIndent}undefined${comma}${errComment}`);
1064
+ }));
1065
+ }
1034
1066
  lines.push(`${indent}]`);
1035
1067
  return lines.join("\n");
1036
1068
  }
@@ -1047,9 +1079,15 @@ function stringify(props) {
1047
1079
  usedErrors
1048
1080
  });
1049
1081
  }
1050
- const existingEntries = Object.entries(value).filter((([_, val]) => val !== undefined));
1082
+ const allEntries = Object.entries(value);
1083
+ const definedEntries = allEntries.filter((([_, val]) => val !== undefined));
1084
+ const undefinedEntryKeys = new Set(allEntries.filter((([_, val]) => val === undefined)).map((([key]) => key)));
1051
1085
  const missingKeys = getMissingProperties(path, value, errors);
1052
- const allKeys = [ ...existingEntries.map((([key]) => key)), ...missingKeys ];
1086
+ const undefinedKeysWithErrors = Array.from(undefinedEntryKeys).filter((key => {
1087
+ const propPath = Escaper.variable(key) ? `${path}.${key}` : `${path}[${JSON.stringify(key)}]`;
1088
+ return errors.some((e => e.path.startsWith(propPath)));
1089
+ }));
1090
+ const allKeys = [ ...definedEntries.map((([key]) => key)), ...undefinedKeysWithErrors, ...missingKeys ];
1053
1091
  if (allKeys.length === 0) {
1054
1092
  return `${indent}{}${errorComment}`;
1055
1093
  }
@@ -1058,15 +1096,16 @@ function stringify(props) {
1058
1096
  allKeys.forEach(((key, index, array) => {
1059
1097
  const propPath = Escaper.variable(key) ? `${path}.${key}` : `${path}[${JSON.stringify(key)}]`;
1060
1098
  const propIndent = " ".repeat(tab + 1);
1061
- const val = missingKeys.includes(key) ? undefined : value[key];
1099
+ const val = missingKeys.includes(key) || undefinedKeysWithErrors.includes(key) ? undefined : value[key];
1062
1100
  if (val === undefined || val === null || typeof val === "boolean" || typeof val === "number" || typeof val === "string") {
1063
1101
  const propErrorComment = getErrorComment(propPath, errors, usedErrors);
1064
- const valueStr = val === undefined ? `${propIndent}"${key}": undefined` : `${propIndent}"${key}": ${JSON.stringify(val)}`;
1102
+ const keyStr = JSON.stringify(key);
1103
+ const valueStr = val === undefined ? `${propIndent}${keyStr}: undefined` : `${propIndent}${keyStr}: ${JSON.stringify(val)}`;
1065
1104
  const withComma = index < array.length - 1 ? `${valueStr},` : valueStr;
1066
1105
  const line = withComma + propErrorComment;
1067
1106
  lines.push(line);
1068
1107
  } else {
1069
- const keyLine = `${propIndent}"${key}": `;
1108
+ const keyLine = `${propIndent}${JSON.stringify(key)}: `;
1070
1109
  let valStr = stringify({
1071
1110
  value: val,
1072
1111
  errors,
@@ -1114,6 +1153,13 @@ function getErrorComment(path, errors, usedErrors) {
1114
1153
  }))))}`;
1115
1154
  }
1116
1155
 
1156
+ function getMissingArrayElementErrors(path, errors, usedErrors) {
1157
+ const wildcardPath = `${path}[]`;
1158
+ const missingErrors = errors.filter((e => e.path === wildcardPath));
1159
+ missingErrors.forEach((e => usedErrors.add(e)));
1160
+ return missingErrors;
1161
+ }
1162
+
1117
1163
  function getMissingProperties(path, value, errors) {
1118
1164
  const missingKeys = new Set;
1119
1165
  for (const e of errors) {
@@ -1132,8 +1178,20 @@ function extractDirectChildKey(parentPath, errorPath) {
1132
1178
  return null;
1133
1179
  }
1134
1180
  const suffix = errorPath.slice(parentPath.length);
1135
- const match = suffix.match(/^\.([^.[\]]+)$/);
1136
- return match !== null ? match[1] : null;
1181
+ const dotMatch = suffix.match(/^\.([^.[\]]+)$/);
1182
+ if (dotMatch !== null) {
1183
+ return dotMatch[1];
1184
+ }
1185
+ const bracketMatch = suffix.match(/^\[("[^"\\]*(?:\\.[^"\\]*)*")\]$/);
1186
+ if (bracketMatch !== null) {
1187
+ try {
1188
+ const parsed = JSON.parse(bracketMatch[1]);
1189
+ if (typeof parsed === "string") {
1190
+ return parsed;
1191
+ }
1192
+ } catch {}
1193
+ }
1194
+ return null;
1137
1195
  }
1138
1196
 
1139
1197
  function transformCompletionChunk(source) {
@@ -1712,9 +1770,9 @@ async function correctTypeError(ctx, callEvent, validateEvent, previousValidatio
1712
1770
  toolCall: {
1713
1771
  id: callEvent.id,
1714
1772
  arguments: JSON.stringify(callEvent.arguments),
1715
- result: [ "🚨 VALIDATION FAILURE: Your function arguments do not conform to the required schema.", "", "The validation errors below represent computed absolute truth from rigorous type validation.", "Each error is marked with ❌ comments showing the exact location, expected type, and actual value.", "", "You must fix ALL errors to achieve 100% schema compliance.", "", JsonUtil.stringifyValidateFailure(validateEvent.result) ].join("\n")
1773
+ result: [ "🚨 VALIDATION FAILURE: Your function arguments do not conform to the required schema.", "", "The validation errors below represent computed absolute truth from rigorous type validation.", "Each error is marked with ❌ comments showing the exact location, expected type, and actual value.", "", "You must fix ALL errors to achieve 100% schema compliance.", "", JsonUtil.stringifyValidationFailure(validateEvent.result) ].join("\n")
1716
1774
  },
1717
- systemPrompt: ctx.config?.systemPrompt?.validate?.(previousValidationErrors.slice(0, -1)) ?? [ AgenticaSystemPrompt.VALIDATE, ...previousValidationErrors.length > 1 ? [ "", AgenticaSystemPrompt.VALIDATE_REPEATED.replace("${{HISTORICAL_ERRORS}}", previousValidationErrors.slice(0, -1).map(((ve, i) => [ `### ${i + 1}. Previous Validation Error`, "", JsonUtil.stringifyValidateFailure(ve.result) ].join("\n"))).join("\n\n")) ] : [] ].join("\n"),
1775
+ systemPrompt: ctx.config?.systemPrompt?.validate?.(previousValidationErrors.slice(0, -1)) ?? [ AgenticaSystemPrompt.VALIDATE, ...previousValidationErrors.length > 1 ? [ "", AgenticaSystemPrompt.VALIDATE_REPEATED.replace("${{HISTORICAL_ERRORS}}", previousValidationErrors.slice(0, -1).map(((ve, i) => [ `### ${i + 1}. Previous Validation Error`, "", JsonUtil.stringifyValidationFailure(ve.result) ].join("\n"))).join("\n\n")) ] : [] ].join("\n"),
1718
1776
  life,
1719
1777
  previousValidationErrors
1720
1778
  });
@@ -1818,7 +1876,13 @@ async function correctError(ctx, props) {
1818
1876
  return ChatGptCompletionMessageUtil.merge(await StreamUtil.readAll(result.value));
1819
1877
  })();
1820
1878
  const toolCall = completion.choices?.[0]?.message.tool_calls?.filter((tc => tc.type === "function")).find((s => s.function.name === props.operation.name));
1821
- return toolCall === undefined ? props.giveUp() : predicate(ctx, props.operation, toolCall, props.previousValidationErrors, props.life);
1879
+ if (toolCall === undefined) {
1880
+ return correctError(ctx, {
1881
+ ...props,
1882
+ life: props.life - 1
1883
+ });
1884
+ }
1885
+ return predicate(ctx, props.operation, toolCall, props.previousValidationErrors, props.life);
1822
1886
  }
1823
1887
 
1824
1888
  async function executeFunction(call, operation) {