@saltcorn/agents 0.7.14 → 0.7.15

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
@@ -133,6 +133,12 @@ const configuration_workflow = (req) =>
133
133
  },
134
134
  ]
135
135
  : []),
136
+ {
137
+ name: "shared",
138
+ label: "Shared runs",
139
+ sublabel: "Users can open runs created by other users",
140
+ type: "Bool",
141
+ },
136
142
  {
137
143
  name: "layout",
138
144
  label: "Layout",
@@ -267,16 +273,31 @@ const run = async (
267
273
  stream,
268
274
  audio_recorder,
269
275
  layout,
276
+ shared,
270
277
  },
271
278
  state,
272
279
  { res, req },
273
280
  ) => {
274
281
  const action = agent_action || (await Trigger.findOne({ id: action_id }));
275
282
  if (!action) throw new Error(`Action not found: ${action_id}`);
283
+ let triggering_row_id;
284
+ if (table_id) {
285
+ const table = Table.findOne(table_id);
286
+ const pk = table?.pk_name;
287
+ if (table && state[pk])
288
+ //triggering_row = await table.getRow({ [pk]: state[pk] });
289
+ triggering_row_id = state[pk];
290
+ }
276
291
  const prevRuns = show_prev_runs
277
292
  ? (
278
293
  await WorkflowRun.find(
279
- { trigger_id: action.id, started_by: req.user?.id },
294
+ {
295
+ trigger_id: action.id,
296
+ ...(shared ? {} : { started_by: req.user?.id }),
297
+ ...(triggering_row_id
298
+ ? { context: { json: ["triggering_row_id", triggering_row_id] } }
299
+ : {}),
300
+ },
280
301
  { orderBy: "started_at", orderDesc: true, limit: 30 },
281
302
  )
282
303
  ).filter((r) => r.context.interactions)
@@ -285,21 +306,14 @@ const run = async (
285
306
  const cfgMsg = incompleteCfgMsg();
286
307
  if (cfgMsg) return cfgMsg;
287
308
  let runInteractions = "";
288
- let triggering_row_id;
289
- if (table_id) {
290
- const table = Table.findOne(table_id);
291
- const pk = table?.pk_name;
292
- if (table && state[pk])
293
- //triggering_row = await table.getRow({ [pk]: state[pk] });
294
- triggering_row_id = state[pk];
295
- }
309
+
296
310
  const initial_q = state.run_id ? undefined : state._q;
297
311
  if (state.run_id) {
298
312
  const run = prevRuns
299
313
  ? prevRuns.find((r) => r.id == state.run_id)
300
314
  : await WorkflowRun.findOne({
301
315
  trigger_id: action.id,
302
- started_by: req.user?.id,
316
+ ...(shared ? {} : { started_by: req.user?.id }),
303
317
  id: state.run_id,
304
318
  });
305
319
  const interactMarkups = [];
@@ -509,6 +523,15 @@ const run = async (
509
523
  { id: "audioinputicon", class: "", onclick: "" },
510
524
  i({ class: "fas fa-microphone" }),
511
525
  ),
526
+ button(
527
+ {
528
+ type: "button",
529
+ class: "btn btn-xs btn-sm btn-outline-secondary cancelbtn ms-2",
530
+ onclick: "press_cancel_button()",
531
+ style: { display: "none" },
532
+ },
533
+ i({ class: "fas fa-stop" }),
534
+ ),
512
535
  explainer && small({ class: "explainer" }, i(explainer)),
513
536
  ),
514
537
  stream && realTimeCollabScript(viewname, rndid, layout),
@@ -670,6 +693,12 @@ const run = async (
670
693
  left: 0.1rem;
671
694
  cursor: pointer;
672
695
  }
696
+ .copilot-entry .cancelbtn {
697
+ position: relative;
698
+ top: -1.8rem;
699
+ left: 0.1rem;
700
+ cursor: pointer;
701
+ }
673
702
  .copilot-entry .skill-form-widget {
674
703
  position: relative;
675
704
  top: -2rem;
@@ -957,7 +986,10 @@ const run = async (
957
986
  $("span.filename-label").text("").removeClass("me-2");
958
987
  window._agentDT.items.clear();
959
988
  $("input#attach_agent_image").val(null);
960
- if(!not_final || (!${JSON.stringify(dyn_updates)})) $("#sendbuttonicon").attr("class","far fa-paper-plane");
989
+ if(!not_final || (!${JSON.stringify(dyn_updates)})) {
990
+ $("#sendbuttonicon").attr("class","far fa-paper-plane");
991
+ $(".cancelbtn").hide();
992
+ }
961
993
  const $runidin= $("input[name=run_id")
962
994
  if(res.run_id && (!$runidin.val() || $runidin.val()=="undefined"))
963
995
  $runidin.val(res.run_id);
@@ -983,6 +1015,7 @@ const run = async (
983
1015
  $(".agent-waiting-indicator").remove();
984
1016
  $("textarea[name=userinput]").prop("disabled", false).attr("placeholder", ${JSON.stringify(placeholder || "How can I help you?")}).focus();
985
1017
  $(".copilot-entry .submit-button").css("pointer-events", "");
1018
+ $(".cancelbtn").hide();
986
1019
  scrollAgentToBottom();
987
1020
  }
988
1021
  window._agentDT = new DataTransfer();
@@ -1028,6 +1061,12 @@ const run = async (
1028
1061
  if(runid)
1029
1062
  view_post('${viewname}', 'debug_info', {run_id:runid, triggering_row_id:$("input[name=triggering_row_id").val()}, show_agent_debug_info)
1030
1063
  }
1064
+ function press_cancel_button() {
1065
+ const $runidin= $("input[name=run_id")
1066
+ const runid = $runidin.val()
1067
+ if(runid)
1068
+ view_post('${viewname}', 'cancel', {run_id:runid})
1069
+ }
1031
1070
  function show_agent_debug_info(info) {
1032
1071
  ensure_modal_exists_and_closed();
1033
1072
  $("#scmodal .modal-body").html(info.debug_html);
@@ -1106,6 +1145,7 @@ const run = async (
1106
1145
  }
1107
1146
  function spin_send_button() {
1108
1147
  $("#sendbuttonicon").attr("class","fas fa-spinner fa-spin");
1148
+ $(".cancelbtn").show()
1109
1149
  $("textarea[name=userinput]").prop("disabled", true).attr("placeholder", "Waiting for response...");
1110
1150
  $(".copilot-entry .submit-button").css("pointer-events", "none");
1111
1151
  const isModernLayout = ${JSON.stringify((layout || "").startsWith("Modern chat"))};
@@ -1235,14 +1275,15 @@ const interact = async (table_id, viewname, config, body, { req, res }) => {
1235
1275
  "You",
1236
1276
  true,
1237
1277
  config.layout,
1278
+ req?.user,
1238
1279
  );
1239
-
1240
1280
  await addToContext(run, {
1241
1281
  interactions: [
1242
1282
  ...(run.context.interactions || []),
1243
1283
  { role: "user", content: userinput },
1244
1284
  ],
1245
1285
  html_interactions: [userInteractions],
1286
+ status: "Running",
1246
1287
  });
1247
1288
  const dyn_updates = getState().getConfig("enable_dynamic_updates", true);
1248
1289
  if (dyn_updates) {
@@ -1292,6 +1333,13 @@ const delprevrun = async (table_id, viewname, config, body, { req, res }) => {
1292
1333
  return;
1293
1334
  };
1294
1335
 
1336
+ const cancel = async (table_id, viewname, config, body, { req, res }) => {
1337
+ const { run_id } = body;
1338
+ const run = await WorkflowRun.findOne({ id: +run_id });
1339
+ await run.update({ status: "Cancel" });
1340
+ return;
1341
+ };
1342
+
1295
1343
  const debug_info = async (table_id, viewname, config, body, { req, res }) => {
1296
1344
  const { run_id, triggering_row_id } = body;
1297
1345
  const action =
@@ -1478,7 +1526,7 @@ const execute_user_action = async (
1478
1526
  const { layout } = config;
1479
1527
 
1480
1528
  const resp = JSON.stringify(
1481
- wrapSegment(uadata.click_replace_text, "You", true, layout),
1529
+ wrapSegment(uadata.click_replace_text, "You", true, layout, req?.user),
1482
1530
  );
1483
1531
  getState().emitDynamicUpdate(
1484
1532
  db.getTenantSchema(),
@@ -1497,6 +1545,7 @@ const execute_user_action = async (
1497
1545
  "You",
1498
1546
  true,
1499
1547
  config.layout,
1548
+ req?.user,
1500
1549
  );
1501
1550
  return hi;
1502
1551
  },
@@ -1544,6 +1593,13 @@ module.exports = {
1544
1593
  //tableless: true,
1545
1594
  table_optional: true,
1546
1595
  run,
1547
- routes: { interact, delprevrun, debug_info, skillroute, execute_user_action },
1596
+ routes: {
1597
+ interact,
1598
+ delprevrun,
1599
+ debug_info,
1600
+ skillroute,
1601
+ execute_user_action,
1602
+ cancel,
1603
+ },
1548
1604
  mobile_render_server_side: true,
1549
1605
  };
package/common.js CHANGED
@@ -4,6 +4,7 @@ const Trigger = require("@saltcorn/data/models/trigger");
4
4
  const View = require("@saltcorn/data/models/view");
5
5
  const { interpolate } = require("@saltcorn/data/utils");
6
6
  const db = require("@saltcorn/data/db");
7
+ const WorkflowRun = require("@saltcorn/data/models/workflow_run");
7
8
 
8
9
  const MarkdownIt = require("markdown-it"),
9
10
  md = new MarkdownIt({ html: true, breaks: true, linkify: true });
@@ -183,6 +184,7 @@ const addToContext = async (run, newCtx) => {
183
184
  if (!run) return;
184
185
  if (run.addToContext) return await run.addToContext(newCtx);
185
186
  let changed = true;
187
+ let extraRunSet = {};
186
188
  Object.keys(newCtx).forEach((k) => {
187
189
  if (Array.isArray(run.context[k])) {
188
190
  if (!Array.isArray(newCtx[k]))
@@ -195,12 +197,15 @@ const addToContext = async (run, newCtx) => {
195
197
  throw new Error("Must be object to append to object");
196
198
  Object.assign(run.context[k], newCtx[k]);
197
199
  changed = true;
200
+ } else if (k === "status") {
201
+ extraRunSet.status = newCtx[k];
198
202
  } else {
199
203
  run.context[k] = newCtx[k];
200
204
  changed = true;
201
205
  }
202
206
  });
203
- if (changed && run.update) await run.update({ context: run.context });
207
+ if (changed && run.update)
208
+ await run.update({ context: run.context, ...extraRunSet });
204
209
  };
205
210
 
206
211
  const saveInteractions = async (run) => {
@@ -209,12 +214,12 @@ const saveInteractions = async (run) => {
209
214
  });
210
215
  };
211
216
 
212
- const wrapSegment = (html, who, to_right, layout) =>
217
+ const wrapSegment = (html, who, to_right, layout, user) =>
213
218
  who === null
214
219
  ? html
215
220
  : layout && layout.startsWith("Modern chat")
216
221
  ? `<div class="chat-message ${to_right ? "chat-user" : "chat-assistant"}">` +
217
- `<div class="chat-avatar"><i class="fas ${to_right ? "fa-user" : "fa-robot"}"></i></div>` +
222
+ `<div class="chat-avatar"${user ? ` title="${user.email} at ${new Date().toString()}"` : ""}><i class="fas ${to_right ? "fa-user" : "fa-robot"}"></i></div>` +
218
223
  `<div class="chat-bubble">${html}</div>` +
219
224
  `</div>`
220
225
  : `<div class="interaction-segment ${to_right ? "to-right" : ""}"><div><div class="badgewrap"><span class="badge bg-secondary">` +
@@ -673,7 +678,10 @@ const process_interaction = async (
673
678
  if (myHasResult && !stop) hasResult = true;
674
679
  }
675
680
  }
676
- if (hasResult)
681
+ //await db.commitAndBeginNewTransaction();
682
+ const freshRun = await WorkflowRun.findOne({ id: run.id });
683
+
684
+ if (hasResult && freshRun.status !== "Cancel")
677
685
  return await process_interaction(
678
686
  run,
679
687
  config,
package/locales/de.json CHANGED
@@ -1 +1 @@
1
- { "Sessions": "Sitzungen" }
1
+ { "Sessions": "Sitzungen", "Approve": "Freigeben", "Approved": "Freigegeben" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/agents",
3
- "version": "0.7.14",
3
+ "version": "0.7.15",
4
4
  "description": "AI agents for Saltcorn",
5
5
  "main": "index.js",
6
6
  "dependencies": {
@@ -63,15 +63,15 @@ class PlanApprovalSkill {
63
63
  provideTools = () => {
64
64
  return {
65
65
  type: "function",
66
- process: async (row) => {
66
+ process: async (row, { req }) => {
67
67
  return {
68
68
  stop: true,
69
69
  add_response: row.plan,
70
70
  add_user_action: {
71
71
  name: "approve_plan",
72
72
  type: "button",
73
- label: `Approve`,
74
- click_replace_text: "Approved",
73
+ label: req.__("Approve"),
74
+ click_replace_text: req.__("Approved"),
75
75
  input: {},
76
76
  },
77
77
  };