@saltcorn/copilot 0.5.3 → 0.6.0
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/index.js +1 -1
- package/package.json +1 -1
- package/page-gen-action.js +179 -0
package/index.js
CHANGED
|
@@ -2,7 +2,6 @@ const Workflow = require("@saltcorn/data/models/workflow");
|
|
|
2
2
|
const Form = require("@saltcorn/data/models/form");
|
|
3
3
|
const { features } = require("@saltcorn/data/db/state");
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
module.exports = {
|
|
7
6
|
sc_plugin_api_version: 1,
|
|
8
7
|
dependencies: ["@saltcorn/large-language-model"],
|
|
@@ -12,4 +11,5 @@ module.exports = {
|
|
|
12
11
|
functions: features.workflows
|
|
13
12
|
? { copilot_generate_workflow: require("./workflow-gen") }
|
|
14
13
|
: {},
|
|
14
|
+
actions: { copilot_generate_page: require("./page-gen-action") },
|
|
15
15
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
const { eval_expression } = require("@saltcorn/data/models/expression");
|
|
2
|
+
const { interpolate } = require("@saltcorn/data/utils");
|
|
3
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
4
|
+
const File = require("@saltcorn/data/models/file");
|
|
5
|
+
const Page = require("@saltcorn/data/models/page");
|
|
6
|
+
|
|
7
|
+
const GeneratePage = require("./actions/generate-page");
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
description: "Generate page with AI copilot",
|
|
11
|
+
configFields: ({ table, mode }) => {
|
|
12
|
+
if (mode === "workflow") {
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
name: "page_name",
|
|
16
|
+
label: "Page name",
|
|
17
|
+
sublabel:
|
|
18
|
+
"Leave blank to not save a Saltcorn page. Use interpolations {{ }} to access variables in the context",
|
|
19
|
+
type: "String",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "prompt_template",
|
|
23
|
+
label: "Prompt",
|
|
24
|
+
sublabel:
|
|
25
|
+
"Prompt text. Use interpolations {{ }} to access variables in the context",
|
|
26
|
+
type: "String",
|
|
27
|
+
fieldview: "textarea",
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "answer_field",
|
|
32
|
+
label: "Answer variable",
|
|
33
|
+
sublabel: "Optional. Set the generated HTML to this context variable",
|
|
34
|
+
type: "String",
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
// ...override_fields,
|
|
38
|
+
{
|
|
39
|
+
name: "model",
|
|
40
|
+
label: "Model",
|
|
41
|
+
sublabel: "Override default model name",
|
|
42
|
+
type: "String",
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
} else if (table) {
|
|
46
|
+
const textFields = table.fields
|
|
47
|
+
.filter((f) => f.type?.sql_name === "text")
|
|
48
|
+
.map((f) => f.name);
|
|
49
|
+
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
name: "prompt_field",
|
|
53
|
+
label: "Prompt field",
|
|
54
|
+
sublabel: "Field with the text of the prompt",
|
|
55
|
+
type: "String",
|
|
56
|
+
required: true,
|
|
57
|
+
attributes: { options: [...textFields, "Formula"] },
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "prompt_formula",
|
|
61
|
+
label: "Prompt formula",
|
|
62
|
+
type: "String",
|
|
63
|
+
showIf: { prompt_field: "Formula" },
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "answer_field",
|
|
67
|
+
label: "Answer field",
|
|
68
|
+
sublabel: "Output field will be set to the generated answer",
|
|
69
|
+
type: "String",
|
|
70
|
+
required: true,
|
|
71
|
+
attributes: { options: textFields },
|
|
72
|
+
},
|
|
73
|
+
// ...override_fields,
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
run: async ({
|
|
78
|
+
row,
|
|
79
|
+
table,
|
|
80
|
+
user,
|
|
81
|
+
mode,
|
|
82
|
+
configuration: {
|
|
83
|
+
page_name,
|
|
84
|
+
prompt_field,
|
|
85
|
+
prompt_formula,
|
|
86
|
+
prompt_template,
|
|
87
|
+
answer_field,
|
|
88
|
+
chat_history_field,
|
|
89
|
+
model,
|
|
90
|
+
},
|
|
91
|
+
}) => {
|
|
92
|
+
let prompt;
|
|
93
|
+
if (mode === "workflow") prompt = interpolate(prompt_template, row, user);
|
|
94
|
+
else if (prompt_field === "Formula" || mode === "workflow")
|
|
95
|
+
prompt = eval_expression(
|
|
96
|
+
prompt_formula,
|
|
97
|
+
row,
|
|
98
|
+
user,
|
|
99
|
+
"llm_generate prompt formula"
|
|
100
|
+
);
|
|
101
|
+
else prompt = row[prompt_field];
|
|
102
|
+
const opts = {};
|
|
103
|
+
|
|
104
|
+
if (model) opts.model = model;
|
|
105
|
+
const tools = [];
|
|
106
|
+
const systemPrompt = await GeneratePage.system_prompt();
|
|
107
|
+
tools.push({
|
|
108
|
+
type: "function",
|
|
109
|
+
function: {
|
|
110
|
+
name: GeneratePage.function_name,
|
|
111
|
+
description: GeneratePage.description,
|
|
112
|
+
parameters: await GeneratePage.json_schema(),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const { llm_generate } = getState().functions;
|
|
116
|
+
|
|
117
|
+
const initial_ans = await llm_generate.run(prompt, {
|
|
118
|
+
tools,
|
|
119
|
+
systemPrompt,
|
|
120
|
+
});
|
|
121
|
+
const initial_info = initial_ans.tool_calls[0].input;
|
|
122
|
+
const full = await GeneratePage.follow_on_generate(initial_info);
|
|
123
|
+
|
|
124
|
+
const page_html = await getState().functions.llm_generate.run(
|
|
125
|
+
`${prompt}.
|
|
126
|
+
|
|
127
|
+
The page title is: ${initial_info.title}.
|
|
128
|
+
Further page description: ${initial_info.description}.
|
|
129
|
+
|
|
130
|
+
Generate the HTML for the web page using the Bootstrap 5 CSS framework.
|
|
131
|
+
If you need to include the standard bootstrap CSS and javascript files, they are available as:
|
|
132
|
+
|
|
133
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
|
134
|
+
|
|
135
|
+
and
|
|
136
|
+
|
|
137
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
|
|
138
|
+
|
|
139
|
+
Just generate HTML code, do not wrap in markdown code tags`,
|
|
140
|
+
{
|
|
141
|
+
debugResult: true,
|
|
142
|
+
response_format: full.response_schema
|
|
143
|
+
? {
|
|
144
|
+
type: "json_schema",
|
|
145
|
+
json_schema: {
|
|
146
|
+
name: "generate_page",
|
|
147
|
+
schema: full.response_schema,
|
|
148
|
+
},
|
|
149
|
+
}
|
|
150
|
+
: undefined,
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const use_page_name = page_name ? interpolate(page_name, row, user) : "";
|
|
155
|
+
if (use_page_name) {
|
|
156
|
+
//save to a file
|
|
157
|
+
const file = await File.from_contents(
|
|
158
|
+
`${use_page_name}.html`,
|
|
159
|
+
"text/html",
|
|
160
|
+
page_html,
|
|
161
|
+
user.id,
|
|
162
|
+
100
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
//create page
|
|
166
|
+
await Page.create({
|
|
167
|
+
name: use_page_name,
|
|
168
|
+
title: initial_info.title,
|
|
169
|
+
description: initial_info.description,
|
|
170
|
+
min_role: 100,
|
|
171
|
+
layout: { html_file: file.path_to_serve },
|
|
172
|
+
});
|
|
173
|
+
getState().refresh_pages()
|
|
174
|
+
}
|
|
175
|
+
const upd = answer_field ? { [answer_field]: page_html } : {};
|
|
176
|
+
if (mode === "workflow") return upd;
|
|
177
|
+
else if (answer_field) await table.updateRow(upd, row[table.pk_name]);
|
|
178
|
+
},
|
|
179
|
+
};
|