@saltcorn/agents 0.7.7 → 0.7.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/action.js CHANGED
@@ -92,6 +92,7 @@ module.exports = {
92
92
  is_sub_agent,
93
93
  agent_view_config,
94
94
  dyn_updates,
95
+ agent_label,
95
96
  ...rest
96
97
  }) => {
97
98
  const userinput = interpolate(configuration.prompt, row, user);
@@ -116,7 +117,7 @@ module.exports = {
116
117
  run,
117
118
  configuration,
118
119
  req,
119
- undefined,
120
+ agent_label || undefined,
120
121
  [],
121
122
  row,
122
123
  agent_view_config || { stream: false },
package/common.js CHANGED
@@ -40,6 +40,7 @@ const get_skills = () => {
40
40
  require("./skills/Fetch"),
41
41
  require("./skills/WebSearch"),
42
42
  require("./skills/Subagent"),
43
+ require("./skills/ExternalSkill"),
43
44
  //require("./skills/AdaptiveFeedback"),
44
45
  ...exchange_skills,
45
46
  ];
@@ -234,7 +235,7 @@ const process_interaction = async (
234
235
  run,
235
236
  config,
236
237
  req,
237
- agent_label = "Copilot",
238
+ agent_label = "Agent",
238
239
  prevResponses = [],
239
240
  triggering_row = {},
240
241
  agentsViewCfg = { stream: false },
@@ -245,16 +246,16 @@ const process_interaction = async (
245
246
  const sysState = getState();
246
247
  const complArgs = await getCompletionArguments(
247
248
  config,
248
- req.user,
249
+ req?.user,
249
250
  triggering_row,
250
- req.body,
251
+ req?.body,
251
252
  );
252
253
  complArgs.appendToChat = true;
253
254
  complArgs.chat = run.context.interactions;
254
255
  const use_alt_config = complArgs.alt_config;
255
256
  //complArgs.debugResult = true;
256
257
  //console.log("complArgs", JSON.stringify(complArgs, null, 2));
257
- const debugMode = is_debug_mode(config, req.user);
258
+ const debugMode = is_debug_mode(config, req?.user);
258
259
  const debugCollector = {};
259
260
  if (debugMode) complArgs.debugCollector = debugCollector;
260
261
  if (stream && viewname) {
@@ -265,10 +266,11 @@ const process_interaction = async (
265
266
  ? response
266
267
  : response.choices[0].content || response.choices[0].delta?.content;
267
268
  if (content) {
268
- const pageLoadTag = req.body.page_load_tag;
269
- view.emitRealTimeEvent(`STREAM_CHUNK?page_load_tag=${pageLoadTag}`, {
270
- content,
271
- });
269
+ const pageLoadTag = req?.body?.page_load_tag;
270
+ if (pageLoadTag)
271
+ view.emitRealTimeEvent(`STREAM_CHUNK?page_load_tag=${pageLoadTag}`, {
272
+ content,
273
+ });
272
274
  }
273
275
  };
274
276
  }
@@ -303,7 +305,7 @@ const process_interaction = async (
303
305
  eval_js: `processCopilotResponse({response: ${JSON.stringify(resp)}, run_id: ${run.id}}, true)`,
304
306
  page_load_tag: req?.headers?.["page-load-tag"],
305
307
  },
306
- [req.user.id],
308
+ [req?.user?.id],
307
309
  );
308
310
  else responses.push(resp);
309
311
  await addToContext(run, {
@@ -342,7 +344,7 @@ const process_interaction = async (
342
344
  }
343
345
  if (answer.content && !answer.tool_calls)
344
346
  add_response(
345
- req.disable_markdown_render
347
+ req?.disable_markdown_render
346
348
  ? answer
347
349
  : wrapSegment(md.render(answer.content), agent_label, false, layout),
348
350
  );
@@ -355,7 +357,7 @@ const process_interaction = async (
355
357
  ) {
356
358
  if (answer.content)
357
359
  add_response(
358
- req.disable_markdown_render
360
+ req?.disable_markdown_render
359
361
  ? answer
360
362
  : wrapSegment(md.render(answer.content), agent_label, false, layout),
361
363
  );
@@ -383,14 +385,18 @@ const process_interaction = async (
383
385
  (tool.skill.skill_label || tool.skill.constructor.skill_name) +
384
386
  " ";
385
387
  const view = View.findOne({ name: viewname });
386
- const pageLoadTag = req.body.page_load_tag;
387
- view.emitRealTimeEvent(
388
- `STREAM_CHUNK?page_load_tag=${pageLoadTag}`,
389
- {
390
- content,
391
- },
392
- );
388
+ const pageLoadTag = req?.body?.page_load_tag;
389
+ if (pageLoadTag)
390
+ view.emitRealTimeEvent(
391
+ `STREAM_CHUNK?page_load_tag=${pageLoadTag}`,
392
+ {
393
+ content,
394
+ },
395
+ );
393
396
  }
397
+ const response_label = is_sub_agent
398
+ ? agent_label
399
+ : tool.skill.skill_label || tool.skill.constructor.skill_name;
394
400
  if (tool.tool.renderToolCall) {
395
401
  const row = tool_call.input;
396
402
 
@@ -400,10 +406,7 @@ const process_interaction = async (
400
406
  if (rendered)
401
407
  add_response(
402
408
  wrapSegment(
403
- wrapCard(
404
- tool.skill.skill_label || tool.skill.constructor.skill_name,
405
- rendered,
406
- ),
409
+ wrapCard(response_label, rendered),
407
410
  agent_label,
408
411
  false,
409
412
  layout,
@@ -411,11 +414,11 @@ const process_interaction = async (
411
414
  );
412
415
  }
413
416
  myHasResult = true;
414
- const result = await tool.tool.process(tool_call.input, {
417
+ let result = await tool.tool.process(tool_call.input, {
415
418
  req,
416
- });
419
+ });
417
420
  toolResults[tool_call.tool_call_id] = result;
418
- if (result.stop) stop = true;
421
+ if (result?.stop) stop = true;
419
422
  if (
420
423
  (typeof result === "object" && Object.keys(result || {}).length) ||
421
424
  typeof result === "string"
@@ -427,11 +430,7 @@ const process_interaction = async (
427
430
  if (rendered)
428
431
  add_response(
429
432
  wrapSegment(
430
- wrapCard(
431
- tool.skill.skill_label ||
432
- tool.skill.constructor.skill_name,
433
- rendered,
434
- ),
433
+ wrapCard(response_label, rendered),
435
434
  agent_label,
436
435
  false,
437
436
  layout,
@@ -474,14 +473,16 @@ const process_interaction = async (
474
473
  myHasResult = false;
475
474
  if (tool.tool.postProcess && !stop) {
476
475
  let result = toolResults[tool_call.tool_call_id];
477
-
476
+ const response_label = is_sub_agent
477
+ ? agent_label
478
+ : tool.skill.skill_label || tool.skill.constructor.skill_name;
478
479
  const chat = run.context.interactions;
479
480
  let generateUsed = false;
480
481
  const systemPrompt = await getSystemPrompt(
481
482
  config,
482
- req.user,
483
+ req?.user,
483
484
  triggering_row,
484
- req.body,
485
+ req?.body,
485
486
  );
486
487
  const postprocres = await tool.tool.postProcess({
487
488
  tool_call,
@@ -504,13 +505,14 @@ const process_interaction = async (
504
505
  emit_update(s) {
505
506
  if (!stream || !viewname) return;
506
507
  const view = View.findOne({ name: viewname });
507
- const pageLoadTag = req.body.page_load_tag;
508
- view.emitRealTimeEvent(
509
- `STREAM_CHUNK?page_load_tag=${pageLoadTag}`,
510
- {
511
- content: s + " ",
512
- },
513
- );
508
+ const pageLoadTag = req?.body?.page_load_tag;
509
+ if (pageLoadTag)
510
+ view.emitRealTimeEvent(
511
+ `STREAM_CHUNK?page_load_tag=${pageLoadTag}`,
512
+ {
513
+ content: s + " ",
514
+ },
515
+ );
514
516
  },
515
517
  });
516
518
  if (generateUsed)
@@ -532,48 +534,75 @@ const process_interaction = async (
532
534
  }
533
535
 
534
536
  for (const add_resp of postprocres.add_responses || []) {
535
- raw_responses.push(add_resp);
536
- const renderedAddResponse =
537
- typeof add_resp === "string" ? md.render(add_resp) : add_resp;
538
- add_response(
539
- wrapSegment(
540
- wrapCard(
541
- tool.skill.skill_label || tool.skill.constructor.skill_name,
542
- renderedAddResponse,
537
+ const content =
538
+ add_resp.role && add_resp.content ? add_resp.content : add_resp;
539
+ raw_responses.push(content);
540
+ if (add_resp.md_response !== null) {
541
+ const renderedAddResponse = add_resp.md_response
542
+ ? md.render(add_resp.md_response)
543
+ : typeof content === "string"
544
+ ? md.render(content)
545
+ : content;
546
+ add_response(
547
+ wrapSegment(
548
+ wrapCard(response_label, renderedAddResponse),
549
+ agent_label,
550
+ false,
551
+ layout,
543
552
  ),
544
- agent_label,
545
- false,
546
- layout,
547
- ),
548
- );
553
+ );
554
+ }
555
+ if (typeof add_resp.md_response !== "undefined")
556
+ delete add_resp.md_response;
549
557
 
550
- const result = add_resp;
551
- await sysState.functions.llm_add_message.run(
552
- "assistant",
558
+ const result = content;
553
559
 
554
- !result || typeof result === "string"
555
- ? result || "Action run"
556
- : JSON.stringify(result),
560
+ if (add_resp.role && add_resp.content) {
561
+ await sysState.functions.llm_add_message.run(
562
+ add_resp.role,
563
+ add_resp.content,
564
+ {
565
+ chat: run.context.interactions,
566
+ },
567
+ );
568
+ } else
569
+ await sysState.functions.llm_add_message.run(
570
+ "assistant",
571
+
572
+ !result || typeof result === "string"
573
+ ? result || "Action run"
574
+ : JSON.stringify(result),
575
+
576
+ {
577
+ chat: run.context.interactions,
578
+ },
579
+ );
557
580
 
558
- {
559
- chat: run.context.interactions,
560
- },
561
- );
562
581
  await addToContext(run, {
563
582
  interactions: run.context.interactions,
564
583
  });
565
584
  }
566
585
  if (!postprocres.stop) {
567
- await sysState.functions.llm_add_message.run(
568
- "user",
569
- postprocres.follow_up_prompt || "Continue with the query",
570
- {
571
- chat: run.context.interactions,
572
- },
573
- );
574
- await addToContext(run, {
575
- interactions: run.context.interactions,
576
- });
586
+ const lastInteract =
587
+ run.context.interactions[run.context.interactions.length - 1];
588
+
589
+ if (
590
+ postprocres.follow_up_prompt ||
591
+ !(
592
+ lastInteract?.role === "user" || lastInteract?.role === "tool"
593
+ )
594
+ ) {
595
+ await sysState.functions.llm_add_message.run(
596
+ "user",
597
+ postprocres.follow_up_prompt || "Continue with the query",
598
+ {
599
+ chat: run.context.interactions,
600
+ },
601
+ );
602
+ await addToContext(run, {
603
+ interactions: run.context.interactions,
604
+ });
605
+ }
577
606
  myHasResult = true;
578
607
  }
579
608
  if (postprocres.add_user_action && viewname) {
@@ -620,18 +649,18 @@ const process_interaction = async (
620
649
  );
621
650
  } else if (typeof answer === "string")
622
651
  add_response(
623
- req.disable_markdown_render
652
+ req?.disable_markdown_render
624
653
  ? answer
625
654
  : wrapSegment(md.render(answer), agent_label, false, layout),
626
655
  );
627
- if (dyn_updates)
656
+ if (dyn_updates && !is_sub_agent)
628
657
  getState().emitDynamicUpdate(
629
658
  db.getTenantSchema(),
630
659
  {
631
660
  eval_js: `final_agent_response()`,
632
661
  page_load_tag: req?.headers?.["page-load-tag"],
633
662
  },
634
- [req.user.id],
663
+ [req?.user?.id],
635
664
  );
636
665
 
637
666
  return {
package/index.js CHANGED
@@ -25,13 +25,13 @@ module.exports = {
25
25
  script: `/plugins/public/agents@${
26
26
  require("./package.json").version
27
27
  }/markdown-it.min.js`,
28
- onlyViews: ["Agent Chat"],
28
+ onlyViews: ["Agent Chat", "Saltcorn Agent copilot"],
29
29
  },
30
30
  {
31
31
  script: `/plugins/public/agents@${
32
32
  require("./package.json").version
33
33
  }/jquery.autogrow-textarea.js`,
34
- onlyViews: ["Agent Chat"],
34
+ onlyViews: ["Agent Chat", "Saltcorn Agent copilot"],
35
35
  },
36
36
  ],
37
37
  actions: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/agents",
3
- "version": "0.7.7",
3
+ "version": "0.7.9",
4
4
  "description": "AI agents for Saltcorn",
5
5
  "main": "index.js",
6
6
  "dependencies": {
@@ -0,0 +1,38 @@
1
+ const path = require("path");
2
+ const fs = require("fs").promises;
3
+
4
+ class ExternalSkill {
5
+ static skill_name = "External Skill (SKILL.md)";
6
+
7
+ get skill_label() {
8
+ return "External Skill";
9
+ }
10
+
11
+ constructor(cfg) {
12
+ Object.assign(this, cfg);
13
+ }
14
+
15
+ static async configFields() {
16
+ return [
17
+ {
18
+ name: "skills_dir",
19
+ label: "Skill directory",
20
+ type: "String",
21
+ required: true,
22
+ sublabel: "Path to the directory containing SKILL.md",
23
+ },
24
+ ];
25
+ }
26
+
27
+ async systemPrompt() {
28
+ if (!this.skills_dir) return;
29
+ const skillMdPath = path.join(this.skills_dir, "SKILL.md");
30
+ try {
31
+ return await fs.readFile(skillMdPath, "utf8");
32
+ } catch (e) {
33
+ return `[ExternalSkill: could not read ${skillMdPath}: ${e.message}]`;
34
+ }
35
+ }
36
+ }
37
+
38
+ module.exports = ExternalSkill;
@@ -215,13 +215,32 @@ Now generate the JavaScript code required by the user.`,
215
215
  if (res !== undefined && res !== null && res !== "") return res;
216
216
  return "Code executed successfully but returned no output.";
217
217
  };
218
+ const mkMdResponse = (result, code) =>
219
+ req?.user?.role_id === 1
220
+ ? `<details>
221
+
222
+ <summary>Show code</summary>
223
+
224
+ \`\`\`javascript
225
+ ${code}
226
+ \`\`\`
227
+
228
+
229
+ </details>
230
+
231
+ ${result}`
232
+ : result;
218
233
  try {
219
234
  const res = await this.runCode(js_code, { user: req.user });
220
235
  getState().log(6, "Code answer: " + JSON.stringify(res));
221
236
  const effectiveRes = ensureResult(res);
222
237
  return {
223
238
  stop: typeof res === "string" && !this.follow_up_prompt,
224
- add_response: effectiveRes,
239
+ add_response: {
240
+ role: "user",
241
+ content: `The result of running the code is: ${effectiveRes}`,
242
+ md_response: mkMdResponse(effectiveRes, js_code),
243
+ },
225
244
  ...(this.follow_up_prompt
226
245
  ? { follow_up_prompt: this.follow_up_prompt }
227
246
  : {}),
@@ -240,7 +259,7 @@ this code produced the following error:
240
259
  ${err.message}
241
260
  \`\`\`
242
261
 
243
- Correct this error.
262
+ Correct this error and generate the new Javascript code to run
244
263
  `);
245
264
  try {
246
265
  const res = await this.runCode(retry_js_code, {
@@ -250,7 +269,11 @@ Correct this error.
250
269
  const effectiveRes = ensureResult(res);
251
270
  return {
252
271
  stop: typeof res === "string" && !this.follow_up_prompt,
253
- add_response: effectiveRes,
272
+ add_response: {
273
+ role: "user",
274
+ content: `The result of running the code is: ${effectiveRes}`,
275
+ md_response: mkMdResponse(effectiveRes, retry_js_code),
276
+ },
254
277
  ...(this.follow_up_prompt
255
278
  ? { follow_up_prompt: this.follow_up_prompt }
256
279
  : {}),
@@ -284,10 +307,11 @@ const getTablePrompt = (read_only) => {
284
307
  tables.forEach((table) => {
285
308
  const fieldLines = table.fields.map(
286
309
  (f) =>
287
- ` * ${f.name} with type: ${f.pretty_type.replace(
288
- "Key to",
289
- "ForeignKey referencing",
290
- )}.${f.description ? ` ${f.description}` : ""}`,
310
+ ` * ${f.name} with type: ${
311
+ f?.pretty_type
312
+ ? f.pretty_type.replace("Key to", "ForeignKey referencing")
313
+ : "Unknown"
314
+ }.${f?.description ? ` ${f.description}` : ""}`,
291
315
  );
292
316
  tableLines.push(
293
317
  `${table.name}${
@@ -102,6 +102,7 @@ class SubagentToSkill {
102
102
  agent_view_config,
103
103
  dyn_updates,
104
104
  req,
105
+ agent_label: this.agent_name,
105
106
  });
106
107
  getState().log(6, "Subagent response", JSON.stringify(subres, null, 2));
107
108
  //if (subres.json.raw_responses)