@saltcorn/copilot 0.7.3 → 0.7.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.
@@ -76,16 +76,6 @@ class GeneratePageSkill {
76
76
  };
77
77
  }
78
78
  provideTools = () => {
79
- let properties = {};
80
- (this.toolargs || []).forEach((arg) => {
81
- properties[arg.name] = {
82
- description: arg.description,
83
- type: arg.argtype,
84
- };
85
- if (arg.options && arg.argtype === "string")
86
- properties[arg.name].enum = arg.options.split(",").map((s) => s.trim());
87
- });
88
-
89
79
  return {
90
80
  type: "function",
91
81
  process: async ({ name, existing_page_name }) => {
@@ -0,0 +1,198 @@
1
+ const Table = require("@saltcorn/data/models/table");
2
+ const View = require("@saltcorn/data/models/view");
3
+ const { fieldProperties } = require("../common");
4
+ const { getState } = require("@saltcorn/data/db/state");
5
+ const {
6
+ div,
7
+ pre,
8
+ code,
9
+ a,
10
+ text,
11
+ escape,
12
+ iframe,
13
+ text_attr,
14
+ } = require("@saltcorn/markup/tags");
15
+
16
+ class GenerateViewSkill {
17
+ static skill_name = "Generate View";
18
+
19
+ get skill_label() {
20
+ return "Generate View";
21
+ }
22
+
23
+ constructor(cfg) {
24
+ Object.assign(this, cfg);
25
+ }
26
+
27
+ async systemPrompt() {
28
+ return `If the user asks to generate a view, use the generate_view tool to enter
29
+ a view generation mode. The tool call only requires high-level details to start this sequence.`;
30
+ }
31
+
32
+ get userActions() {
33
+ return {
34
+ async build_copilot_view_gen({
35
+ wfctx,
36
+ name,
37
+ viewpattern,
38
+ table,
39
+ min_role,
40
+ }) {
41
+ await View.create({
42
+ name,
43
+ viewtemplate: viewpattern,
44
+ table: Table.findOne({ name: table }),
45
+ min_role: { admin: 1, public: 100, user: 80 }[min_role],
46
+ configuration: wfctx,
47
+ });
48
+ setTimeout(() => getState().refresh_views(), 200);
49
+ return {
50
+ notify: `View saved: <a target="_blank" href="/view/${name}">${name}</a>`,
51
+ };
52
+ },
53
+ };
54
+ }
55
+
56
+ provideTools = () => {
57
+ const state = getState();
58
+ const vts = state.viewtemplates;
59
+ const tables = state.tables;
60
+ const all_vt_names = Object.keys(vts);
61
+ const enabled_vt_names = all_vt_names.filter(
62
+ (vtnm) =>
63
+ vts[vtnm].enable_copilot_viewgen ||
64
+ vts[vtnm].copilot_generate_view_prompt,
65
+ );
66
+ //const roles = await User.get_roles();
67
+ const tableless = enabled_vt_names.filter(
68
+ (vtnm) => vts[vtnm].tableless === true,
69
+ );
70
+ const parameters = {
71
+ type: "object",
72
+ required: ["name", "viewpattern"],
73
+ properties: {
74
+ name: {
75
+ description: `The name of the view, this should be a short name which is part of the url. `,
76
+ type: "string",
77
+ },
78
+ viewpattern: {
79
+ description: `The type of view to generate. Some of the view descriptions: ${enabled_vt_names.map((vtnm) => `${vtnm}: ${vts[vtnm].description}.`).join(" ")}`,
80
+ type: "string",
81
+ enum: enabled_vt_names,
82
+ },
83
+ table: {
84
+ description:
85
+ "Which table is this a view on. These viewpatterns are tablesless, do not supply a tablename: " +
86
+ tableless.join(", "),
87
+ type: "string",
88
+ enum: tables.map((t) => t.name),
89
+ },
90
+ min_role: {
91
+ description:
92
+ "The minimum role needed to access the view. For views accessible only by admin, use 'admin', pages with min_role 'public' is publicly accessible and also available to all users",
93
+ type: "string",
94
+ enum: ["admin", "user", "public"],
95
+ },
96
+ },
97
+ };
98
+
99
+ return {
100
+ type: "function",
101
+ function: {
102
+ name: "generate_view",
103
+ description:
104
+ "Generate a view by supplying high-level details. This will trigger a view generation sequence",
105
+ parameters,
106
+ },
107
+ process: async (input) => {
108
+ return "Metadata received";
109
+ },
110
+ postProcess: async ({ tool_call, req, generate }) => {
111
+ const state = getState();
112
+ const vt = state.viewtemplates[tool_call.input.viewpattern];
113
+ const table =
114
+ vt.tableless === true
115
+ ? null
116
+ : Table.findOne({ name: tool_call.input.table });
117
+ const flow = vt.configuration_workflow(req);
118
+ const wfctx = { viewname: tool_call.input.name, table_id: table?.id };
119
+ let vt_prompt = "";
120
+ if (vt.copilot_generate_view_prompt) {
121
+ if (typeof vt.copilot_generate_view_prompt === "string")
122
+ vt_prompt = vt.copilot_generate_view_prompt;
123
+ else if (typeof vt.copilot_generate_view_prompt === "function")
124
+ vt_prompt = await vt.copilot_generate_view_prompt(tool_call.input);
125
+ }
126
+
127
+ for (const step of flow.steps) {
128
+ const form = await step.form(wfctx);
129
+ const properties = {};
130
+ //TODO onlyWhen
131
+ for (const field of form.fields) {
132
+ //TODO showIf
133
+ properties[field.name] = {
134
+ description:
135
+ field.copilot_description ||
136
+ `${field.label}.${field.sublabel ? ` ${field.sublabel}` : ""}`,
137
+ ...fieldProperties(field),
138
+ };
139
+ }
140
+
141
+ const answer = await generate(
142
+ `${vt_prompt ? vt_prompt + "\n\n" : ""}Now generate the ${step.name} details of the view by calling the generate_view_details tool`,
143
+ {
144
+ tools: [
145
+ {
146
+ type: "function",
147
+ function: {
148
+ name: "generate_view_details",
149
+ description: "Provide view details",
150
+ parameters: {
151
+ type: "object",
152
+ properties,
153
+ },
154
+ },
155
+ },
156
+ ],
157
+ tool_choice: {
158
+ type: "function",
159
+ function: {
160
+ name: "generate_view_details",
161
+ },
162
+ },
163
+ },
164
+ );
165
+ const tc = answer.getToolCalls()[0];
166
+ Object.assign(wfctx, tc.input);
167
+ }
168
+ const view = new View({
169
+ name: tool_call.input.name,
170
+ viewtemplate: tool_call.input.viewpattern,
171
+ table,
172
+ min_role: { admin: 1, public: 100, user: 80 }[
173
+ tool_call.input.min_role
174
+ ],
175
+ configuration: wfctx,
176
+ });
177
+ const runres = await view.run({}, { req });
178
+ return {
179
+ stop: true,
180
+ add_response:
181
+ pre(JSON.stringify(wfctx, null, 2)) +
182
+ div(
183
+ { style: { maxHeight: 800, maxWidth: 500, overflow: "scroll" } },
184
+ runres,
185
+ ),
186
+ add_user_action: {
187
+ name: "build_copilot_view_gen",
188
+ type: "button",
189
+ label: "Save view " + tool_call.input.name,
190
+ input: { wfctx, ...tool_call.input },
191
+ },
192
+ };
193
+ },
194
+ };
195
+ };
196
+ }
197
+
198
+ module.exports = GenerateViewSkill;
@@ -45,6 +45,7 @@ const get_agent_view = () => {
45
45
  { skill_type: "Generate Page" },
46
46
  { skill_type: "Database design" },
47
47
  { skill_type: "Generate Workflow" },
48
+ { skill_type: "Generate View" },
48
49
  ],
49
50
  },
50
51
  });
package/index.js CHANGED
@@ -24,6 +24,7 @@ module.exports = {
24
24
  require("./agent-skills/pagegen.js"),
25
25
  require("./agent-skills/database-design.js"),
26
26
  require("./agent-skills/workflow.js"),
27
+ require("./agent-skills/viewgen.js"),
27
28
  ],
28
29
  },
29
30
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/copilot",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "AI assistant for building Saltcorn applications",
5
5
  "main": "index.js",
6
6
  "dependencies": {