@saltcorn/copilot 0.4.0 → 0.4.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/package.json +1 -1
- package/user-copilot.js +114 -22
package/package.json
CHANGED
package/user-copilot.js
CHANGED
|
@@ -68,10 +68,12 @@ const configuration_workflow = (req) =>
|
|
|
68
68
|
viewtemplate: "Show",
|
|
69
69
|
});
|
|
70
70
|
show_view_opts[t.name] = views.map((v) => v.name);
|
|
71
|
-
const lviews = await View.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
const lviews = await View.find_table_views_where(
|
|
72
|
+
t.id,
|
|
73
|
+
({ state_fields, viewrow }) =>
|
|
74
|
+
viewrow.viewtemplate !== "Edit" &&
|
|
75
|
+
state_fields.every((sf) => !sf.required)
|
|
76
|
+
);
|
|
75
77
|
list_view_opts[t.name] = lviews.map((v) => v.name);
|
|
76
78
|
}
|
|
77
79
|
return new Form({
|
|
@@ -193,11 +195,26 @@ const run = async (table_id, viewname, config, state, { res, req }) => {
|
|
|
193
195
|
);
|
|
194
196
|
if (action) {
|
|
195
197
|
const row = JSON.parse(tool_call.function.arguments);
|
|
198
|
+
if (Object.keys(row || {}).length)
|
|
199
|
+
interactMarkups.push(
|
|
200
|
+
wrapSegment(
|
|
201
|
+
wrapCard(
|
|
202
|
+
action.trigger_name,
|
|
203
|
+
pre(JSON.stringify(row, null, 2))
|
|
204
|
+
),
|
|
205
|
+
"Copilot"
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
} else if (tool_call.function.name.startsWith("Query")) {
|
|
209
|
+
const query = JSON.parse(tool_call.function.arguments);
|
|
210
|
+
const queryText = query.sql_id_query
|
|
211
|
+
? query.sql_id_query
|
|
212
|
+
: JSON.stringify(query, null, 2);
|
|
196
213
|
interactMarkups.push(
|
|
197
214
|
wrapSegment(
|
|
198
215
|
wrapCard(
|
|
199
|
-
|
|
200
|
-
pre(
|
|
216
|
+
"Query " + tool_call.function.name.replace("Query", ""),
|
|
217
|
+
pre(queryText)
|
|
201
218
|
),
|
|
202
219
|
"Copilot"
|
|
203
220
|
)
|
|
@@ -233,11 +250,7 @@ const run = async (table_id, viewname, config, state, { res, req }) => {
|
|
|
233
250
|
wrapSegment(
|
|
234
251
|
wrapCard(
|
|
235
252
|
interact.name,
|
|
236
|
-
pre(
|
|
237
|
-
interact.name.startsWith("Query")
|
|
238
|
-
? JSON.stringify(JSON.parse(interact.content), null, 2)
|
|
239
|
-
: JSON.stringify(interact.content, null, 2)
|
|
240
|
-
)
|
|
253
|
+
pre(JSON.stringify(JSON.parse(interact.content), null, 2))
|
|
241
254
|
),
|
|
242
255
|
"Copilot"
|
|
243
256
|
)
|
|
@@ -482,7 +495,10 @@ const getCompletionArguments = async (config) => {
|
|
|
482
495
|
];
|
|
483
496
|
} else Object.assign(fieldProperties(field), properties[field.name]);
|
|
484
497
|
});
|
|
485
|
-
|
|
498
|
+
properties.sql_id_query = {
|
|
499
|
+
type: "string",
|
|
500
|
+
description: `An SQL query for this table's primary keys (${table.pk_name}). This must select only the primary keys, for example SELECT ${table.name[0]}."${table.pk_name}" from "${table.name}" ${table.name[0]} JOIN ... where... Use this to join other tables in the database. If you use sql_id_query, use it alone, do not combine with other field queries. Any other fields you want to filter on must be accessed in the WHERE clause`,
|
|
501
|
+
};
|
|
486
502
|
tools.push({
|
|
487
503
|
type: "function",
|
|
488
504
|
function: {
|
|
@@ -527,9 +543,39 @@ const getCompletionArguments = async (config) => {
|
|
|
527
543
|
},
|
|
528
544
|
});
|
|
529
545
|
}
|
|
546
|
+
const tables = (await Table.find({})).filter(
|
|
547
|
+
(t) => !t.external && !t.provider_name
|
|
548
|
+
);
|
|
549
|
+
const schemaPrefix = db.getTenantSchemaPrefix();
|
|
530
550
|
const systemPrompt =
|
|
531
551
|
"You are helping users retrieve information and perform actions on a relational database" +
|
|
532
|
-
config.sys_prompt
|
|
552
|
+
config.sys_prompt +
|
|
553
|
+
`
|
|
554
|
+
If you are generating SQL, Your database the following tables in PostgreSQL:
|
|
555
|
+
|
|
556
|
+
` +
|
|
557
|
+
tables
|
|
558
|
+
.map(
|
|
559
|
+
(t) => `CREATE TABLE "${t.name}" (${
|
|
560
|
+
t.description
|
|
561
|
+
? `
|
|
562
|
+
/* ${t.description} */`
|
|
563
|
+
: ""
|
|
564
|
+
}
|
|
565
|
+
${t.fields
|
|
566
|
+
.map(
|
|
567
|
+
(f) =>
|
|
568
|
+
` "${f.name}" ${
|
|
569
|
+
f.primary_key && f.type?.name === "Integer"
|
|
570
|
+
? "SERIAL PRIMARY KEY"
|
|
571
|
+
: f.sql_type.replace(schemaPrefix, "")
|
|
572
|
+
}`
|
|
573
|
+
)
|
|
574
|
+
.join(",\n")}
|
|
575
|
+
)`
|
|
576
|
+
)
|
|
577
|
+
.join(";\n\n");
|
|
578
|
+
//console.log("sysprompt", systemPrompt);
|
|
533
579
|
|
|
534
580
|
if (tools.length === 0) tools = undefined;
|
|
535
581
|
return { tools, systemPrompt };
|
|
@@ -636,12 +682,13 @@ const process_interaction = async (
|
|
|
636
682
|
if (action) {
|
|
637
683
|
const trigger = Trigger.findOne({ name: action.trigger_name });
|
|
638
684
|
const row = JSON.parse(tool_call.function.arguments);
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
685
|
+
if (Object.keys(row || {}).length)
|
|
686
|
+
responses.push(
|
|
687
|
+
wrapSegment(
|
|
688
|
+
wrapCard(action.trigger_name, pre(JSON.stringify(row, null, 2))),
|
|
689
|
+
"Copilot"
|
|
690
|
+
)
|
|
691
|
+
);
|
|
645
692
|
const result = await trigger.runWithoutRow({ user: req.user, row });
|
|
646
693
|
console.log("ran trigger with result", {
|
|
647
694
|
name: trigger.name,
|
|
@@ -649,7 +696,10 @@ const process_interaction = async (
|
|
|
649
696
|
result,
|
|
650
697
|
});
|
|
651
698
|
|
|
652
|
-
if (
|
|
699
|
+
if (
|
|
700
|
+
(typeof result === "object" && Object.keys(result || {}).length) ||
|
|
701
|
+
typeof result === "string"
|
|
702
|
+
) {
|
|
653
703
|
responses.push(
|
|
654
704
|
wrapSegment(
|
|
655
705
|
wrapCard(
|
|
@@ -667,7 +717,10 @@ const process_interaction = async (
|
|
|
667
717
|
role: "tool",
|
|
668
718
|
tool_call_id: tool_call.id,
|
|
669
719
|
name: tool_call.function.name,
|
|
670
|
-
content:
|
|
720
|
+
content:
|
|
721
|
+
result && typeof result !== "string"
|
|
722
|
+
? JSON.stringify(result)
|
|
723
|
+
: result || "Action run",
|
|
671
724
|
},
|
|
672
725
|
],
|
|
673
726
|
});
|
|
@@ -676,7 +729,46 @@ const process_interaction = async (
|
|
|
676
729
|
name: tool_call.function.name.replace("Query", ""),
|
|
677
730
|
});
|
|
678
731
|
const query = JSON.parse(tool_call.function.arguments);
|
|
679
|
-
|
|
732
|
+
let result;
|
|
733
|
+
if (query.sql_id_query) {
|
|
734
|
+
const is_sqlite = db.isSQLite;
|
|
735
|
+
|
|
736
|
+
const client = is_sqlite ? db : await db.getClient();
|
|
737
|
+
await client.query(`BEGIN;`);
|
|
738
|
+
if (!is_sqlite) {
|
|
739
|
+
await client.query(
|
|
740
|
+
`SET LOCAL search_path TO "${db.getTenantSchema()}";`
|
|
741
|
+
);
|
|
742
|
+
await client.query(
|
|
743
|
+
`SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;`
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const { rows } = await client.query(query.sql_id_query);
|
|
748
|
+
await client.query(`ROLLBACK;`);
|
|
749
|
+
|
|
750
|
+
if (!is_sqlite) client.release(true);
|
|
751
|
+
result = await table.getRows(
|
|
752
|
+
{ [table.pk_name]: { in: rows.map((r) => r[table.pk_name]) } },
|
|
753
|
+
{
|
|
754
|
+
forUser: req.user,
|
|
755
|
+
forPublic: !req.user,
|
|
756
|
+
}
|
|
757
|
+
);
|
|
758
|
+
responses.push(
|
|
759
|
+
wrapSegment(
|
|
760
|
+
wrapCard(
|
|
761
|
+
"Query " + tool_call.function.name.replace("Query", ""),
|
|
762
|
+
pre(query.sql_id_query)
|
|
763
|
+
),
|
|
764
|
+
"Copilot"
|
|
765
|
+
)
|
|
766
|
+
);
|
|
767
|
+
} else
|
|
768
|
+
result = await table.getRows(query, {
|
|
769
|
+
forUser: req.user,
|
|
770
|
+
forPublic: !req.user,
|
|
771
|
+
});
|
|
680
772
|
await addToContext(run, {
|
|
681
773
|
interactions: [
|
|
682
774
|
{
|