@saltcorn/agents 0.6.1 → 0.6.3

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
@@ -169,6 +169,7 @@ const uploadForm = (viewname, req) =>
169
169
  type: "file",
170
170
  class: "d-none",
171
171
  accept: "image/*",
172
+ multiple: true,
172
173
  onchange: `agent_file_attach(event)`,
173
174
  }),
174
175
  span({ class: "ms-2 filename-label" }),
@@ -584,9 +585,15 @@ const run = async (
584
585
  cursor: pointer;
585
586
  }
586
587
  .copilot-entry span.attach_agent_image_wrap {
587
- position: relative;
588
+ position: relative;
588
589
  top: -1.8rem;
589
- left: 0.2rem;
590
+ left: 0.2rem;
591
+ }
592
+ .copilot-entry.dragover {
593
+ outline: 2px dashed var(--tblr-primary, #0054a6);
594
+ outline-offset: -2px;
595
+ background: var(--tblr-primary-bg-subtle, rgba(0, 84, 166, 0.05));
596
+ border-radius: 0.25rem;
590
597
  }
591
598
  .copilot-entry .explainer {
592
599
  position: relative;
@@ -620,16 +627,24 @@ const run = async (
620
627
  }
621
628
  function processCopilotResponse(res) {
622
629
  console.log("processCopilotResponse", res)
623
- const hadFile = $("input#attach_agent_image").val();
624
- let fileBadge = hadFile ? '<span class="badge text-bg-info"><i class="fas fa-image me-1"></i>'+$("input#attach_agent_image")[0].files?.item?.(0)?.name||"File"+'</span>': ""
625
- $("span.filename-label").text("");
630
+ const fileInput = $("input#attach_agent_image")[0];
631
+ let fileBadge = "";
632
+ if (fileInput?.files?.length) {
633
+ fileBadge = Array.from(fileInput.files).map(f =>
634
+ '<span class="badge text-bg-info"><i class="fas fa-image me-1"></i>'+f.name+'</span>'
635
+ ).join(" ");
636
+ }
637
+ $("span.filename-label").text("").removeClass("me-2");
638
+ _agentDT.items.clear();
626
639
  $("input#attach_agent_image").val(null);
627
640
  $("#sendbuttonicon").attr("class","far fa-paper-plane");
628
641
  const $runidin= $("input[name=run_id")
629
642
  if(res.run_id && (!$runidin.val() || $runidin.val()=="undefined"))
630
643
  $runidin.val(res.run_id);
631
644
  const wrapSegment = (html, who) => '<div class="interaction-segment"><span class="badge bg-secondary">'+who+'</span>'+html+'</div>'
632
- $("#copilotinteractions").append(wrapSegment('<p>'+$("textarea[name=userinput]").val()+'</p>'+fileBadge, "You"))
645
+ const user_input = $("textarea[name=userinput]").val()
646
+ if(user_input)
647
+ $("#copilotinteractions").append(wrapSegment('<p>'+user_input+'</p>'+fileBadge, "You"))
633
648
  $("textarea[name=userinput]").val("")
634
649
  $('form.agent-view div.next_response_scratch').html("")
635
650
  window['stream scratch ${viewname} ${rndid}'] = []
@@ -637,8 +652,32 @@ const run = async (
637
652
  $("#copilotinteractions").append(res.response)
638
653
  }
639
654
  window.processCopilotResponse = processCopilotResponse;
655
+ const _agentDT = new DataTransfer();
656
+ function setAgentFiles(files) {
657
+ for (const f of files) _agentDT.items.add(f);
658
+ document.getElementById('attach_agent_image').files = _agentDT.files;
659
+ updateFileLabel();
660
+ }
661
+ function updateFileLabel() {
662
+ const n = _agentDT.files.length;
663
+ const $label = $(".attach_agent_image_wrap span.filename-label");
664
+ if (n === 0) {
665
+ $label.html("").removeClass("me-2");
666
+ } else {
667
+ $label.addClass("me-2");
668
+ 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>');
670
+ }
671
+ }
672
+ function clearAgentFiles() {
673
+ _agentDT.items.clear();
674
+ $("input#attach_agent_image").val(null);
675
+ updateFileLabel();
676
+ }
677
+ window.clearAgentFiles = clearAgentFiles;
640
678
  function agent_file_attach(e) {
641
- $(".attach_agent_image_wrap span.filename-label").text(e.target.files[0].name)
679
+ _agentDT.items.clear();
680
+ setAgentFiles(e.target.files);
642
681
  }
643
682
  function restore_old_button_elem(btn) {
644
683
  const oldText = $(btn).data("old-text");
@@ -690,6 +729,44 @@ const run = async (
690
729
  }
691
730
  }
692
731
  document.getElementById("inputuserinput").addEventListener("keydown", submitOnEnter);
732
+ if (document.getElementById('attach_agent_image')) {
733
+ let _dragCtr = 0;
734
+ const _copilotEntry = document.querySelector('.copilot-entry');
735
+ _copilotEntry.addEventListener('dragover', function(e) {
736
+ e.preventDefault();
737
+ });
738
+ _copilotEntry.addEventListener('dragenter', function(e) {
739
+ e.preventDefault();
740
+ _dragCtr++;
741
+ this.classList.add('dragover');
742
+ });
743
+ _copilotEntry.addEventListener('dragleave', function(e) {
744
+ _dragCtr--;
745
+ if (_dragCtr === 0) this.classList.remove('dragover');
746
+ });
747
+ _copilotEntry.addEventListener('drop', function(e) {
748
+ e.preventDefault();
749
+ _dragCtr = 0;
750
+ this.classList.remove('dragover');
751
+ const imgs = Array.from(e.dataTransfer.files).filter(f => f.type.startsWith('image/'));
752
+ if (imgs.length) setAgentFiles(imgs);
753
+ });
754
+ document.getElementById('inputuserinput').addEventListener('paste', function(e) {
755
+ const items = e.clipboardData?.items;
756
+ if (!items) return;
757
+ const pastedFiles = [];
758
+ for (const item of items) {
759
+ if (item.type.startsWith('image/')) {
760
+ const file = item.getAsFile();
761
+ if (file) pastedFiles.push(file);
762
+ }
763
+ }
764
+ if (pastedFiles.length) {
765
+ e.preventDefault();
766
+ setAgentFiles(pastedFiles);
767
+ }
768
+ });
769
+ }
693
770
  function spin_send_button() {
694
771
  $("#sendbuttonicon").attr("class","fas fa-spinner fa-spin");
695
772
  };`,
@@ -751,35 +828,43 @@ const interact = async (table_id, viewname, config, body, { req, res }) => {
751
828
  }
752
829
  let fileBadges = "";
753
830
  if (config.image_upload && req.files?.file) {
754
- const file = await File.from_req_files(
755
- req.files.file,
756
- req.user ? req.user.id : null,
757
- 100,
758
- // file_field?.attributes?.folder
759
- );
760
- fileBadges = span(
761
- { class: "badge text-bg-info" },
762
- i({ class: "fas fa-image me-1" }),
763
- file.filename,
764
- );
765
- const baseUrl = getState().getConfig("base_url").replace(/\/$/, "");
766
- let imageurl;
767
- if (
768
- !config.image_base64 &&
769
- baseUrl &&
770
- !baseUrl.includes("http://localhost:")
771
- ) {
772
- imageurl = `${baseUrl}/files/serve/${file.path_to_serve}`;
773
- } else {
774
- const b64 = await file.get_contents("base64");
775
- imageurl = `data:${file.mimetype};base64,${b64}`;
831
+ const rawFiles = Array.isArray(req.files.file)
832
+ ? req.files.file
833
+ : [req.files.file];
834
+ const badges = [];
835
+ for (const rawFile of rawFiles) {
836
+ const file = await File.from_req_files(
837
+ rawFile,
838
+ req.user ? req.user.id : null,
839
+ 100,
840
+ );
841
+ badges.push(
842
+ span(
843
+ { class: "badge text-bg-info" },
844
+ i({ class: "fas fa-image me-1" }),
845
+ file.filename,
846
+ ),
847
+ );
848
+ const baseUrl = getState().getConfig("base_url").replace(/\/$/, "");
849
+ let imageurl;
850
+ if (
851
+ !config.image_base64 &&
852
+ baseUrl &&
853
+ !baseUrl.includes("http://localhost:")
854
+ ) {
855
+ imageurl = `${baseUrl}/files/serve/${file.path_to_serve}`;
856
+ } else {
857
+ const b64 = await file.get_contents("base64");
858
+ imageurl = `data:${file.mimetype};base64,${b64}`;
859
+ }
860
+ await getState().functions.llm_add_message.run("image", imageurl, {
861
+ chat: run.context.interactions || [],
862
+ });
863
+ await addToContext(run, {
864
+ interactions: run.context.interactions || [],
865
+ });
776
866
  }
777
- await getState().functions.llm_add_message.run("image", imageurl, {
778
- chat: run.context.interactions || [],
779
- });
780
- await addToContext(run, {
781
- interactions: run.context.interactions || [],
782
- });
867
+ fileBadges = badges.join("");
783
868
  }
784
869
  await addToContext(run, {
785
870
  interactions: [
@@ -806,6 +891,7 @@ const interact = async (table_id, viewname, config, body, { req, res }) => {
806
891
  db.getTenantSchema(),
807
892
  {
808
893
  error: e?.message || e,
894
+ page_load_tag: req?.headers?.["page-load-tag"],
809
895
  },
810
896
  [req.user.id],
811
897
  );
package/common.js CHANGED
@@ -242,13 +242,14 @@ const process_interaction = async (
242
242
  db.getTenantSchema(),
243
243
  {
244
244
  eval_js: `processCopilotResponse({response: ${JSON.stringify(resp)}, run_id: ${run.id}})`,
245
+ page_load_tag: req?.headers?.["page-load-tag"],
245
246
  },
246
247
  [req.user.id],
247
248
  );
248
249
  else responses.push(resp);
249
250
  await addToContext(run, {
250
- html_interactions: [resp],
251
- });
251
+ html_interactions: [resp],
252
+ });
252
253
  };
253
254
  if (answer && typeof answer === "object" && answer.image_calls) {
254
255
  for (const image_call of answer.image_calls) {
@@ -359,7 +360,7 @@ const process_interaction = async (
359
360
  req,
360
361
  });
361
362
  if (rendered)
362
- add_responses(
363
+ add_response(
363
364
  wrapSegment(
364
365
  wrapCard(
365
366
  tool.skill.skill_label ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/agents",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "AI agents for Saltcorn",
5
5
  "main": "index.js",
6
6
  "dependencies": {
@@ -10,6 +10,10 @@ const { interpolate } = require("@saltcorn/data/utils");
10
10
  class RetrievalByEmbedding {
11
11
  static skill_name = "Retrieval by embedding";
12
12
 
13
+ get skill_label() {
14
+ return `Searching...`;
15
+ }
16
+
13
17
  constructor(cfg) {
14
18
  Object.assign(this, cfg);
15
19
  }