@saltcorn/agents 0.1.0 → 0.1.2
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/README.md +8 -0
- package/agent-view.js +19 -153
- package/common.js +162 -4
- package/index.js +11 -10
- package/package.json +1 -1
- package/skills/EmbeddingRetrieval.js +61 -35
- package/skills/FTSRetrieval.js +12 -1
package/README.md
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
# agents
|
|
2
|
+
|
|
2
3
|
AI Agents for Saltcorn
|
|
4
|
+
|
|
5
|
+
To use this plugin:
|
|
6
|
+
|
|
7
|
+
1. Create an action of type Agent. Configure with system prompt and skills
|
|
8
|
+
2. Create a view of type Agent chat, picking that agent in the configuration
|
|
9
|
+
|
|
10
|
+
The module is currently unstable. Some skills will be moved to other modules which will have to be installed as well.
|
package/agent-view.js
CHANGED
|
@@ -33,6 +33,10 @@ const {
|
|
|
33
33
|
incompleteCfgMsg,
|
|
34
34
|
getCompletionArguments,
|
|
35
35
|
find_tool,
|
|
36
|
+
addToContext,
|
|
37
|
+
wrapCard,
|
|
38
|
+
wrapSegment,
|
|
39
|
+
process_interaction,
|
|
36
40
|
} = require("./common");
|
|
37
41
|
const MarkdownIt = require("markdown-it"),
|
|
38
42
|
md = new MarkdownIt();
|
|
@@ -72,6 +76,12 @@ const configuration_workflow = (req) =>
|
|
|
72
76
|
label: "Show previous runs",
|
|
73
77
|
type: "Bool",
|
|
74
78
|
},
|
|
79
|
+
{
|
|
80
|
+
name: "placeholder",
|
|
81
|
+
label: "Placeholder",
|
|
82
|
+
type: "String",
|
|
83
|
+
default: "How can I help you?"
|
|
84
|
+
},
|
|
75
85
|
],
|
|
76
86
|
});
|
|
77
87
|
},
|
|
@@ -84,7 +94,7 @@ const get_state_fields = () => [];
|
|
|
84
94
|
const run = async (
|
|
85
95
|
table_id,
|
|
86
96
|
viewname,
|
|
87
|
-
{ action_id, show_prev_runs },
|
|
97
|
+
{ action_id, show_prev_runs, placeholder },
|
|
88
98
|
state,
|
|
89
99
|
{ res, req }
|
|
90
100
|
) => {
|
|
@@ -120,7 +130,7 @@ const run = async (
|
|
|
120
130
|
interactMarkups.push(
|
|
121
131
|
div(
|
|
122
132
|
{ class: "interaction-segment" },
|
|
123
|
-
span({ class: "badge bg-secondary" },
|
|
133
|
+
span({ class: "badge bg-secondary" }, action.name),
|
|
124
134
|
typeof interact.content === "string"
|
|
125
135
|
? md.render(interact.content)
|
|
126
136
|
: interact.content
|
|
@@ -146,7 +156,7 @@ const run = async (
|
|
|
146
156
|
toolSkill.skill.constructor.skill_name,
|
|
147
157
|
rendered
|
|
148
158
|
),
|
|
149
|
-
|
|
159
|
+
action.name
|
|
150
160
|
)
|
|
151
161
|
);
|
|
152
162
|
}
|
|
@@ -156,7 +166,7 @@ const run = async (
|
|
|
156
166
|
interactMarkups.push(
|
|
157
167
|
div(
|
|
158
168
|
{ class: "interaction-segment" },
|
|
159
|
-
span({ class: "badge bg-secondary" },
|
|
169
|
+
span({ class: "badge bg-secondary" }, action.name),
|
|
160
170
|
typeof interact.content === "string"
|
|
161
171
|
? md.render(interact.content)
|
|
162
172
|
: interact.content
|
|
@@ -188,7 +198,7 @@ const run = async (
|
|
|
188
198
|
interact.name,
|
|
189
199
|
markupContent
|
|
190
200
|
),
|
|
191
|
-
|
|
201
|
+
action.name
|
|
192
202
|
)
|
|
193
203
|
);
|
|
194
204
|
}
|
|
@@ -220,7 +230,7 @@ const run = async (
|
|
|
220
230
|
class: "form-control",
|
|
221
231
|
name: "userinput",
|
|
222
232
|
"data-fieldname": "userinput",
|
|
223
|
-
placeholder: "How can I help you?",
|
|
233
|
+
placeholder: placeholder || "How can I help you?",
|
|
224
234
|
id: "inputuserinput",
|
|
225
235
|
rows: "3",
|
|
226
236
|
autofocus: true,
|
|
@@ -406,7 +416,9 @@ const interact = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
406
416
|
interactions: [{ role: "user", content: userinput }],
|
|
407
417
|
});
|
|
408
418
|
}
|
|
409
|
-
|
|
419
|
+
const action = await Trigger.findOne({ id: config.action_id });
|
|
420
|
+
|
|
421
|
+
return await process_interaction(run, action.configuration, req, action.name);
|
|
410
422
|
};
|
|
411
423
|
|
|
412
424
|
const delprevrun = async (table_id, viewname, config, body, { req, res }) => {
|
|
@@ -420,130 +432,6 @@ const delprevrun = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
420
432
|
return;
|
|
421
433
|
};
|
|
422
434
|
|
|
423
|
-
const process_interaction = async (run, config, req, prevResponses = []) => {
|
|
424
|
-
const action = await Trigger.findOne({ id: config.action_id });
|
|
425
|
-
const complArgs = await getCompletionArguments(action.configuration);
|
|
426
|
-
complArgs.chat = run.context.interactions;
|
|
427
|
-
//complArgs.debugResult = true;
|
|
428
|
-
console.log("complArgs", JSON.stringify(complArgs, null, 2));
|
|
429
|
-
|
|
430
|
-
const answer = await getState().functions.llm_generate.run("", complArgs);
|
|
431
|
-
console.log("answer", answer);
|
|
432
|
-
await addToContext(run, {
|
|
433
|
-
interactions:
|
|
434
|
-
typeof answer === "object" && answer.tool_calls
|
|
435
|
-
? [
|
|
436
|
-
{
|
|
437
|
-
role: "assistant",
|
|
438
|
-
tool_calls: answer.tool_calls,
|
|
439
|
-
content: answer.content,
|
|
440
|
-
},
|
|
441
|
-
]
|
|
442
|
-
: [{ role: "assistant", content: answer }],
|
|
443
|
-
});
|
|
444
|
-
const responses = [];
|
|
445
|
-
|
|
446
|
-
if (typeof answer === "object" && answer.tool_calls) {
|
|
447
|
-
if (answer.content)
|
|
448
|
-
responses.push(wrapSegment(md.render(answer.content), "Copilot"));
|
|
449
|
-
//const actions = [];
|
|
450
|
-
let hasResult = false;
|
|
451
|
-
for (const tool_call of answer.tool_calls) {
|
|
452
|
-
console.log("call function", tool_call.function);
|
|
453
|
-
|
|
454
|
-
await addToContext(run, {
|
|
455
|
-
funcalls: { [tool_call.id]: tool_call.function },
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
const tool = find_tool(tool_call.function.name, action.configuration);
|
|
459
|
-
|
|
460
|
-
if (tool) {
|
|
461
|
-
if (tool.tool.renderToolCall) {
|
|
462
|
-
const row = JSON.parse(tool_call.function.arguments);
|
|
463
|
-
const rendered = await tool.tool.renderToolCall(row, {
|
|
464
|
-
req,
|
|
465
|
-
});
|
|
466
|
-
if (rendered)
|
|
467
|
-
responses.push(
|
|
468
|
-
wrapSegment(
|
|
469
|
-
wrapCard(
|
|
470
|
-
tool.skill.skill_label || tool.skill.constructor.skill_name,
|
|
471
|
-
rendered
|
|
472
|
-
),
|
|
473
|
-
"Copilot"
|
|
474
|
-
)
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
hasResult = true;
|
|
478
|
-
const result = await tool.tool.process(
|
|
479
|
-
JSON.parse(tool_call.function.arguments)
|
|
480
|
-
);
|
|
481
|
-
if (
|
|
482
|
-
(typeof result === "object" && Object.keys(result || {}).length) ||
|
|
483
|
-
typeof result === "string"
|
|
484
|
-
) {
|
|
485
|
-
if (tool.tool.renderToolResponse) {
|
|
486
|
-
const rendered = await tool.tool.renderToolResponse(result, {
|
|
487
|
-
req,
|
|
488
|
-
});
|
|
489
|
-
if (rendered)
|
|
490
|
-
responses.push(
|
|
491
|
-
wrapSegment(
|
|
492
|
-
wrapCard(
|
|
493
|
-
tool.skill.skill_label || tool.skill.constructor.skill_name,
|
|
494
|
-
rendered
|
|
495
|
-
),
|
|
496
|
-
"Copilot"
|
|
497
|
-
)
|
|
498
|
-
);
|
|
499
|
-
}
|
|
500
|
-
hasResult = true;
|
|
501
|
-
}
|
|
502
|
-
await addToContext(run, {
|
|
503
|
-
interactions: [
|
|
504
|
-
{
|
|
505
|
-
role: "tool",
|
|
506
|
-
tool_call_id: tool_call.id,
|
|
507
|
-
name: tool_call.function.name,
|
|
508
|
-
content:
|
|
509
|
-
result && typeof result !== "string"
|
|
510
|
-
? JSON.stringify(result)
|
|
511
|
-
: result || "Action run",
|
|
512
|
-
},
|
|
513
|
-
],
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
if (hasResult)
|
|
518
|
-
return await process_interaction(run, config, req, [
|
|
519
|
-
...prevResponses,
|
|
520
|
-
...responses,
|
|
521
|
-
]);
|
|
522
|
-
} else responses.push(wrapSegment(md.render(answer), "Copilot"));
|
|
523
|
-
|
|
524
|
-
return {
|
|
525
|
-
json: {
|
|
526
|
-
success: "ok",
|
|
527
|
-
response: [...prevResponses, ...responses].join(""),
|
|
528
|
-
run_id: run.id,
|
|
529
|
-
},
|
|
530
|
-
};
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
const wrapSegment = (html, who) =>
|
|
534
|
-
'<div class="interaction-segment"><span class="badge bg-secondary">' +
|
|
535
|
-
who +
|
|
536
|
-
"</span>" +
|
|
537
|
-
html +
|
|
538
|
-
"</div>";
|
|
539
|
-
|
|
540
|
-
const wrapCard = (title, ...inners) =>
|
|
541
|
-
span({ class: "badge bg-info ms-1" }, title) +
|
|
542
|
-
div(
|
|
543
|
-
{ class: "card mb-3 bg-secondary-subtle" },
|
|
544
|
-
div({ class: "card-body" }, inners)
|
|
545
|
-
);
|
|
546
|
-
|
|
547
435
|
const wrapAction = (
|
|
548
436
|
inner_markup,
|
|
549
437
|
viewname,
|
|
@@ -575,28 +463,6 @@ const wrapAction = (
|
|
|
575
463
|
) + div({ id: "postexec-" + tool_call.id })
|
|
576
464
|
);
|
|
577
465
|
|
|
578
|
-
const addToContext = async (run, newCtx) => {
|
|
579
|
-
if (run.addToContext) return await run.addToContext(newCtx);
|
|
580
|
-
let changed = true;
|
|
581
|
-
Object.keys(newCtx).forEach((k) => {
|
|
582
|
-
if (Array.isArray(run.context[k])) {
|
|
583
|
-
if (!Array.isArray(newCtx[k]))
|
|
584
|
-
throw new Error("Must be array to append to array");
|
|
585
|
-
run.context[k].push(...newCtx[k]);
|
|
586
|
-
changed = true;
|
|
587
|
-
} else if (typeof run.context[k] === "object") {
|
|
588
|
-
if (typeof newCtx[k] !== "object")
|
|
589
|
-
throw new Error("Must be object to append to object");
|
|
590
|
-
Object.assign(run.context[k], newCtx[k]);
|
|
591
|
-
changed = true;
|
|
592
|
-
} else {
|
|
593
|
-
run.context[k] = newCtx[k];
|
|
594
|
-
changed = true;
|
|
595
|
-
}
|
|
596
|
-
});
|
|
597
|
-
if (changed) await run.update({ context: run.context });
|
|
598
|
-
};
|
|
599
|
-
|
|
600
466
|
module.exports = {
|
|
601
467
|
name: "Agent Chat",
|
|
602
468
|
configuration_workflow,
|
package/common.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
const { getState } = require("@saltcorn/data/db/state");
|
|
2
|
+
const { div, span } = require("@saltcorn/markup/tags");
|
|
3
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
4
|
+
|
|
5
|
+
const MarkdownIt = require("markdown-it"),
|
|
6
|
+
md = new MarkdownIt();
|
|
2
7
|
|
|
3
8
|
const get_skills = () => {
|
|
4
9
|
return [
|
|
5
|
-
//require("./skills/EmbeddingRetrieval"),
|
|
6
10
|
require("./skills/FTSRetrieval"),
|
|
11
|
+
require("./skills/EmbeddingRetrieval"),
|
|
7
12
|
//require("./skills/AdaptiveFeedback"),
|
|
8
13
|
];
|
|
9
14
|
};
|
|
@@ -37,7 +42,6 @@ const find_tool = (name, config) => {
|
|
|
37
42
|
}
|
|
38
43
|
};
|
|
39
44
|
|
|
40
|
-
|
|
41
45
|
const getCompletionArguments = async (config) => {
|
|
42
46
|
let tools = [];
|
|
43
47
|
|
|
@@ -55,7 +59,6 @@ const getCompletionArguments = async (config) => {
|
|
|
55
59
|
return { tools, systemPrompt: sysPrompts.join("\n\n") };
|
|
56
60
|
};
|
|
57
61
|
|
|
58
|
-
|
|
59
62
|
const getCompletion = async (language, prompt) => {
|
|
60
63
|
return getState().functions.llm_generate.run(prompt, {
|
|
61
64
|
systemPrompt: `You are a helpful code assistant. Your language of choice is ${language}. Do not include any explanation, just generate the code block itself.`,
|
|
@@ -81,6 +84,157 @@ const incompleteCfgMsg = () => {
|
|
|
81
84
|
}
|
|
82
85
|
};
|
|
83
86
|
|
|
87
|
+
const addToContext = async (run, newCtx) => {
|
|
88
|
+
if (run.addToContext) return await run.addToContext(newCtx);
|
|
89
|
+
let changed = true;
|
|
90
|
+
Object.keys(newCtx).forEach((k) => {
|
|
91
|
+
if (Array.isArray(run.context[k])) {
|
|
92
|
+
if (!Array.isArray(newCtx[k]))
|
|
93
|
+
throw new Error("Must be array to append to array");
|
|
94
|
+
run.context[k].push(...newCtx[k]);
|
|
95
|
+
changed = true;
|
|
96
|
+
} else if (typeof run.context[k] === "object") {
|
|
97
|
+
if (typeof newCtx[k] !== "object")
|
|
98
|
+
throw new Error("Must be object to append to object");
|
|
99
|
+
Object.assign(run.context[k], newCtx[k]);
|
|
100
|
+
changed = true;
|
|
101
|
+
} else {
|
|
102
|
+
run.context[k] = newCtx[k];
|
|
103
|
+
changed = true;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
if (changed) await run.update({ context: run.context });
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const wrapSegment = (html, who) =>
|
|
110
|
+
'<div class="interaction-segment"><span class="badge bg-secondary">' +
|
|
111
|
+
who +
|
|
112
|
+
"</span>" +
|
|
113
|
+
html +
|
|
114
|
+
"</div>";
|
|
115
|
+
|
|
116
|
+
const wrapCard = (title, ...inners) =>
|
|
117
|
+
span({ class: "badge bg-info ms-1" }, title) +
|
|
118
|
+
div(
|
|
119
|
+
{ class: "card mb-3 bg-secondary-subtle" },
|
|
120
|
+
div({ class: "card-body" }, inners)
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const process_interaction = async (
|
|
124
|
+
run,
|
|
125
|
+
config,
|
|
126
|
+
req,
|
|
127
|
+
agent_label = "Copilot",
|
|
128
|
+
prevResponses = []
|
|
129
|
+
) => {
|
|
130
|
+
const complArgs = await getCompletionArguments(config);
|
|
131
|
+
complArgs.chat = run.context.interactions;
|
|
132
|
+
//complArgs.debugResult = true;
|
|
133
|
+
console.log("complArgs", JSON.stringify(complArgs, null, 2));
|
|
134
|
+
|
|
135
|
+
const answer = await getState().functions.llm_generate.run("", complArgs);
|
|
136
|
+
console.log("answer", answer);
|
|
137
|
+
await addToContext(run, {
|
|
138
|
+
interactions:
|
|
139
|
+
typeof answer === "object" && answer.tool_calls
|
|
140
|
+
? [
|
|
141
|
+
{
|
|
142
|
+
role: "assistant",
|
|
143
|
+
tool_calls: answer.tool_calls,
|
|
144
|
+
content: answer.content,
|
|
145
|
+
},
|
|
146
|
+
]
|
|
147
|
+
: [{ role: "assistant", content: answer }],
|
|
148
|
+
});
|
|
149
|
+
const responses = [];
|
|
150
|
+
|
|
151
|
+
if (typeof answer === "object" && answer.tool_calls) {
|
|
152
|
+
if (answer.content)
|
|
153
|
+
responses.push(wrapSegment(md.render(answer.content), agent_label));
|
|
154
|
+
//const actions = [];
|
|
155
|
+
let hasResult = false;
|
|
156
|
+
for (const tool_call of answer.tool_calls) {
|
|
157
|
+
console.log("call function", tool_call.function);
|
|
158
|
+
|
|
159
|
+
await addToContext(run, {
|
|
160
|
+
funcalls: { [tool_call.id]: tool_call.function },
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const tool = find_tool(tool_call.function.name, config);
|
|
164
|
+
|
|
165
|
+
if (tool) {
|
|
166
|
+
if (tool.tool.renderToolCall) {
|
|
167
|
+
const row = JSON.parse(tool_call.function.arguments);
|
|
168
|
+
const rendered = await tool.tool.renderToolCall(row, {
|
|
169
|
+
req,
|
|
170
|
+
});
|
|
171
|
+
if (rendered)
|
|
172
|
+
responses.push(
|
|
173
|
+
wrapSegment(
|
|
174
|
+
wrapCard(
|
|
175
|
+
tool.skill.skill_label || tool.skill.constructor.skill_name,
|
|
176
|
+
rendered
|
|
177
|
+
),
|
|
178
|
+
agent_label
|
|
179
|
+
)
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
hasResult = true;
|
|
183
|
+
const result = await tool.tool.process(
|
|
184
|
+
JSON.parse(tool_call.function.arguments)
|
|
185
|
+
);
|
|
186
|
+
if (
|
|
187
|
+
(typeof result === "object" && Object.keys(result || {}).length) ||
|
|
188
|
+
typeof result === "string"
|
|
189
|
+
) {
|
|
190
|
+
if (tool.tool.renderToolResponse) {
|
|
191
|
+
const rendered = await tool.tool.renderToolResponse(result, {
|
|
192
|
+
req,
|
|
193
|
+
});
|
|
194
|
+
if (rendered)
|
|
195
|
+
responses.push(
|
|
196
|
+
wrapSegment(
|
|
197
|
+
wrapCard(
|
|
198
|
+
tool.skill.skill_label || tool.skill.constructor.skill_name,
|
|
199
|
+
rendered
|
|
200
|
+
),
|
|
201
|
+
agent_label
|
|
202
|
+
)
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
hasResult = true;
|
|
206
|
+
}
|
|
207
|
+
await addToContext(run, {
|
|
208
|
+
interactions: [
|
|
209
|
+
{
|
|
210
|
+
role: "tool",
|
|
211
|
+
tool_call_id: tool_call.id,
|
|
212
|
+
name: tool_call.function.name,
|
|
213
|
+
content:
|
|
214
|
+
result && typeof result !== "string"
|
|
215
|
+
? JSON.stringify(result)
|
|
216
|
+
: result || "Action run",
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (hasResult)
|
|
223
|
+
return await process_interaction(run, config, req, agent_label, [
|
|
224
|
+
...prevResponses,
|
|
225
|
+
...responses,
|
|
226
|
+
]);
|
|
227
|
+
} else responses.push(wrapSegment(md.render(answer), agent_label));
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
json: {
|
|
231
|
+
success: "ok",
|
|
232
|
+
response: [...prevResponses, ...responses].join(""),
|
|
233
|
+
run_id: run.id,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
|
|
84
238
|
module.exports = {
|
|
85
239
|
get_skills,
|
|
86
240
|
get_skill_class,
|
|
@@ -88,5 +242,9 @@ module.exports = {
|
|
|
88
242
|
getCompletion,
|
|
89
243
|
find_tool,
|
|
90
244
|
get_skill_instances,
|
|
91
|
-
getCompletionArguments
|
|
245
|
+
getCompletionArguments,
|
|
246
|
+
addToContext,
|
|
247
|
+
wrapCard,
|
|
248
|
+
wrapSegment,
|
|
249
|
+
process_interaction,
|
|
92
250
|
};
|
package/index.js
CHANGED
|
@@ -2,7 +2,11 @@ const Workflow = require("@saltcorn/data/models/workflow");
|
|
|
2
2
|
const Form = require("@saltcorn/data/models/form");
|
|
3
3
|
const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
|
|
4
4
|
const { features } = require("@saltcorn/data/db/state");
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
get_skills,
|
|
7
|
+
getCompletionArguments,
|
|
8
|
+
process_interaction,
|
|
9
|
+
} = require("./common");
|
|
6
10
|
const { applyAsync } = require("@saltcorn/data/utils");
|
|
7
11
|
const WorkflowRun = require("@saltcorn/data/models/workflow_run");
|
|
8
12
|
const { interpolate } = require("@saltcorn/data/utils");
|
|
@@ -68,7 +72,7 @@ module.exports = {
|
|
|
68
72
|
}),
|
|
69
73
|
];
|
|
70
74
|
},
|
|
71
|
-
run: async ({ configuration, user, row, trigger_id, ...rest }) => {
|
|
75
|
+
run: async ({ configuration, user, row, trigger_id, req, ...rest }) => {
|
|
72
76
|
const userinput = interpolate(configuration.prompt, row, user);
|
|
73
77
|
const run = await WorkflowRun.create({
|
|
74
78
|
status: "Running",
|
|
@@ -80,11 +84,7 @@ module.exports = {
|
|
|
80
84
|
funcalls: {},
|
|
81
85
|
},
|
|
82
86
|
});
|
|
83
|
-
|
|
84
|
-
const answer = await getState().functions.llm_generate.run(
|
|
85
|
-
"",
|
|
86
|
-
complArgs
|
|
87
|
-
);
|
|
87
|
+
return await process_interaction(run, configuration, req);
|
|
88
88
|
},
|
|
89
89
|
},
|
|
90
90
|
},
|
|
@@ -93,8 +93,9 @@ module.exports = {
|
|
|
93
93
|
/*
|
|
94
94
|
TODO
|
|
95
95
|
|
|
96
|
-
-embedding retrieval
|
|
97
|
-
-
|
|
98
|
-
-
|
|
96
|
+
-embedding retrieval test
|
|
97
|
+
-promote triggers to skills (optional user confirm)
|
|
98
|
+
-sql access
|
|
99
|
+
-memory
|
|
99
100
|
|
|
100
101
|
*/
|
package/package.json
CHANGED
|
@@ -25,7 +25,11 @@ class RetrievalByEmbedding {
|
|
|
25
25
|
table.name
|
|
26
26
|
}${
|
|
27
27
|
table.description ? ` (${table.description})` : ""
|
|
28
|
-
} for documents related to a search phrase or a question
|
|
28
|
+
} for documents related to a search phrase or a question.${
|
|
29
|
+
this.add_sys_prompt
|
|
30
|
+
? ` Additional information for the ${this.toolName} tool: ${this.add_sys_prompt}`
|
|
31
|
+
: ""
|
|
32
|
+
}`;
|
|
29
33
|
}
|
|
30
34
|
}
|
|
31
35
|
|
|
@@ -33,7 +37,6 @@ class RetrievalByEmbedding {
|
|
|
33
37
|
const allTables = await Table.find();
|
|
34
38
|
const tableOpts = [];
|
|
35
39
|
const relation_opts = {};
|
|
36
|
-
const list_view_opts = {};
|
|
37
40
|
for (const table of allTables) {
|
|
38
41
|
table.fields
|
|
39
42
|
.filter((f) => f.type?.name === "PGVector")
|
|
@@ -44,8 +47,6 @@ class RetrievalByEmbedding {
|
|
|
44
47
|
.filter((f) => f.is_fkey)
|
|
45
48
|
.map((f) => f.name);
|
|
46
49
|
relation_opts[relNm] = ["", ...fkeys];
|
|
47
|
-
|
|
48
|
-
list_view_opts[relNm] = []
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
return [
|
|
@@ -73,20 +74,22 @@ class RetrievalByEmbedding {
|
|
|
73
74
|
required: true,
|
|
74
75
|
attributes: { calcOptions: ["vec_field", relation_opts] },
|
|
75
76
|
},
|
|
76
|
-
|
|
77
|
-
name: "
|
|
78
|
-
label: "
|
|
77
|
+
{
|
|
78
|
+
name: "hidden_fields",
|
|
79
|
+
label: "Hide fields",
|
|
79
80
|
type: "String",
|
|
80
|
-
|
|
81
|
-
calcOptions: ["vec_field", list_view_opts],
|
|
82
|
-
},
|
|
81
|
+
sublabel: "Comma-separated list of fields to hide from the prompt",
|
|
83
82
|
},
|
|
84
83
|
{
|
|
85
|
-
name: "
|
|
86
|
-
label: "
|
|
84
|
+
name: "limit",
|
|
85
|
+
label: "Limit",
|
|
86
|
+
sublabel: "Max number of rows to find",
|
|
87
|
+
type: "String",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "add_sys_prompt",
|
|
91
|
+
label: "Additional prompt",
|
|
87
92
|
type: "String",
|
|
88
|
-
sublabel:
|
|
89
|
-
"Use handlebars (<code>{{ }}</code>) to access fields in the retrieved rows",
|
|
90
93
|
},
|
|
91
94
|
];
|
|
92
95
|
}
|
|
@@ -103,28 +106,51 @@ class RetrievalByEmbedding {
|
|
|
103
106
|
: table0;
|
|
104
107
|
return {
|
|
105
108
|
type: "function",
|
|
106
|
-
process({ phrase_or_question }) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
109
|
+
process: async ({ phrase_or_question }) => {
|
|
110
|
+
const [table_name, field_name] = this.vec_field.split(".");
|
|
111
|
+
const table = Table.findOne({ name: table_name });
|
|
112
|
+
if (!table)
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Table ${table_name} not found in vector_similarity_search action`
|
|
115
|
+
);
|
|
116
|
+
const embedF = getState().functions.llm_embedding;
|
|
117
|
+
const opts = {};
|
|
118
|
+
const qembed = await embedF.run(phrase_or_question, opts);
|
|
119
|
+
const selLimit = +(this.limit || 10);
|
|
120
|
+
const rows = await table.getRows(
|
|
121
|
+
{},
|
|
122
|
+
{
|
|
123
|
+
orderBy: {
|
|
124
|
+
operator: "nearL2",
|
|
125
|
+
field: field_name,
|
|
126
|
+
target: JSON.stringify(qembed),
|
|
127
|
+
},
|
|
128
|
+
limit: this.doc_relation ? 5 * selLimit : selLimit,
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
rows.forEach((r) => {
|
|
132
|
+
delete r[field_name];
|
|
133
|
+
});
|
|
134
|
+
if (!rows.length)
|
|
135
|
+
return {
|
|
136
|
+
response:
|
|
137
|
+
"There are no documents related to: " + phrase_or_question,
|
|
138
|
+
};
|
|
139
|
+
else if (!this.doc_relation) return { rows };
|
|
140
|
+
else {
|
|
141
|
+
const relField = table.getField(this.doc_relation);
|
|
142
|
+
const relTable = Table.findOne(relField.reftable_name);
|
|
143
|
+
const ids = [];
|
|
144
|
+
rows.forEach((vrow) => {
|
|
145
|
+
if (ids.length < selLimit) ids.push(vrow[this.doc_relation]);
|
|
146
|
+
});
|
|
147
|
+
const docsUnsorted = await relTable.getRows({ id: { in: ids } });
|
|
148
|
+
//ensure order
|
|
149
|
+
const docs = ids
|
|
150
|
+
.map((id) => docsUnsorted.find((d) => d.id == id))
|
|
151
|
+
.filter(Boolean);
|
|
152
|
+
return { rows: docs };
|
|
126
153
|
}
|
|
127
|
-
return div({ class: "border border-success p-2 m-2" }, response);
|
|
128
154
|
},
|
|
129
155
|
function: {
|
|
130
156
|
name: this.toolName,
|
package/skills/FTSRetrieval.js
CHANGED
|
@@ -23,7 +23,13 @@ class RetrievalByFullTextSearch {
|
|
|
23
23
|
|
|
24
24
|
systemPrompt() {
|
|
25
25
|
if (this.mode === "Tool")
|
|
26
|
-
return `Use the ${this.toolName} tool to search the ${
|
|
26
|
+
return `Use the ${this.toolName} tool to search the ${
|
|
27
|
+
this.table_name
|
|
28
|
+
} database by a search phrase which will locate rows where any field match that query.${
|
|
29
|
+
this.add_sys_prompt
|
|
30
|
+
? ` Additional information for the ${this.toolName} tool: ${this.add_sys_prompt}`
|
|
31
|
+
: ""
|
|
32
|
+
}`;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
static async configFields() {
|
|
@@ -68,6 +74,11 @@ class RetrievalByFullTextSearch {
|
|
|
68
74
|
type: "String",
|
|
69
75
|
sublabel: "Comma-separated list of fields to hide from the prompt",
|
|
70
76
|
},
|
|
77
|
+
{
|
|
78
|
+
name: "add_sys_prompt",
|
|
79
|
+
label: "Additional prompt",
|
|
80
|
+
type: "String",
|
|
81
|
+
},
|
|
71
82
|
/*{
|
|
72
83
|
name: "contents_expr",
|
|
73
84
|
label: "Contents string",
|