openkbs 0.0.53 → 0.0.55

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.
Files changed (27) hide show
  1. package/README.md +1490 -202
  2. package/package.json +2 -1
  3. package/src/actions.js +345 -1
  4. package/src/index.js +17 -1
  5. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/app/instructions.txt +44 -9
  6. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/actions.js +43 -42
  7. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/handler.js +14 -8
  8. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Frontend/contentRender.js +95 -12
  9. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/README.md +64 -0
  10. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/app/instructions.txt +160 -0
  11. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/app/settings.json +7 -0
  12. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/actions.js +258 -0
  13. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onRequest.js +13 -0
  14. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onRequest.json +3 -0
  15. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onResponse.js +13 -0
  16. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onResponse.json +3 -0
  17. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Frontend/contentRender.js +170 -0
  18. package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Frontend/contentRender.json +3 -0
  19. package/templates/.openkbs/knowledge/metadata.json +1 -1
  20. package/templates/CLAUDE.md +593 -222
  21. package/templates/app/instructions.txt +13 -1
  22. package/templates/app/settings.json +5 -6
  23. package/templates/src/Events/actions.js +43 -9
  24. package/templates/src/Events/handler.js +24 -25
  25. package/templates/webpack.contentRender.config.js +8 -2
  26. package/version.json +3 -3
  27. package/MODIFY.md +0 -132
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openkbs",
3
- "version": "0.0.53",
3
+ "version": "0.0.55",
4
4
  "description": "OpenKBS - Command Line Interface",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -36,6 +36,7 @@
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
38
  "@aws-sdk/client-s3": "^3.658.1",
39
+ "archiver": "^7.0.1",
39
40
  "bip39": "^3.1.0",
40
41
  "cli-spinner": "^0.2.10",
41
42
  "commander": "^12.1.0",
package/src/actions.js CHANGED
@@ -837,6 +837,349 @@ async function downloadClaudeMdFromS3(claudeMdPath) {
837
837
  }
838
838
  }
839
839
 
840
+ // ===== Elastic Functions Commands =====
841
+
842
+ async function fnAction(subCommand, args = []) {
843
+ const localKBData = await fetchLocalKBData();
844
+ const kbId = localKBData?.kbId;
845
+
846
+ if (!kbId) {
847
+ return console.red('No KB found. Please run this command in a KB project directory.');
848
+ }
849
+
850
+ const { kbToken } = await fetchKBJWT(kbId);
851
+
852
+ switch (subCommand) {
853
+ case 'list':
854
+ return await fnListAction(kbToken);
855
+ case 'deploy':
856
+ return await fnDeployAction(kbToken, args[0], args.slice(1));
857
+ case 'delete':
858
+ return await fnDeleteAction(kbToken, args[0]);
859
+ case 'logs':
860
+ return await fnLogsAction(kbToken, args[0], args.slice(1));
861
+ case 'env':
862
+ return await fnEnvAction(kbToken, args[0], args.slice(1));
863
+ case 'invoke':
864
+ return await fnInvokeAction(kbToken, args[0], args.slice(1));
865
+ default:
866
+ console.log('Usage: openkbs fn <command> [options]');
867
+ console.log('');
868
+ console.log('Commands:');
869
+ console.log(' list List all elastic functions');
870
+ console.log(' deploy <name> Deploy a function from ./functions/<name>/');
871
+ console.log(' delete <name> Delete a function');
872
+ console.log(' logs <name> View function logs');
873
+ console.log(' env <name> [KEY=value] View or set environment variables');
874
+ console.log(' invoke <name> [payload] Invoke a function');
875
+ console.log('');
876
+ console.log('Options for deploy:');
877
+ console.log(' --region <region> Region (us-east-2, eu-central-1, ap-southeast-1)');
878
+ console.log(' --memory <mb> Memory size (128-3008 MB)');
879
+ console.log(' --timeout <seconds> Timeout (1-900 seconds)');
880
+ }
881
+ }
882
+
883
+ async function fnListAction(kbToken) {
884
+ try {
885
+ const response = await makePostRequest(KB_API_URL, {
886
+ token: kbToken,
887
+ action: 'listElasticFunctions'
888
+ });
889
+
890
+ if (response.error) {
891
+ return console.red('Error:', response.error);
892
+ }
893
+
894
+ const functions = response.functions || [];
895
+
896
+ if (functions.length === 0) {
897
+ console.log('No elastic functions found.');
898
+ console.log('');
899
+ console.log('Create a function:');
900
+ console.log(' 1. Create directory: mkdir -p functions/hello');
901
+ console.log(' 2. Create handler: echo "export const handler = async (event) => ({ body: \'Hello!\' });" > functions/hello/index.mjs');
902
+ console.log(' 3. Deploy: openkbs fn deploy hello --region us-east-2');
903
+ return;
904
+ }
905
+
906
+ console.log('Elastic Functions:\n');
907
+ const maxNameLen = Math.max(...functions.map(f => f.functionName.length), 10);
908
+
909
+ functions.forEach(f => {
910
+ const name = f.functionName.padEnd(maxNameLen);
911
+ const region = f.region || 'unknown';
912
+ const url = f.customUrl || f.functionUrl || 'N/A';
913
+ console.log(` ${name} ${region} ${url}`);
914
+ });
915
+ } catch (error) {
916
+ console.red('Error listing functions:', error.message);
917
+ }
918
+ }
919
+
920
+ async function fnDeployAction(kbToken, functionName, args) {
921
+ if (!functionName) {
922
+ return console.red('Function name required. Usage: openkbs fn deploy <name>');
923
+ }
924
+
925
+ // Parse arguments
926
+ let region = 'us-east-2';
927
+ let memorySize = 256;
928
+ let timeout = 30;
929
+
930
+ for (let i = 0; i < args.length; i++) {
931
+ if (args[i] === '--region' && args[i + 1]) {
932
+ region = args[++i];
933
+ } else if (args[i] === '--memory' && args[i + 1]) {
934
+ memorySize = parseInt(args[++i]);
935
+ } else if (args[i] === '--timeout' && args[i + 1]) {
936
+ timeout = parseInt(args[++i]);
937
+ }
938
+ }
939
+
940
+ const functionDir = path.join(process.cwd(), 'functions', functionName);
941
+
942
+ if (!await fs.pathExists(functionDir)) {
943
+ return console.red(`Function directory not found: ${functionDir}`);
944
+ }
945
+
946
+ console.log(`Deploying function '${functionName}' to ${region}...`);
947
+
948
+ try {
949
+ // Create a zip of the function directory
950
+ const archiver = require('archiver');
951
+ const { PassThrough } = require('stream');
952
+
953
+ const archive = archiver('zip', { zlib: { level: 9 } });
954
+ const chunks = [];
955
+ const passThrough = new PassThrough();
956
+
957
+ passThrough.on('data', chunk => chunks.push(chunk));
958
+
959
+ await new Promise((resolve, reject) => {
960
+ passThrough.on('end', resolve);
961
+ passThrough.on('error', reject);
962
+ archive.on('error', reject);
963
+
964
+ archive.pipe(passThrough);
965
+ archive.directory(functionDir, false);
966
+ archive.finalize();
967
+ });
968
+
969
+ const zipBuffer = Buffer.concat(chunks);
970
+ const code = zipBuffer.toString('base64');
971
+
972
+ // Check if function exists
973
+ const listResponse = await makePostRequest(KB_API_URL, {
974
+ token: kbToken,
975
+ action: 'listElasticFunctions'
976
+ });
977
+
978
+ const existingFunc = listResponse.functions?.find(f => f.functionName === functionName);
979
+
980
+ let response;
981
+ if (existingFunc) {
982
+ // Update existing function
983
+ console.log('Updating existing function...');
984
+ response = await makePostRequest(KB_API_URL, {
985
+ token: kbToken,
986
+ action: 'updateElasticFunction',
987
+ functionName,
988
+ code
989
+ });
990
+ } else {
991
+ // Create new function
992
+ console.log('Creating new function...');
993
+ response = await makePostRequest(KB_API_URL, {
994
+ token: kbToken,
995
+ action: 'createElasticFunction',
996
+ functionName,
997
+ code,
998
+ region,
999
+ memorySize,
1000
+ timeout
1001
+ });
1002
+ }
1003
+
1004
+ if (response.error) {
1005
+ return console.red('Deploy failed:', response.error);
1006
+ }
1007
+
1008
+ console.green('Deploy successful!');
1009
+ if (response.functionUrl) {
1010
+ console.log(`Lambda URL: ${response.functionUrl}`);
1011
+ }
1012
+ if (response.customUrl) {
1013
+ console.log(`Custom URL: ${response.customUrl}`);
1014
+ }
1015
+ } catch (error) {
1016
+ console.red('Deploy failed:', error.message);
1017
+ }
1018
+ }
1019
+
1020
+ async function fnDeleteAction(kbToken, functionName) {
1021
+ if (!functionName) {
1022
+ return console.red('Function name required. Usage: openkbs fn delete <name>');
1023
+ }
1024
+
1025
+ try {
1026
+ console.log(`Deleting function '${functionName}'...`);
1027
+
1028
+ const response = await makePostRequest(KB_API_URL, {
1029
+ token: kbToken,
1030
+ action: 'deleteElasticFunction',
1031
+ functionName
1032
+ });
1033
+
1034
+ if (response.error) {
1035
+ return console.red('Delete failed:', response.error);
1036
+ }
1037
+
1038
+ console.green(`Function '${functionName}' deleted successfully.`);
1039
+ } catch (error) {
1040
+ console.red('Delete failed:', error.message);
1041
+ }
1042
+ }
1043
+
1044
+ async function fnLogsAction(kbToken, functionName, args) {
1045
+ if (!functionName) {
1046
+ return console.red('Function name required. Usage: openkbs fn logs <name>');
1047
+ }
1048
+
1049
+ try {
1050
+ let limit = 50;
1051
+ for (let i = 0; i < args.length; i++) {
1052
+ if (args[i] === '--limit' && args[i + 1]) {
1053
+ limit = parseInt(args[++i]);
1054
+ }
1055
+ }
1056
+
1057
+ const response = await makePostRequest(KB_API_URL, {
1058
+ token: kbToken,
1059
+ action: 'getElasticFunctionLogs',
1060
+ functionName,
1061
+ limit
1062
+ });
1063
+
1064
+ if (response.error) {
1065
+ return console.red('Error:', response.error);
1066
+ }
1067
+
1068
+ if (!response.events || response.events.length === 0) {
1069
+ console.log('No logs found. Function may not have been invoked yet.');
1070
+ return;
1071
+ }
1072
+
1073
+ console.log(`Logs for '${functionName}':\n`);
1074
+ response.events.forEach(event => {
1075
+ const time = new Date(event.timestamp).toISOString();
1076
+ console.log(`[${time}] ${event.message}`);
1077
+ });
1078
+ } catch (error) {
1079
+ console.red('Error fetching logs:', error.message);
1080
+ }
1081
+ }
1082
+
1083
+ async function fnEnvAction(kbToken, functionName, args) {
1084
+ if (!functionName) {
1085
+ return console.red('Function name required. Usage: openkbs fn env <name> [KEY=value ...]');
1086
+ }
1087
+
1088
+ try {
1089
+ if (args.length === 0) {
1090
+ // Show current env vars
1091
+ const response = await makePostRequest(KB_API_URL, {
1092
+ token: kbToken,
1093
+ action: 'getElasticFunction',
1094
+ functionName
1095
+ });
1096
+
1097
+ if (response.error) {
1098
+ return console.red('Error:', response.error);
1099
+ }
1100
+
1101
+ console.log(`Environment variables for '${functionName}':\n`);
1102
+ const env = response.env || {};
1103
+ if (Object.keys(env).length === 0) {
1104
+ console.log(' (none)');
1105
+ } else {
1106
+ Object.entries(env).forEach(([key, value]) => {
1107
+ console.log(` ${key}=${value}`);
1108
+ });
1109
+ }
1110
+ } else {
1111
+ // Set env vars
1112
+ const env = {};
1113
+ args.forEach(arg => {
1114
+ const [key, ...valueParts] = arg.split('=');
1115
+ if (key && valueParts.length > 0) {
1116
+ env[key] = valueParts.join('=');
1117
+ }
1118
+ });
1119
+
1120
+ if (Object.keys(env).length === 0) {
1121
+ return console.red('Invalid format. Use: openkbs fn env <name> KEY=value');
1122
+ }
1123
+
1124
+ console.log(`Setting environment variables for '${functionName}'...`);
1125
+
1126
+ const response = await makePostRequest(KB_API_URL, {
1127
+ token: kbToken,
1128
+ action: 'setElasticFunctionEnv',
1129
+ functionName,
1130
+ env
1131
+ });
1132
+
1133
+ if (response.error) {
1134
+ return console.red('Error:', response.error);
1135
+ }
1136
+
1137
+ console.green('Environment variables updated.');
1138
+ }
1139
+ } catch (error) {
1140
+ console.red('Error:', error.message);
1141
+ }
1142
+ }
1143
+
1144
+ async function fnInvokeAction(kbToken, functionName, args) {
1145
+ if (!functionName) {
1146
+ return console.red('Function name required. Usage: openkbs fn invoke <name> [payload]');
1147
+ }
1148
+
1149
+ try {
1150
+ let payload = {};
1151
+ if (args.length > 0) {
1152
+ try {
1153
+ payload = JSON.parse(args.join(' '));
1154
+ } catch (e) {
1155
+ return console.red('Invalid JSON payload');
1156
+ }
1157
+ }
1158
+
1159
+ console.log(`Invoking '${functionName}'...`);
1160
+
1161
+ const response = await makePostRequest(KB_API_URL, {
1162
+ token: kbToken,
1163
+ action: 'invokeElasticFunction',
1164
+ functionName,
1165
+ payload
1166
+ });
1167
+
1168
+ if (response.error) {
1169
+ return console.red('Error:', response.error);
1170
+ }
1171
+
1172
+ console.log('\nResponse:');
1173
+ console.log(JSON.stringify(response.payload, null, 2));
1174
+
1175
+ if (response.functionError) {
1176
+ console.red('\nFunction Error:', response.functionError);
1177
+ }
1178
+ } catch (error) {
1179
+ console.red('Error invoking function:', error.message);
1180
+ }
1181
+ }
1182
+
840
1183
  module.exports = {
841
1184
  signAction,
842
1185
  loginAction,
@@ -857,5 +1200,6 @@ module.exports = {
857
1200
  updateKnowledgeAction,
858
1201
  updateCliAction,
859
1202
  publishAction,
860
- unpublishAction
1203
+ unpublishAction,
1204
+ fnAction
861
1205
  };
package/src/index.js CHANGED
@@ -13,7 +13,8 @@ const {
13
13
  deleteFileAction,
14
14
  describeAction, deployAction, createByTemplateAction, initByTemplateAction,
15
15
  logoutAction, installFrontendPackageAction, modifyAction, downloadModifyAction,
16
- updateKnowledgeAction, updateCliAction, publishAction, unpublishAction
16
+ updateKnowledgeAction, updateCliAction, publishAction, unpublishAction,
17
+ fnAction
17
18
  } = require('./actions');
18
19
 
19
20
 
@@ -197,4 +198,19 @@ Examples:
197
198
  This will unpublish your KB from the domain example.com
198
199
  `);
199
200
 
201
+ program
202
+ .command('fn [subCommand] [args...]')
203
+ .description('Manage Elastic Functions (serverless Lambda functions)')
204
+ .action((subCommand, args) => fnAction(subCommand, args))
205
+ .addHelpText('after', `
206
+ Examples:
207
+ $ openkbs fn list List all functions
208
+ $ openkbs fn deploy hello --region us-east-2 Deploy function from ./functions/hello/
209
+ $ openkbs fn delete hello Delete a function
210
+ $ openkbs fn logs hello View function logs
211
+ $ openkbs fn env hello View environment variables
212
+ $ openkbs fn env hello API_KEY=secret Set environment variable
213
+ $ openkbs fn invoke hello '{"test": true}' Invoke a function
214
+ `);
215
+
200
216
  program.parse(process.argv);
@@ -38,24 +38,59 @@ Description: """
38
38
  Output this JSON format if you can't extract the required data
39
39
  """
40
40
 
41
- List of API commands you can use to accomplish the Task:
41
+ LIST OF AVAILABLE COMMANDS:
42
+ To execute a command, output it as text and wait for system response.
42
43
 
43
- /googleSearch("query")
44
+ <googleSearch>
45
+ {
46
+ "query": "search query"
47
+ }
48
+ </googleSearch>
44
49
  Description: """
45
50
  Get results from Google Search API.
46
51
  """
47
52
 
48
- /youtubeSearch("query")
53
+ <youtubeSearch>
54
+ {
55
+ "query": "search query"
56
+ }
57
+ </youtubeSearch>
49
58
  Description: """
50
- Get results from youtube Search API.
59
+ Get results from YouTube Search API.
51
60
  """
52
61
 
53
- /googleImageSearch("query")
62
+ <googleImageSearch>
63
+ {
64
+ "query": "search query"
65
+ }
66
+ </googleImageSearch>
54
67
  Description: """
55
- Get results from google Image Search
68
+ Get results from Google Image Search.
56
69
  """
57
70
 
58
- /webpageToText("URL")
71
+ <webpageToText>
72
+ {
73
+ "url": "https://example.com/page"
74
+ }
75
+ </webpageToText>
59
76
  Description: """
60
- Use this API to open/read a web pages like product pages.
61
- """
77
+ Use this API to open/read web pages like product pages.
78
+ """
79
+
80
+ <documentToText>
81
+ {
82
+ "url": "https://example.com/document.pdf"
83
+ }
84
+ </documentToText>
85
+ Description: """
86
+ Extract text from documents (PDF, DOC, etc.).
87
+ """
88
+
89
+ <imageToText>
90
+ {
91
+ "url": "https://example.com/image.png"
92
+ }
93
+ </imageToText>
94
+ Description: """
95
+ OCR - Extract text from images.
96
+ """
@@ -15,7 +15,7 @@ const extractJSONFromText = (text) => {
15
15
  return null;
16
16
  }
17
17
 
18
- export const getActions = () => [
18
+ export const getActions = (meta) => [
19
19
  // IMPORTANT: Actions returning JOB_COMPLETED or JOB_FAILED stop agent execution and return final result
20
20
  [/[\s\S]*"type"\s*:\s*"JOB_COMPLETED"[\s\S]*/, async (match, event) => {
21
21
  const parsedData = extractJSONFromText(match[0]);
@@ -31,7 +31,6 @@ export const getActions = () => [
31
31
  }
32
32
  }],
33
33
 
34
-
35
34
  [/[\s\S]*"type"\s*:\s*"JOB_FAILED"[\s\S]*/, async (match, event) => {
36
35
  const parsedData = extractJSONFromText(match[0]);
37
36
  if (parsedData && parsedData.type === "JOB_FAILED") {
@@ -45,24 +44,26 @@ export const getActions = () => [
45
44
  }
46
45
  }],
47
46
 
48
- [/\/?googleSearch\("(.*?)"\)/, async (match) => {
49
- const q = match[1];
47
+ // Google Search with XML+JSON format
48
+ [/<googleSearch>([\s\S]*?)<\/googleSearch>/s, async (match) => {
50
49
  try {
51
- const response = await openkbs.googleSearch(q, {});
52
- const data = response?.map(({ title, link, snippet, pagemap }) => ({
50
+ const data = JSON.parse(match[1].trim());
51
+ const response = await openkbs.googleSearch(data.query);
52
+ const results = response?.map(({ title, link, snippet, pagemap }) => ({
53
53
  title, link, snippet, image: pagemap?.metatags?.[0]?.["og:image"]
54
54
  }));
55
- return { data };
55
+ return { data: results, ...meta };
56
56
  } catch (e) {
57
- return { error: e.message };
57
+ return { error: e.message, ...meta };
58
58
  }
59
59
  }],
60
60
 
61
- [/\/?youtubeSearch\("(.*?)"\)/, async (match) => {
62
- const q = match[1];
61
+ // YouTube Search with XML+JSON format
62
+ [/<youtubeSearch>([\s\S]*?)<\/youtubeSearch>/s, async (match) => {
63
63
  try {
64
- const response = await openkbs.googleSearch(q + ' site:youtube.com', { videoOnly: true });
65
- const data = response?.map(({ title, link, snippet, pagemap }) => ({
64
+ const data = JSON.parse(match[1].trim());
65
+ const response = await openkbs.googleSearch(data.query + ' site:youtube.com', { videoOnly: true });
66
+ const results = response?.map(({ title, link, snippet, pagemap }) => ({
66
67
  title,
67
68
  link: link.replace('www.youtube.com/watch?v=', 'youtu.be/'),
68
69
  snippet,
@@ -70,62 +71,62 @@ export const getActions = () => [
70
71
  duration: pagemap?.videoobject?.[0]?.duration,
71
72
  channel: pagemap?.metatags?.[0]?.["og:site_name"],
72
73
  })).filter(item => item.link.includes('youtu'));
73
- return { data };
74
+ return { data: results, ...meta };
74
75
  } catch (e) {
75
- return { error: e.message };
76
+ return { error: e.message, ...meta };
76
77
  }
77
78
  }],
78
79
 
79
- [/\/?googleImageSearch\("(.*?)"\)/, async (match) => {
80
- const q = match[1];
80
+ // Google Image Search with XML+JSON format
81
+ [/<googleImageSearch>([\s\S]*?)<\/googleImageSearch>/s, async (match) => {
81
82
  try {
82
- const response = await openkbs.googleSearch(q, { searchType: 'image' });
83
- const data = response?.map(({ title, link, snippet, pagemap }) => {
83
+ const data = JSON.parse(match[1].trim());
84
+ const response = await openkbs.googleSearch(data.query, { searchType: 'image' });
85
+ const results = response?.map(({ title, link, snippet, pagemap }) => {
84
86
  const imageObj = pagemap?.cse_image?.[0];
85
87
  const thumbnail = imageObj?.src || pagemap?.metatags?.[0]?.["og:image"] || link;
86
- return {
87
- title,
88
- link: link,
89
- snippet,
90
- image: thumbnail
91
- };
88
+ return { title, link, snippet, image: thumbnail };
92
89
  });
93
- return { data };
90
+ return { data: results, ...meta };
94
91
  } catch (e) {
95
- return { error: e.message };
92
+ return { error: e.message, ...meta };
96
93
  }
97
94
  }],
98
95
 
99
- [/\/?webpageToText\("(.*)"\)/, async (match) => {
96
+ // Webpage to Text with XML+JSON format
97
+ [/<webpageToText>([\s\S]*?)<\/webpageToText>/s, async (match) => {
100
98
  try {
101
- let response = await openkbs.webpageToText(match[1]);
102
- if(!response?.url) return { data: { error: "Unable to read website" } };
103
- return { data: response };
99
+ const data = JSON.parse(match[1].trim());
100
+ let response = await openkbs.webpageToText(data.url);
101
+ if (!response?.url) return { data: { error: "Unable to read website" }, ...meta };
102
+ return { data: response, ...meta };
104
103
  } catch (e) {
105
- return { error: e.response?.data || e };
104
+ return { error: e.response?.data || e.message, ...meta };
106
105
  }
107
106
  }],
108
107
 
109
- [/\/?documentToText\("(.*)"\)/, async (match) => {
108
+ // Document to Text with XML+JSON format
109
+ [/<documentToText>([\s\S]*?)<\/documentToText>/s, async (match) => {
110
110
  try {
111
- let response = await openkbs.documentToText(match[1]);
112
- return { data: response };
111
+ const data = JSON.parse(match[1].trim());
112
+ let response = await openkbs.documentToText(data.url);
113
+ return { data: response, ...meta };
113
114
  } catch (e) {
114
- return { error: e.response.data };
115
+ return { error: e.response?.data || e.message, ...meta };
115
116
  }
116
117
  }],
117
118
 
118
- [/\/?imageToText\("(.*)"\)/, async (match) => {
119
+ // Image to Text (OCR) with XML+JSON format
120
+ [/<imageToText>([\s\S]*?)<\/imageToText>/s, async (match) => {
119
121
  try {
120
- let response = await openkbs.imageToText(match[1]);
121
-
122
+ const data = JSON.parse(match[1].trim());
123
+ let response = await openkbs.imageToText(data.url);
122
124
  if (response?.detections?.[0]?.txt) {
123
125
  response = { detections: response?.detections?.[0]?.txt };
124
126
  }
125
-
126
- return { data: response };
127
+ return { data: response, ...meta };
127
128
  } catch (e) {
128
- return { error: e.response.data };
129
+ return { error: e.response?.data || e.message, ...meta };
129
130
  }
130
131
  }],
131
- ];
132
+ ];
@@ -2,7 +2,14 @@ import {getActions} from './actions.js';
2
2
 
3
3
  export const backendHandler = async (event) => {
4
4
  const lastMessage = event.payload.messages[event.payload.messages.length - 1];
5
- const actions = getActions();
5
+ const reachedMessageLimit = event?.payload?.messages?.length > 60;
6
+
7
+ // Meta for continuing chat model requests
8
+ const meta = {
9
+ _meta_actions: reachedMessageLimit ? [] : ["REQUEST_CHAT_MODEL"]
10
+ };
11
+
12
+ const actions = getActions(meta);
6
13
 
7
14
  const matchingActions = actions.reduce((acc, [regex, action]) => {
8
15
  const matches = [...lastMessage.content.matchAll(new RegExp(regex, 'g'))];
@@ -12,31 +19,30 @@ export const backendHandler = async (event) => {
12
19
  return acc;
13
20
  }, []);
14
21
 
15
- const reachedMessageLimit = event?.payload?.messages?.length > 60;
16
-
17
22
  if (matchingActions.length > 0) {
18
23
  try {
19
24
  const results = await Promise.all(matchingActions);
20
25
 
21
26
  // IMPORTANT: Actions returning JOB_COMPLETED or JOB_FAILED stop agent execution and return final result
22
- const isOnlyJobCompletion = results.length === 1 &&
27
+ const isOnlyJobCompletion = results.length === 1 &&
23
28
  (results[0]?.type === 'JOB_COMPLETED' || results[0]?.type === 'JOB_FAILED');
24
-
25
- const meta = {
29
+
30
+ // Override meta for job completion
31
+ const finalMeta = {
26
32
  _meta_actions: (reachedMessageLimit || isOnlyJobCompletion) ? [] : ["REQUEST_CHAT_MODEL"]
27
33
  };
28
34
 
29
35
  if (results?.[0]?.data?.some?.(o => o?.type === 'image_url')) {
30
36
  return {
31
37
  ...results[0],
32
- ...meta
38
+ ...finalMeta
33
39
  };
34
40
  }
35
41
 
36
42
  return {
37
43
  type: 'RESPONSE',
38
44
  data: results,
39
- ...meta
45
+ ...finalMeta
40
46
  };
41
47
  } catch (error) {
42
48
  return {