Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 101 additions & 33 deletions app-constructor/feedback-action.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
const { eval_expression } = require("@saltcorn/data/models/expression");
const MetaData = require("@saltcorn/data/models/metadata");
const Table = require("@saltcorn/data/models/table");
const View = require("@saltcorn/data/models/view");
const Trigger = require("@saltcorn/data/models/trigger");
const Page = require("@saltcorn/data/models/page");
const Plugin = require("@saltcorn/data/models/plugin");
const { interpolate } = require("@saltcorn/data/utils");
const { getState } = require("@saltcorn/data/db/state");
const { requirements_tool, task_tool } = require("./tools");
const { tool_choice } = require("./common");
const { getResearchAnswersText } = require("./research");
const {
saltcorn_description,
existing_tables_list,
existing_entities_list,
installed_plugins_list,
available_plugins_list,
task_planning_rules,
task_planning_closing,
research_answers_section,
} = require("./prompts");

module.exports = {
description: "Provide user feedback to the AppConstructor",
Expand Down Expand Up @@ -71,6 +86,7 @@ module.exports = {
title_field,
description_field,
url_field,
research_context,
},
}) => {
const use_title =
Expand All @@ -81,32 +97,46 @@ module.exports = {
: row[description_field];
const use_url =
mode === "workflow" ? interpolate(url, row, user) : row[url_field];
await MetaData.create({
type: "CopilotConstructMgr",
name: "feedback",
body: { title: use_title, description: use_description, url: use_url },
user_id: user?.id,
});
const spec = await MetaData.findOne({
type: "CopilotConstructMgr",
name: "spec",
});
if (!spec) return;

const researchText = await getResearchAnswersText();
const feedbackResearchSection = research_context
? `\nThe user also answered clarifying questions about this feedback:\n\n${research_context}\n`
: "";

let urlSection = "";
if (use_url) {
const mView = use_url.match(/\/view\/([^/?#]+)/);
const mPage = use_url.match(/\/page\/([^/?#]+)/);
if (mView)
urlSection =
`\nThe feedback was submitted from the Saltcorn view named "${mView[1]}"` +
` (URL: ${use_url}).\n`;
else if (mPage)
urlSection =
`\nThe feedback was submitted from the Saltcorn page named "${mPage[1]}"` +
` (URL: ${use_url}).\n`;
else
urlSection = `\nThe feedback was submitted from: ${use_url}\n`;
}

const reqAnswer = await getState().functions.llm_generate.run(
`The following application is being built:

Description: ${spec.body.description}
Audience: ${spec.body.audience}
Core features: ${spec.body.core_features}
Out of scope: ${spec.body.out_of_scope}
Visual style: ${spec.body.visual_style}

${spec.body.specification}
${research_answers_section(researchText)}
A new piece of feedback has come in from a user:

Title: ${use_title}
Description: ${use_description}
${urlSection}${feedbackResearchSection}
Now use the make_requirements tool to create a single or several (a single is preferred) new requirements that captures this new piece of feedback.

Now use the make_requirements tool to create a single or several (a single is prefered) new requirements that captures this new piece of feedback.
* Priority reflects how central the feature is to the core purpose of the application. Assign 5 to features without which the application cannot function at all, 3-4 to features that are important but not blocking, 1-2 to minor convenience features. Do not assign 5 to everything.
`,
{
tools: [requirements_tool],
Expand All @@ -116,43 +146,74 @@ Now use the make_requirements tool to create a single or several (a single is pr
},
);
const tc = reqAnswer.getToolCalls()[0];
console.log("gotr new requiremenrts", tc.input.requirements);
console.log("got new requiremenrts", tc.input.requirements);

const allReqs = await MetaData.find({
type: "CopilotConstructMgr",
name: "requirement",
});

for (const reqm of tc.input.requirements)
await MetaData.create({
type: "CopilotConstructMgr",
name: "requirement",
body: reqm,
body: { ...reqm, source: "feedback", feedback_title: use_title },
user_id: req.user?.id,
});

const taskAnswer = await getState().functions.llm_generate.run(
`The following application is being built:

Description: ${spec.body.description}
Audience: ${spec.body.audience}
Core features: ${spec.body.core_features}
Out of scope: ${spec.body.out_of_scope}
Visual style: ${spec.body.visual_style}
const tables = await Table.find({});
const tableById = Object.fromEntries(tables.map((t) => [t.id, t.name]));
const views = await View.find({});
const triggers = await Trigger.find({});
const pages = await Page.find({});
const entitiesSection = existing_entities_list({
views,
triggers,
pages,
tableById,
});
const installedPlugins = await Plugin.find({});
const installedNames = new Set(installedPlugins.map((p) => p.name));
let storePlugins = [];
try {
storePlugins = await Plugin.store_plugins_available();
} catch (_) {}
const installedPluginsSection = installed_plugins_list(installedNames);
const pluginsSection = available_plugins_list(storePlugins, installedNames);

This application will be implemented in Saltcorn, a database application development
environment.
const taskAnswer = await getState().functions.llm_generate.run(
`Generate implementation tasks for a new piece of feedback for this application:

${spec.body.specification}
${research_answers_section(researchText)}
A new piece of feedback has come in from a user:

Title: ${use_title}
Description: ${use_description}
${urlSection}${feedbackResearchSection}
The existing application requirements are:

A product manager has determined that the following requirements should be added to the list of application requirements:
${allReqs.map((r) => `* ${r.body.requirement}`).join("\n")}

A product manager has determined that the following new requirements should be added to implement this feedback:

${tc.input.requirements.map((r) => " * " + r.requirement).join("\n")}

Your plan for implementing this new fedback and requirements should not include any clarification or questions to the product owner. The
information you have been given so far is all that is available. Every step in the plan
should be immediately implementable in Saltcorn. You are writing the steps in the plan
for a person who is competent in using saltcorn but has no other business knowledge.
${saltcorn_description}

The database has already been built. The following tables are now present in the database:

${existing_tables_list(tables)}

The plan should outline continued development of the application on top of this database.
Your plan can add additional tables if needed or adjust the table fields, but normally the tables
should be designed optimally for this application.

${entitiesSection ? entitiesSection + "\n\n" : ""}${installedPluginsSection ? installedPluginsSection + "\n\n" : ""}${pluginsSection ? pluginsSection + "\n\n" : ""}${task_planning_rules}

Now use the plan_tasks tool to create the tasks to implement this new feedback
${task_planning_closing}

Now use the plan_tasks tool to create the tasks to implement this new feedback.
`,
{
tools: [task_tool],
Expand All @@ -168,8 +229,15 @@ Now use the plan_tasks tool to create the tasks to implement this new feedback
await MetaData.create({
type: "CopilotConstructMgr",
name: "task",
body: task,
body: { ...task, source: "feedback", feedback_title: use_title },
user_id: req.user?.id,
});

await MetaData.create({
type: "CopilotConstructMgr",
name: "feedback",
body: { title: use_title, description: use_description, url: use_url, research_context },
user_id: user?.id,
});
},
};
Loading