@saltcorn/agents 0.6.3 → 0.6.5

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/agent-view.js CHANGED
@@ -31,6 +31,7 @@ const {
31
31
  label,
32
32
  a,
33
33
  br,
34
+ img,
34
35
  } = require("@saltcorn/markup/tags");
35
36
  const { getState } = require("@saltcorn/data/db/state");
36
37
  const {
@@ -45,9 +46,12 @@ const {
45
46
  is_debug_mode,
46
47
  get_initial_interactions,
47
48
  get_skill_instances,
49
+ saveInteractions,
48
50
  } = require("./common");
49
51
  const MarkdownIt = require("markdown-it"),
50
52
  md = new MarkdownIt();
53
+ const { isWeb } = require("@saltcorn/data/utils");
54
+ const path = require("path");
51
55
 
52
56
  const configuration_workflow = (req) =>
53
57
  new Workflow({
@@ -223,6 +227,7 @@ const run = async (
223
227
  viewname,
224
228
  {
225
229
  action_id,
230
+ agent_action,
226
231
  show_prev_runs,
227
232
  prev_runs_closed,
228
233
  placeholder,
@@ -235,7 +240,7 @@ const run = async (
235
240
  state,
236
241
  { res, req },
237
242
  ) => {
238
- const action = await Trigger.findOne({ id: action_id });
243
+ const action = agent_action || await Trigger.findOne({ id: action_id });
239
244
  if (!action) throw new Error(`Action not found: ${action_id}`);
240
245
  const prevRuns = show_prev_runs
241
246
  ? (
@@ -271,6 +276,7 @@ const run = async (
271
276
  interactMarkups.push(...run.context.html_interactions);
272
277
  } else
273
278
  for (const interact of run.context.interactions) {
279
+ //legacy
274
280
  switch (interact.role) {
275
281
  case "user":
276
282
  if (interact.content?.[0]?.type === "image_url") {
@@ -485,7 +491,7 @@ const run = async (
485
491
  ? div(
486
492
  div(
487
493
  {
488
- class: "d-flex justify-content-between align-middle mb-2",
494
+ class: "d-flex flex-wrap justify-content-between align-middle mb-2",
489
495
  },
490
496
  div(
491
497
  { class: "d-flex" },
@@ -498,7 +504,7 @@ const run = async (
498
504
  button(
499
505
  {
500
506
  type: "button",
501
- class: "btn btn-secondary btn-sm py-0",
507
+ class: "btn btn-secondary btn-sm pt-0 pb-1",
502
508
  style: "font-size: 0.9em;height:1.5em",
503
509
  onclick: "unset_state_field('run_id')",
504
510
  title: "New session",
@@ -514,7 +520,10 @@ const run = async (
514
520
  },
515
521
  div(
516
522
  { class: "d-flex justify-content-between" },
517
- localeDateTime(run.started_at),
523
+ span(
524
+ { class: "text-truncate", style: "min-width:0" },
525
+ localeDateTime(run.started_at),
526
+ ),
518
527
  i({
519
528
  class: "far fa-trash-alt",
520
529
  onclick: `delprevrun(event, ${run.id})`,
@@ -550,7 +559,15 @@ const run = async (
550
559
  `div.interaction-segment:not(:first-child) {border-top: 1px solid #e7e7e7; }
551
560
  div.interaction-segment {padding-top: 5px;padding-bottom: 5px;}
552
561
  div.interaction-segment p {margin-bottom: 0px;}
553
- div.interaction-segment div.card {margin-top: 0.5rem;}
562
+ div.interaction-segment div.card {margin-top: 0.5rem;}
563
+ div.interaction-segment.to-right {
564
+ display: flex;
565
+ flex-direction: row-reverse;
566
+ }
567
+ div.interaction-segment.to-right div.badgewrap {
568
+ display: flex;
569
+ flex-direction: row-reverse;
570
+ }
554
571
  div.prevcopilotrun:hover {cursor: pointer; background-color: var(--tblr-secondary-bg-subtle, var(--bs-secondary-bg-subtle, gray));}
555
572
  div.prevcopilotrun i.fa-trash-alt {display: none;}
556
573
  div.prevcopilotrun:hover i.fa-trash-alt {display: block;}
@@ -625,7 +642,7 @@ const run = async (
625
642
  function get_run_id(elem) {
626
643
  return $("input[name=run_id").val()
627
644
  }
628
- function processCopilotResponse(res) {
645
+ function processCopilotResponse(res, not_final) {
629
646
  console.log("processCopilotResponse", res)
630
647
  const fileInput = $("input#attach_agent_image")[0];
631
648
  let fileBadge = "";
@@ -637,13 +654,13 @@ const run = async (
637
654
  $("span.filename-label").text("").removeClass("me-2");
638
655
  _agentDT.items.clear();
639
656
  $("input#attach_agent_image").val(null);
640
- $("#sendbuttonicon").attr("class","far fa-paper-plane");
657
+ if(!not_final || (!${JSON.stringify(dyn_updates)})) $("#sendbuttonicon").attr("class","far fa-paper-plane");
641
658
  const $runidin= $("input[name=run_id")
642
659
  if(res.run_id && (!$runidin.val() || $runidin.val()=="undefined"))
643
660
  $runidin.val(res.run_id);
644
661
  const wrapSegment = (html, who) => '<div class="interaction-segment"><span class="badge bg-secondary">'+who+'</span>'+html+'</div>'
645
662
  const user_input = $("textarea[name=userinput]").val()
646
- if(user_input)
663
+ if(user_input && (!${JSON.stringify(dyn_updates)}))
647
664
  $("#copilotinteractions").append(wrapSegment('<p>'+user_input+'</p>'+fileBadge, "You"))
648
665
  $("textarea[name=userinput]").val("")
649
666
  $('form.agent-view div.next_response_scratch').html("")
@@ -652,6 +669,9 @@ const run = async (
652
669
  $("#copilotinteractions").append(res.response)
653
670
  }
654
671
  window.processCopilotResponse = processCopilotResponse;
672
+ window.final_agent_response = () => {
673
+ $("#sendbuttonicon").attr("class","far fa-paper-plane");
674
+ }
655
675
  const _agentDT = new DataTransfer();
656
676
  function setAgentFiles(files) {
657
677
  for (const f of files) _agentDT.items.add(f);
@@ -666,7 +686,11 @@ const run = async (
666
686
  } else {
667
687
  $label.addClass("me-2");
668
688
  const text = n === 1 ? _agentDT.files[0].name : n + " files";
669
- $label.html(text + ' <span class="badge text-bg-secondary" style="cursor:pointer;font-size:.65em;vertical-align:middle" onclick="clearAgentFiles()" title="Remove files">&times;</span>');
689
+ $label.html(${
690
+ isWeb(req)
691
+ ? `text + ' <span class="badge text-bg-secondary" style="cursor:pointer;font-size:.65em;vertical-align:middle" onclick="clearAgentFiles()" title="Remove files">&times;</span>'`
692
+ : `'<span style="max-width:8em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;vertical-align:middle">' + text + '</span> <span class="badge text-bg-secondary" style="cursor:pointer;font-size:.65em;vertical-align:middle" onclick="clearAgentFiles()" title="Remove files">&times;</span>'`
693
+ });
670
694
  }
671
695
  }
672
696
  function clearAgentFiles() {
@@ -796,7 +820,7 @@ const run = async (
796
820
 
797
821
  const interact = async (table_id, viewname, config, body, { req, res }) => {
798
822
  const { userinput, run_id, triggering_row_id } = body;
799
- const action = await Trigger.findOne({ id: config.action_id });
823
+ const action = config.agent_action || await Trigger.findOne({ id: config.action_id });
800
824
 
801
825
  let run;
802
826
  let triggering_row;
@@ -839,9 +863,13 @@ const interact = async (table_id, viewname, config, body, { req, res }) => {
839
863
  100,
840
864
  );
841
865
  badges.push(
842
- span(
843
- { class: "badge text-bg-info" },
844
- i({ class: "fas fa-image me-1" }),
866
+ div(
867
+ { class: "bg-secondary-subtle p-2 m-2 rounded-2" },
868
+ img({
869
+ src: `/files/resize/${50}/${50}/${file.path_to_serve}`,
870
+ class: "d-block",
871
+ onclick: `expand_thumbnail('${file.path_to_serve}', '${path.basename(file.path_to_serve)}')`,
872
+ }),
845
873
  file.filename,
846
874
  ),
847
875
  );
@@ -860,20 +888,30 @@ const interact = async (table_id, viewname, config, body, { req, res }) => {
860
888
  await getState().functions.llm_add_message.run("image", imageurl, {
861
889
  chat: run.context.interactions || [],
862
890
  });
863
- await addToContext(run, {
864
- interactions: run.context.interactions || [],
865
- });
866
891
  }
867
- fileBadges = badges.join("");
892
+ await saveInteractions(run);
893
+ fileBadges = div({ class: "d-flex" }, badges);
868
894
  }
895
+ const userInteractions = wrapSegment(p(userinput) + fileBadges, "You", true);
896
+
869
897
  await addToContext(run, {
870
898
  interactions: [
871
899
  ...(run.context.interactions || []),
872
900
  { role: "user", content: userinput },
873
901
  ],
874
- html_interactions: [wrapSegment(p(userinput) + fileBadges, "You")],
902
+ html_interactions: [userInteractions],
875
903
  });
876
904
  const dyn_updates = getState().getConfig("enable_dynamic_updates", true);
905
+ if (dyn_updates) {
906
+ getState().emitDynamicUpdate(
907
+ db.getTenantSchema(),
908
+ {
909
+ eval_js: `processCopilotResponse({response: ${JSON.stringify(userInteractions)}, run_id: ${run.id}}, true)`,
910
+ page_load_tag: req?.headers?.["page-load-tag"],
911
+ },
912
+ [req.user.id],
913
+ );
914
+ }
877
915
  const process_promise = process_interaction(
878
916
  run,
879
917
  action.configuration,
@@ -913,7 +951,7 @@ const delprevrun = async (table_id, viewname, config, body, { req, res }) => {
913
951
 
914
952
  const debug_info = async (table_id, viewname, config, body, { req, res }) => {
915
953
  const { run_id, triggering_row_id } = body;
916
- const action = await Trigger.findOne({ id: config.action_id });
954
+ const action = config.agent_action || await Trigger.findOne({ id: config.action_id });
917
955
  let triggering_row;
918
956
  if (table_id && triggering_row_id) {
919
957
  const table = Table.findOne(table_id);
@@ -955,7 +993,7 @@ const debug_info = async (table_id, viewname, config, body, { req, res }) => {
955
993
 
956
994
  const skillroute = async (table_id, viewname, config, body, { req, res }) => {
957
995
  const { run_id, triggering_row_id, skillid } = body;
958
- const action = await Trigger.findOne({ id: config.action_id });
996
+ const action = config.agent_action || await Trigger.findOne({ id: config.action_id });
959
997
  let triggering_row;
960
998
  if (table_id && triggering_row_id) {
961
999
  const table = Table.findOne(table_id);
@@ -992,7 +1030,7 @@ const execute_user_action = async (
992
1030
  ) => {
993
1031
  const { run_id, rndid, uaname } = body;
994
1032
 
995
- const action = await Trigger.findOne({ id: config.action_id });
1033
+ const action = config.agent_action || await Trigger.findOne({ id: config.action_id });
996
1034
  const run = await WorkflowRun.findOne({ id: +run_id });
997
1035
  //console.log("run uas",run.context.user_actions );
998
1036
 
@@ -1028,4 +1066,5 @@ module.exports = {
1028
1066
  table_optional: true,
1029
1067
  run,
1030
1068
  routes: { interact, delprevrun, debug_info, skillroute, execute_user_action },
1069
+ mobile_render_server_side: true,
1031
1070
  };
package/common.js CHANGED
@@ -166,14 +166,20 @@ const addToContext = async (run, newCtx) => {
166
166
  if (changed && run.update) await run.update({ context: run.context });
167
167
  };
168
168
 
169
- const wrapSegment = (html, who) =>
169
+ const saveInteractions = async (run) => {
170
+ await addToContext(run, {
171
+ interactions: run.context.interactions || [],
172
+ });
173
+ };
174
+
175
+ const wrapSegment = (html, who, to_right) =>
170
176
  who === null
171
177
  ? html
172
- : '<div class="interaction-segment"><span class="badge bg-secondary">' +
178
+ : `<div class="interaction-segment ${to_right ? "to-right" : ""}"><div><div class="badgewrap"><span class="badge bg-secondary">` +
173
179
  who +
174
- "</span>" +
180
+ "</span></div>" +
175
181
  html +
176
- "</div>";
182
+ "</div></div>";
177
183
 
178
184
  const wrapCard = (title, ...inners) =>
179
185
  span({ class: "badge bg-info ms-1" }, title) +
@@ -236,12 +242,13 @@ const process_interaction = async (
236
242
  interactions: complArgs.chat,
237
243
  });
238
244
  const responses = [];
239
- const add_response = async (resp) => {
245
+
246
+ const add_response = async (resp, not_final) => {
240
247
  if (dyn_updates)
241
248
  getState().emitDynamicUpdate(
242
249
  db.getTenantSchema(),
243
250
  {
244
- eval_js: `processCopilotResponse({response: ${JSON.stringify(resp)}, run_id: ${run.id}})`,
251
+ eval_js: `processCopilotResponse({response: ${JSON.stringify(resp)}, run_id: ${run.id}}, true)`,
245
252
  page_load_tag: req?.headers?.["page-load-tag"],
246
253
  },
247
254
  [req.user.id],
@@ -479,6 +486,15 @@ const process_interaction = async (
479
486
  ? answer
480
487
  : wrapSegment(md.render(answer), agent_label),
481
488
  );
489
+ if (dyn_updates)
490
+ getState().emitDynamicUpdate(
491
+ db.getTenantSchema(),
492
+ {
493
+ eval_js: `final_agent_response()`,
494
+ page_load_tag: req?.headers?.["page-load-tag"],
495
+ },
496
+ [req.user.id],
497
+ );
482
498
 
483
499
  return {
484
500
  json: {
@@ -497,6 +513,7 @@ module.exports = {
497
513
  get_skill_instances,
498
514
  getCompletionArguments,
499
515
  addToContext,
516
+ saveInteractions,
500
517
  wrapCard,
501
518
  wrapSegment,
502
519
  process_interaction,
package/index.js CHANGED
@@ -18,6 +18,7 @@ module.exports = {
18
18
  dependencies: ["@saltcorn/large-language-model"],
19
19
  viewtemplates: [require("./agent-view")],
20
20
  plugin_name: "agents",
21
+ ready_for_mobile: true,
21
22
  headers: [
22
23
  {
23
24
  script: `/plugins/public/agents@${
@@ -36,6 +37,26 @@ module.exports = {
36
37
  Agent: require("./action"),
37
38
  },
38
39
  functions: {
40
+ inspect_agent: {
41
+ run: async (agent, user, row) => {
42
+ const action = agent.runWithoutRow
43
+ ? agent
44
+ : await Trigger.findOne(
45
+ typeof agent == "number" ? { id: agent } : { name: agent },
46
+ );
47
+ const complArgs = await getCompletionArguments(
48
+ action.configuration,
49
+ user,
50
+ row,
51
+ );
52
+ return {
53
+ ...complArgs,
54
+ action,
55
+ };
56
+ },
57
+ isAsync: true,
58
+ description: "Return system prompt, tools and action of an agent",
59
+ },
39
60
  agent_generate: {
40
61
  run: async (agent_name, prompt, opts = {}) => {
41
62
  const action = await Trigger.findOne({ name: agent_name });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/agents",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "AI agents for Saltcorn",
5
5
  "main": "index.js",
6
6
  "dependencies": {