diff --git a/frontend/src/features/admin-form/create/builder-and-design/MagicFormBuilderModal/MagicFormBuilderModalContent/PdfDetailsScreen.tsx b/frontend/src/features/admin-form/create/builder-and-design/MagicFormBuilderModal/MagicFormBuilderModalContent/PdfDetailsScreen.tsx index c51f7f1ea8..396141daa0 100644 --- a/frontend/src/features/admin-form/create/builder-and-design/MagicFormBuilderModal/MagicFormBuilderModalContent/PdfDetailsScreen.tsx +++ b/frontend/src/features/admin-form/create/builder-and-design/MagicFormBuilderModal/MagicFormBuilderModalContent/PdfDetailsScreen.tsx @@ -86,8 +86,6 @@ export const MagicFormBuilderPdfDetailsScreen = (): JSX.Element => { return combinedText.trim() } - const isError = pdfFileText.length > 3000 - return ( <> @@ -97,7 +95,7 @@ export const MagicFormBuilderPdfDetailsScreen = (): JSX.Element => { - + Upload a PDF - The PDF should not be a scanned copy and should not contain any restricted or sensitive information. @@ -105,7 +103,7 @@ export const MagicFormBuilderPdfDetailsScreen = (): JSX.Element => { { - if (pdfFileText && pdfFileText.length < 3001) return true + if (pdfFileText) return true return 'This PDF file cannot be processed. Please ensure that the PDF uploaded contains selectable text and is not a scanned copy.' }, }} @@ -127,15 +125,11 @@ export const MagicFormBuilderPdfDetailsScreen = (): JSX.Element => { /> - {(isError || !pdfFileText) && ( - - )} + {!pdfFileText && } {!pdfFileText && (pdfFile ? `${errors.pdfFileText?.message}` : 'Please upload a PDF.')} - {isError && - `The PDF uploaded exceeds the character limit acceptable (${pdfFileText.length}/3000). Please try another form.`} diff --git a/src/app/modules/form/admin-form/admin-form.assistance.constants.ts b/src/app/modules/form/admin-form/admin-form.assistance.constants.ts index 921435bfc7..c35006f106 100644 --- a/src/app/modules/form/admin-form/admin-form.assistance.constants.ts +++ b/src/app/modules/form/admin-form/admin-form.assistance.constants.ts @@ -6,3 +6,5 @@ export enum Roles { export const MODEL_TYPE = 'gpt-3.5-turbo' export const sampleFormFields = `[{"title":"","description":"","required":"","disabled":"","fieldType":"section"},{"ValidationOptions":{"selectedValidation":null,"customVal":null},"allowPrefill":"","lockPrefill":"","title":"","description":"","required":"","disabled":"","fieldType":"textfield"},{"ValidationOptions":{"selectedValidation":null,"customVal":null},"title":"","description":"","required":"","disabled":"","fieldType":"textarea"},{"fieldOptions":[""],"othersRadioButton":"","title":"","description":"","required":"","disabled":"","fieldType":"radiobutton"},{"ValidationOptions":{"customMax":null,"customMin":null},"fieldOptions":[""],"othersRadioButton":"","validateByValue":"","title":"","description":"","required":"","disabled":"","fieldType":"checkbox"},{"fieldOptions":[""],"title":"","description":"","required":"","disabled":"","fieldType":"dropdown"},{"title":"","description":"","required":"","disabled":"","fieldType":"yes_no"},{"ratingOptions":{"steps":5,"shape":"Star"},"title":"","description":"","required":"","disabled":"","fieldType":"rating"},{"autoReplyOptions":{"hasAutoReply":"","autoReplySubject":"","autoReplySender":"","autoReplyMessage":"","includeFormSummary":""},"isVerifiable":"","hasAllowedEmailDomains":false,"allowedEmailDomains":[],"title":"","description":"","required":"","disabled":"","fieldType":"email"},{"allowIntlNumbers":"","isVerifiable":"","title":"","description":"","required":"","disabled":"","fieldType":"mobile"},{"allowIntlNumbers":"","title":"","description":"","required":"","disabled":"","fieldType":"homeno"},{"dateValidation":{"customMinDate":null,"customMaxDate":null,"selectedDateValidation":null},"invalidDays":[],"title":"","description":"","required":"","disabled":"","fieldType":"date"},{"addMoreRows":"","title":"","description":"","required":"","disabled":"","fieldType":"table","columns":[{"ValidationOptions":{"customVal":null,"selectedValidation":null},"allowPrefill":"","lockPrefill":"","columnType":"textfield","required":"","title":""}],"minimumRows":2,"maximumRows":null},{"title":"","description":"","required":"","disabled":"","fieldType":"attachment","attachmentSize":"1"},{"title":"","description":"","required":"","disabled":"","fieldType":"number","ValidationOptions":{"LengthValidationOptions":{"customVal":null,"selectedLengthValidation":null},"RangeValidationOptions":{"customMin":null,"customMax":null},"selectedValidation":null}},{"ValidationOptions":{"customMax":null,"customMin":null},"validateByValue":"","title":"","description":"","required":"","disabled":"","fieldType":"decimal"},{"title":"","description":"","required":"","disabled":"","fieldType":"nric"},{"title":"","description":"","required":"","disabled":"","fieldType":"uen"}]` + +export const fieldTypes = `'section','textfield','textarea','radiobutton','checkbox','dropdown','yes_no','rating','email','mobile','homeno','date','table','attachment','number','decimal','nric','uen'` diff --git a/src/app/modules/form/admin-form/admin-form.assistance.controller.ts b/src/app/modules/form/admin-form/admin-form.assistance.controller.ts index ff2bba3d7c..4536d956b6 100644 --- a/src/app/modules/form/admin-form/admin-form.assistance.controller.ts +++ b/src/app/modules/form/admin-form/admin-form.assistance.controller.ts @@ -21,10 +21,9 @@ const generateQuestionsSchema = Joi.object({ type: Joi.string() .valid(...Object.values(ContentTypes)) .required(), - content: Joi.alternatives().conditional('type', { + content: Joi.string().when('type', { is: ContentTypes.PROMPT, then: Joi.string().max(300).required(), - otherwise: Joi.string().max(3000).required(), // max character of 3000 to cater for PDF uploads }), }) diff --git a/src/app/modules/form/admin-form/admin-form.assistance.service.ts b/src/app/modules/form/admin-form/admin-form.assistance.service.ts index 351b4ad23e..eeed1e2480 100644 --- a/src/app/modules/form/admin-form/admin-form.assistance.service.ts +++ b/src/app/modules/form/admin-form/admin-form.assistance.service.ts @@ -111,7 +111,10 @@ export const generateFormFields = ( > => { const messages: ChatRequestMessage[] = [ { role: Roles.SYSTEM, content: schemaPromptBuilder(sampleFormFields) }, - { role: Roles.USER, content: formFieldsPromptBuilder(questions) }, + { + role: Roles.USER, + content: formFieldsPromptBuilder(questions, sampleFormFields), + }, ] return ResultAsync.fromPromise( azureOpenAi.getChatCompletions(deploymentId, messages), @@ -132,6 +135,7 @@ export const generateFormFields = ( }, ).andThen((chatCompletions) => { const { message } = chatCompletions.choices[0] + // const {tokenUsage} = chatCompletions.usage?.totalTokens? if (!message) { return errAsync(new AssistanceConnectionError()) } diff --git a/src/app/modules/form/admin-form/admin-form.assistance.utils.ts b/src/app/modules/form/admin-form/admin-form.assistance.utils.ts index f7f29b191f..1db29af812 100644 --- a/src/app/modules/form/admin-form/admin-form.assistance.utils.ts +++ b/src/app/modules/form/admin-form/admin-form.assistance.utils.ts @@ -1,4 +1,5 @@ // type guard for OpenAIError +import { fieldTypes } from './admin-form.assistance.constants' import { OpenAIError } from './admin-form.assistance.types' export function isOpenAIError(error: unknown): error is OpenAIError { @@ -24,12 +25,12 @@ export const schemaPromptBuilder = (schema: string) => { export const questionListPromptBuilder = (purpose: string) => { return `I am a public officer who wants to create a form that collects ${purpose}. - Give me a list of content / questions I should have in my form built with this form builder, in the form of "${expectedQuestionsListFormat}".` + Give me a list of content / questions I should have in my form built with this form builder, in the form of "${expectedQuestionsListFormat}", where must follow the category of types within ${fieldTypes}. Do not create the question if the does not exist in ${fieldTypes}.` } -export const formFieldsPromptBuilder = (questions: string) => { +export const formFieldsPromptBuilder = (questions: string, schema: string) => { return `Help me generate a form with the following list of questions: ${questions} - Present the questions as FormSG form fields in JSON (list of form field schemas), in the form of "${expectedFormFieldSchemaFormat}" as defined by the system, without any code blocks. Format the JSON as a single line.` + Provide the questions as FormSG form fields in JSON format (with the following keys: ${schema}), in the form of "${expectedFormFieldSchemaFormat}" as defined by the system, without any code blocks. Format the JSON as a single line. Ensure the JSON generated only contain fieldTypes of types ${fieldTypes}. Do not create any fieldTypes which are not ${fieldTypes}. Replace values in <> with actual primitive values. Do not build the fieldType if a path required is not available.` } export const migratePromptBuilder = (parsedContent: string) => { return `Help me generate the corresponding JSON form fields from content parsed from a PDF document.