Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/nsbradford/TalkFormAI into …
Browse files Browse the repository at this point in the history
…main
  • Loading branch information
hbrooks committed Sep 5, 2023
2 parents 17d119c + 1298908 commit fa9cc51
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 130 deletions.
2 changes: 1 addition & 1 deletion src/pages/contact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const teamMembers = [
{
name: 'Nick Bradford',
image: 'https://avatars.githubusercontent.com/u/6633811',
subtitle: 'Building LLM agents',
subtitle: 'Building LLM coding agents',
website: 'nsbradford.com',
socials: {
twitter: 'https://twitter.com/n_s_bradford',
Expand Down
142 changes: 91 additions & 51 deletions src/pages/forms/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function NewFormPage(props: NewFormPageProps) {
const supabase = createClientComponentClient<Database>();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [step, setStep] = useState(1);
const [description, setDescription] = useState('');
// const [description, setDescription] = useState('');
const [formTopic, setFormTopic] = useState('');
const [title, setTitle] = useState('');
const [fieldsGuidance, setFieldsGuidance] = useState('');
Expand Down Expand Up @@ -65,7 +65,7 @@ export default function NewFormPage(props: NewFormPageProps) {
const formId = v4();
await supabase.from('forms').insert([
{
description: description,
description: formTopic, // we're deleting the intermediate description
raw_instructions: formTopic,
fields_guidance: fieldsGuidance,
fields_schema: fieldsSchema,
Expand Down Expand Up @@ -101,24 +101,55 @@ export default function NewFormPage(props: NewFormPageProps) {

console.log('titleResponse', titleResponse);
conversationThread.push(titleResponse);
conversationThread.push({
role: 'user',
content: `Create a short description of this survey form. This description will be given to a person who is in charge of administering the form data collection. This person will use this description to understand what the form is about, so that they can collect the correct information from respondents. Don't include information that is not relevant to the form, or state that this is a form. This survey administrator already knows that, instead they care about information relation the forms content. For example, if you want to collect a respondent\'s name and age, you can write: "This form is to collect the names and ages of people attending a birthday party."`,
});
const descriptionResponse = await callLLM(PROMPT_BUILD, conversationThread);
if (descriptionResponse instanceof Error) {
console.error('No response from LLM');
setStep(1);
return;
}
setDescription(removeStartAndEndQuotes(descriptionResponse.content) || '');
// conversationThread.push({
// role: 'user',
// content: `Create a short description of this survey form. This description will be given to a person who is in charge of administering the form data collection. This person will use this description to understand what the form is about, so that they can collect the correct information from respondents. Don't include information that is not relevant to the form, or state that this is a form. This survey administrator already knows that, instead they care about information relation the forms content. For example, if you want to collect a respondent\'s name and age, you can write: "This form is to collect the names and ages of people attending a birthday party."`,
// });
// const descriptionResponse = await callLLM(PROMPT_BUILD, conversationThread);
// if (descriptionResponse instanceof Error) {
// console.error('No response from LLM');
// setStep(1);
// return;
// }
// setDescription(removeStartAndEndQuotes(descriptionResponse.content) || '');

console.log('descriptionResponse', descriptionResponse);
conversationThread.push(descriptionResponse);
// console.log('descriptionResponse', descriptionResponse);
// conversationThread.push(descriptionResponse);
conversationThread.push({
role: 'user',
content:
'Write any guidance that will help respondents fill out this form, such as conditional information to collect or how to answer questions. The survey administrator will reference this guidence when deciding which follow up questions to ask or how to interpret the answers. For example, if you want to collect a RSVP for a birthday party, including the number of guests attending, you may write "Please include the number of guests attending the birthday party, but only if the RSVP is yes." because it doesn\'t make sense to ask for the number of guests if the respondent is not attending.',
content: `Now, we must write instructions to the survey administrator. Write any guidance that the administrator will need, such as conditional information to collect or how to answer questions. The survey administrator will reference this guidence when deciding which follow up questions to ask or how to interpret the answers. Be concise.
Examples:
-If you want to collect a RSVP for a birthday party, including the number of guests attending, you may write "Please include the number of guests attending the birthday party (skip if RSVP is no)." because it doesn\'t make sense to ask for the number of guests if the respondent is not attending.
-If a question is a Yes/No question, or has to pass certain validation checks, make a note, e.g. "RSVP: yes/no".
Example instructions:
----
Movie Night RSVP
- Name: string (double check with user if it doesn't look like a real name)
- Email: valid email address
- RSVP: yes/no
If RSVP is yes:
- Number of guests: number (double check with user if more than 10)
- Dietary restrictions: string
----
----
Startup marketing survey
- Name: string (double check with user if it doesn't look like a real name)
- Email: valid email address
- Company name: string
- Job title: string
If job title is some kind of software engineer or related (ML engineer, data scientist, engineering manager, etc.):
- GitHub username: string (just the username, not the full URL. Extract if necessary.)
All respondents:
- Marketing technologies: string (comma separated list of marketing technologies used)
----
`,
});

const fieldsGuidanceResponse = await callLLM(
Expand All @@ -138,8 +169,18 @@ export default function NewFormPage(props: NewFormPageProps) {
conversationThread.push(fieldsGuidanceResponse);
conversationThread.push({
role: 'user',
content:
'Now return a JSON object that will serve as a template for the form responses. This object should have keys that are strings and values that are strings. The keys are the names of the fields that the survey administrator should attempt to obtain. They values should be descriptions of those fields, including any formatting information or non-obvious ways to validate the provided information. For example, if you want to collect a respondent\'s name and age, you can write: {"name": "name of the respondent", "age": "age of the respondent"}',
content: `Now return a JSON object that will serve as a template for the form responses. This object should have keys that are strings and values that are strings. The keys are the names of the fields that the survey administrator should attempt to obtain. They values should be descriptions of those fields, including any formatting information or non-obvious ways to validate the provided information. Keys should be in \`snake_case\`. Be concise.
Examples:
Input:
- GitHub username: GitHub username. Extract if necessary, e.g. if user provides URL.
Your output:
{
"github_username": "GitHub username. Extract if necessary, e.g. if user provides URL."
}
`,
});

const fieldsSchemaResponse = await callLLM(
Expand Down Expand Up @@ -191,54 +232,47 @@ export default function NewFormPage(props: NewFormPageProps) {
</div>
</div>
);
} /* if (step === 1.5) {
return (
<div className="flex flex-col items-center justify-center space-y-2">
<Spinner />
<div className="text-gray-600">{loadingMessage}</div>
</div>
);
} else if (step === 2) */ else {
} else {
return (
<div className="col-span-full px-4">
<div className="col-span-full px-4">
<div className="sm:col-span-4">
{title !== '' && (
{formTopic !== '' && (
<>
<label
htmlFor="title"
className="block text-sm font-medium leading-6 text-gray-900"
htmlFor="description"
className="block mt-0 text-md font-medium leading-6 text-gray-900"
>
Title
Your Description
</label>
<div className="mt-2">
<input
type="text"
id="title"
<textarea
id="description"
rows={5}
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Birthday party RSVP"
value={formTopic}
onChange={(e) => setFormTopic(e.target.value)}
placeholder="Adding a description can help us gather the correct information from respondents..."
/>
</div>
</>
)}

{description !== '' && (
{title !== '' && (
<>
<label
htmlFor="description"
className="block mt-4 text-sm font-medium leading-6 text-gray-900"
htmlFor="title"
className="block mt-6 text-md font-medium leading-6 text-gray-900"
>
Description (Optional)
Title
</label>
<div className="mt-2">
<textarea
id="description"
rows={5}
<input
type="text"
id="title"
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Adding a description can help us gather the correct information from respondents..."
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Birthday party RSVP"
/>
</div>
</>
Expand All @@ -248,9 +282,9 @@ export default function NewFormPage(props: NewFormPageProps) {
<>
<label
htmlFor="fields_guidance"
className="block mt-4 text-sm font-medium leading-6 text-gray-900"
className="block mt-6 text-md font-medium leading-6 text-gray-900"
>
Guidance (Optional)
Guidance
</label>
<div className="mt-2">
<textarea
Expand All @@ -269,7 +303,13 @@ export default function NewFormPage(props: NewFormPageProps) {
<>
<div>
<div className="mt-4">
Fields Schema:
<label
htmlFor="fields_guidance"
className="block mt-6 text-md font-medium leading-6 text-gray-900"
>
Fields Schema:
</label>

<pre className="bg-gray-100 p-3 rounded whitespace-pre-wrap text-sm">
{JSON.stringify(fieldsSchema, null, 2)}
</pre>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function GetStartedButtons() {
View on GitHub
</Link>
<Link
href="/forms/fill/bb16be91-9ba7-40b8-8e3d-ead3cf3184ca"
href="/forms/fill/5771953d-a003-4969-9071-fcfff4c5bb10"
className={`inline-block text-gray-500 border border-2 border-gray-500 font-semibold py-2 px-4 my-2 mx-1 rounded-lg transition duration-200 ease-in-out hover:bg-gray-200`}
>
Fill a sample form
Expand Down
53 changes: 4 additions & 49 deletions src/prompts.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Form } from '@/types';

export const PROMPT_BUILD = `You are FormGPT, a helpful, honest, and harmless AI assistant helping gather information from users and using it to populate forms. First, we need to create a form.`;
export const PROMPT_BUILD = `You are TalkForm AI, a helpful, honest, and harmless AI assistant helping gather information from users and using it to populate forms. First, we need to create a form.`;

export function PROMPT_FILL(form: Form) {
return `You are FormGPT, a helpful, honest, and harmless AI assistant helping gather information from users and using it to populate forms. Your job: Given a form schema to populate, continue your conversation with the user until you have enough information to fill out the form.
return `You are TalkForm AI, a helpful, honest, and harmless AI assistant helping gather information from users and using it to populate forms. Your job: Given a form schema to populate, continue your conversation with the user until you have enough information to fill out the form.
You must respond with a JSON blob with the following format:
Expand Down Expand Up @@ -35,11 +35,12 @@ RULES YOU MUST FOLLOW:
- ALWAYS start by introducing yourself and immediately asking about the first field in the form. You can assume the user is ready to start.
- The questions should be ordered logically. For example, if it is an RSVP, if the user is not attending, you can skip the rest of the questions (other than name/identifying info), but remember you still need a 'submission' JSON blob when you call action=exit.
- Users might sometimes be uncertain about some fields; you can press a little, but you must ultimately respect their decision.
- Intelligently infer things based on the user inputs. For example, if you are asking for a GitHub username, and the user provides you with their GitHub URL, you can simply extract the username from the URL instead of asking the user for clarification. (Never tell the user you are doing this, though.)
YOUR SCHEMA:
Name: ${form.name}
Description: ${form.description || '[No description]'}
Guidence: ${form.fields_guidance || '[No guidance, use your best judgement]'}
Guidance: ${form.fields_guidance || '[No guidance, use your best judgement]'}
\`\`\`
${JSON.stringify(form.fields_schema, null, 2)}
\`\`\`
Expand All @@ -48,49 +49,3 @@ You MUST provide your response in JSON.
`;
}
// Movie-themed game night. Ask everyone their name, RSVP, favorite movie, dietary preferences, # of guests. Remind everyone to BYOB.

export const FAKE_SCHEMA = `
{
"rsvpForm": {
"meta": "RSVP for Sarah's bday dinner",
"fields": [
{
"label": "Full Name",
"type": "text",
"required": true
},
{
"label": "Email",
"type": "email",
"required": true
},
{
"label": "Contact Number",
"type": "number",
"required": false
},
{
"label": "Attending?",
"type": "checkbox",
"options": ["Yes", "No"],
"required": true
},
{
"label": "Number of Guests",
"type": "number",
"required": true
},
{
"label": "Dietary Restrictions",
"type": "textarea",
"required": false
},
{
"label": "Special Note",
"type": "textarea",
"required": false
}
]
}
}
`;
Loading

0 comments on commit fa9cc51

Please sign in to comment.