@saltcorn/agents 0.4.2 → 0.4.4

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
@@ -86,6 +86,12 @@ const configuration_workflow = (req) =>
86
86
  name: "prev_runs_closed",
87
87
  label: "Initially closed",
88
88
  type: "Bool",
89
+ showIf: { show_prev_runs: true },
90
+ },
91
+ {
92
+ name: "stream",
93
+ label: "Stream response",
94
+ type: "Bool",
89
95
  },
90
96
  {
91
97
  name: "placeholder",
@@ -161,6 +167,7 @@ const run = async (
161
167
  placeholder,
162
168
  explainer,
163
169
  image_upload,
170
+ stream,
164
171
  },
165
172
  state,
166
173
  { res, req }
@@ -320,7 +327,7 @@ const run = async (
320
327
  const input_form = form(
321
328
  {
322
329
  onsubmit: `event.preventDefault();spin_send_button();view_post('${viewname}', 'interact', new FormData(this), processCopilotResponse);return false;`,
323
- class: "form-namespace copilot mt-2",
330
+ class: ["form-namespace copilot mt-2 agent-view"],
324
331
  method: "post",
325
332
  },
326
333
  input({
@@ -365,7 +372,8 @@ const run = async (
365
372
  class: "debugicon fas fa-bug",
366
373
  }),
367
374
  explainer && small({ class: "explainer" }, i(explainer))
368
- )
375
+ ),
376
+ stream && div({ class: "next_response_scratch" })
369
377
  );
370
378
 
371
379
  const prev_runs_side_bar = div(
@@ -497,6 +505,7 @@ const run = async (
497
505
  if(hadFile)
498
506
  $("#copilotinteractions").append(wrapSegment('File', "You"))
499
507
  $("textarea[name=userinput]").val("")
508
+ $('form.agent-view div.next_response_scratch').html("")
500
509
 
501
510
  if(res.response)
502
511
  $("#copilotinteractions").append(res.response)
@@ -657,7 +666,8 @@ const interact = async (table_id, viewname, config, body, { req, res }) => {
657
666
  req,
658
667
  action.name,
659
668
  [],
660
- triggering_row
669
+ triggering_row,
670
+ config.stream
661
671
  );
662
672
  };
663
673
 
package/common.js CHANGED
@@ -2,6 +2,7 @@ const { getState } = require("@saltcorn/data/db/state");
2
2
  const { div, span } = require("@saltcorn/markup/tags");
3
3
  const Trigger = require("@saltcorn/data/models/trigger");
4
4
  const { interpolate } = require("@saltcorn/data/utils");
5
+ const db = require("@saltcorn/data/db");
5
6
 
6
7
  const MarkdownIt = require("markdown-it"),
7
8
  md = new MarkdownIt();
@@ -105,13 +106,9 @@ const getCompletionArguments = async (config, user, triggering_row) => {
105
106
  else if (skillTools) tools.push(skillTools);
106
107
  }
107
108
  if (tools.length === 0) tools = undefined;
108
- return { tools, systemPrompt: sysPrompts.join("\n\n") };
109
- };
110
-
111
- const getCompletion = async (language, prompt) => {
112
- return getState().functions.llm_generate.run(prompt, {
113
- systemPrompt: `You are a helpful code assistant. Your language of choice is ${language}. Do not include any explanation, just generate the code block itself.`,
114
- });
109
+ const complArgs = { tools, systemPrompt: sysPrompts.join("\n\n") };
110
+ if (config.model) complArgs.model = config.model;
111
+ return complArgs;
115
112
  };
116
113
 
117
114
  const incompleteCfgMsg = () => {
@@ -194,8 +191,10 @@ const process_interaction = async (
194
191
  req,
195
192
  agent_label = "Copilot",
196
193
  prevResponses = [],
197
- triggering_row = {}
194
+ triggering_row = {},
195
+ stream = false
198
196
  ) => {
197
+ const sysState = getState();
199
198
  const complArgs = await getCompletionArguments(
200
199
  config,
201
200
  req.user,
@@ -207,7 +206,24 @@ const process_interaction = async (
207
206
  const debugMode = is_debug_mode(config, req.user);
208
207
  const debugCollector = {};
209
208
  if (debugMode) complArgs.debugCollector = debugCollector;
210
- const answer = await getState().functions.llm_generate.run("", complArgs);
209
+ if (stream && sysState.getConfig("enable_dynamic_updates") && req.user) {
210
+ complArgs.streamCallback = (response) => {
211
+ const content =
212
+ response.choices[0].content || response.choices[0].delta?.content;
213
+ if (content)
214
+ sysState?.emitDynamicUpdate(
215
+ db.getTenantSchema(),
216
+ {
217
+ eval_js: `$('form.agent-view div.next_response_scratch').append(${JSON.stringify(
218
+ content
219
+ )})`,
220
+ },
221
+ [req.user.id]
222
+ );
223
+ };
224
+ }
225
+ const answer = await sysState.functions.llm_generate.run("", complArgs);
226
+
211
227
  if (debugMode)
212
228
  await addToContext(run, {
213
229
  api_interactions: [debugCollector],
@@ -267,7 +283,7 @@ const process_interaction = async (
267
283
  let hasResult = false;
268
284
  if ((answer.mcp_calls || []).length && !answer.content) hasResult = true;
269
285
  for (const tool_call of answer.tool_calls || []) {
270
- console.log("call function", tool_call.function);
286
+ console.log("call function", tool_call.function?.name);
271
287
 
272
288
  await addToContext(run, {
273
289
  funcalls: { [tool_call.id]: tool_call.function },
@@ -276,6 +292,24 @@ const process_interaction = async (
276
292
  const tool = find_tool(tool_call.function?.name, config);
277
293
 
278
294
  if (tool) {
295
+ if (
296
+ stream &&
297
+ sysState.getConfig("enable_dynamic_updates") &&
298
+ req.user
299
+ ) {
300
+ let content =
301
+ "Using skill: " + tool.skill.skill_label ||
302
+ tool.skill.constructor.skill_name;
303
+ sysState?.emitDynamicUpdate(
304
+ db.getTenantSchema(),
305
+ {
306
+ eval_js: `$('form.agent-view div.next_response_scratch').append(${JSON.stringify(
307
+ content
308
+ )})`,
309
+ },
310
+ [req.user.id]
311
+ );
312
+ }
279
313
  if (tool.tool.renderToolCall) {
280
314
  const row = JSON.parse(tool_call.function.arguments);
281
315
  const rendered = await tool.tool.renderToolCall(row, {
@@ -341,7 +375,8 @@ const process_interaction = async (
341
375
  req,
342
376
  agent_label,
343
377
  [...prevResponses, ...responses],
344
- triggering_row
378
+ triggering_row,
379
+ stream
345
380
  );
346
381
  } else if (typeof answer === "string")
347
382
  responses.push(wrapSegment(md.render(answer), agent_label));
@@ -359,7 +394,6 @@ module.exports = {
359
394
  get_skills,
360
395
  get_skill_class,
361
396
  incompleteCfgMsg,
362
- getCompletion,
363
397
  find_tool,
364
398
  get_skill_instances,
365
399
  getCompletionArguments,
package/index.js CHANGED
@@ -56,7 +56,12 @@ module.exports = {
56
56
  type: "String",
57
57
  fieldview: "textarea",
58
58
  },
59
-
59
+ {
60
+ name: "model",
61
+ label: "Model",
62
+ sublabel: "Override default model name",
63
+ type: "String",
64
+ },
60
65
  new FieldRepeat({
61
66
  name: "skills",
62
67
  label: "Skills",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/agents",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "AI agents for Saltcorn",
5
5
  "main": "index.js",
6
6
  "dependencies": {
@@ -9,7 +9,8 @@
9
9
  "node-sql-parser": "4.15.0",
10
10
  "markdown-it": "14.1.0",
11
11
  "style-to-object": "1.0.8",
12
- "node-html-parser": "7.0.1"
12
+ "node-html-parser": "7.0.1",
13
+ "node-fetch": "2.6.9"
13
14
  },
14
15
  "author": "Tom Nielsen",
15
16
  "license": "MIT",
@@ -11,6 +11,7 @@ const db = require("@saltcorn/data/db");
11
11
  const { eval_expression } = require("@saltcorn/data/models/expression");
12
12
  const { interpolate } = require("@saltcorn/data/utils");
13
13
  const vm = require("vm");
14
+ const fetch = global.fetch || require("node-fetch");
14
15
 
15
16
  class PreloadData {
16
17
  static skill_name = "Preload Data";
@@ -36,6 +37,7 @@ class PreloadData {
36
37
  Trigger,
37
38
  setTimeout,
38
39
  interpolate,
40
+ fetch,
39
41
  require,
40
42
  getConfig: (k) =>
41
43
  sysState.isFixedConfig(k) ? undefined : sysState.getConfig(k),