Looking for a real person to speak to?

We're here for you! Just drop in your queries below and we'll connect with you as soon as we can.

", "re_fillable": false, "use_captcha": false, "redirect_url": null, "submitted_text": "

Great, we've received your message. We'll get back to you as soon as we can :)

", "uppercase_labels": false, "submit_button_text": "Submit", "re_fill_button_text": "Fill Again", "color": "#64748b" } ``` The form properties can only have one of the following types: 'text', 'number', 'select', 'multi_select', 'date', 'files', 'checkbox', 'url', 'email', 'phone_number', 'signature'. All form properties objects need to have the keys 'help', 'name', 'type', 'hidden', 'placeholder', 'prefill'. The placeholder property is optional (can be "null") and is used to display a placeholder text in the input field. The help property is optional (can be "null") and is used to display extra information about the field. For the type "select" and "multi_select", the input object must have a key "select" (or "multi_select") that's mapped to an object like this one: ```json { "options": [ {"name": 1, "value": 1}, {"name": 2, "value": 2}, {"name": 3, "value": 3}, {"name": 4, "value": 4} ] } ``` For numerical rating inputs, use a "number" type input and set the property "is_rating" to "true" to turn it into a star rating input. Ex: ```json { "name":"How would you rate your overall experience?", "type":"number", "is_rating": true } ``` If the form is too long, you can paginate it by adding a page break block in the list of properties: ```json { "name":"Page Break", "next_btn_text":"Next", "previous_btn_text":"Previous", "type":"nf-page-break", } ``` If you need to add more context to the form, you can add text blocks: ```json { "name":"My Text", "type":"nf-text", "content": "

This is a text block.

" } ``` Give me the valid JSON object only, representing the following form: "[REPLACE]" Do not ask me for more information about required properties or types, only suggest me a form structure. EOD; const FORM_DESCRIPTION_PROMPT = <<setAiModel('gpt-3.5-turbo-16k') ->useStreaming() ->setSystemMessage('You are an assistant helping to generate forms.'); $completer->completeChat([ ["role" => "user", "content" => Str::of(self::FORM_STRUCTURE_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString()] ], 6000); $formData = $completer->getArray(); $formDescriptionPrompt = Str::of(self::FORM_DESCRIPTION_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString(); $formShortDescription = $completer->completeChat([ ["role" => "user", "content" => Str::of(self::FORM_SHORT_DESCRIPTION_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString()] ])->getString(); // If description is between quotes, remove quotes $formShortDescription = Str::of($formShortDescription)->replaceMatches('/^"(.*)"$/', '$1')->toString(); // Get industry & types $industry = $this->getIndustries($completer, $this->argument('prompt')); $types = $this->getTypes($completer, $this->argument('prompt')); // Get Related Templates $relatedTemplates = $this->getRelatedTemplates($industry, $types); // Now get description and QAs $formDescription = $completer->completeChat([ ["role" => "user", "content" => $formDescriptionPrompt] ])->getHtml(); $formCoverKeywords = $completer->completeChat([ ["role" => "user", "content" => $formDescriptionPrompt], ["role" => "assistant", "content" => $formDescription], ["role" => "user", "content" => self::FORM_IMG_KEYWORDS_PROMPT] ])->getArray(); $imageUrl = $this->getImageCoverUrl($formCoverKeywords['search_query']); $formQAs = $completer->completeChat([ ["role" => "user", "content" => $formDescriptionPrompt], ["role" => "assistant", "content" => $formDescription], ["role" => "user", "content" => self::FORM_QAS_PROMPT] ])->getArray(); $formTitle = $completer->completeChat([ ["role" => "user", "content" => $formDescriptionPrompt], ["role" => "assistant", "content" => $formDescription], ["role" => "user", "content" => self::FORM_TITLE_PROMPT] ])->getString(); $template = $this->createFormTemplate( $formData, $formTitle, $formDescription, $formShortDescription, $formQAs, $imageUrl, $industry, $types, $relatedTemplates ); $this->info('/form-templates/' . $template->slug); // Set reverse related Templates $this->setReverseRelatedTemplates($template); return Command::SUCCESS; } /** * Get an image cover URL for the template using unsplash API */ private function getImageCoverUrl($searchQuery): ?string { $url = 'https://api.unsplash.com/search/photos?query=' . urlencode($searchQuery) . '&client_id=' . config('services.unsplash.access_key'); $response = Http::get($url)->json(); $photoIndex = rand(0, max(count($response['results']) - 1, 10)); if (isset($response['results'][$photoIndex]['urls']['regular'])) { return Str::of($response['results'][$photoIndex]['urls']['regular'])->replace('w=1080', 'w=600')->toString(); } return null; } private function getIndustries(GptCompleter $completer, string $formPrompt): array { $industriesString = Template::getAllIndustries()->pluck('slug')->join(', '); return $completer->completeChat([ ["role" => "user", "content" => Str::of(self::FORM_INDUSTRY_PROMPT) ->replace('[REPLACE]', $formPrompt) ->replace('[INDUSTRIES]', $industriesString) ->toString()] ])->getArray(); } private function getTypes(GptCompleter $completer, string $formPrompt): array { $typesString = Template::getAllTypes()->pluck('slug')->join(', '); return $completer->completeChat([ ["role" => "user", "content" => Str::of(self::FORM_TYPES_PROMPT) ->replace('[REPLACE]', $formPrompt) ->replace('[TYPES]', $typesString) ->toString()] ])->getArray(); } private function getRelatedTemplates(array $industries, array $types): array { $templateScore = []; Template::chunk(100, function ($otherTemplates) use ($industries, $types, &$templateScore) { foreach ($otherTemplates as $otherTemplate) { $industryOverlap = count(array_intersect($industries ?? [], $otherTemplate->industry ?? [])); $typeOverlap = count(array_intersect($types ?? [], $otherTemplate->types ?? [])); $score = $industryOverlap + $typeOverlap; if ($score > 1) { $templateScore[$otherTemplate->slug] = $score; } } }); arsort($templateScore); // Sort by Score return array_slice(array_keys($templateScore), 0, self::MAX_RELATED_TEMPLATES); } private function createFormTemplate( array $formData, string $formTitle, string $formDescription, string $formShortDescription, array $formQAs, ?string $imageUrl, array $industry, array $types, array $relatedTemplates ) { // Add property uuids, improve form with options foreach ($formData['properties'] as &$property) { $property['id'] = Str::uuid()->toString(); // Column ID // Fix ratings if ($property['type'] == 'number' && ($property['is_rating'] ?? false)) { $property['rating_max_value'] = 5; } if (($property['type'] == 'select' && count($property['select']['options']) <= 4) || ($property['type'] == 'multi_select' && count($property['multi_select']['options']) <= 4)) { $property['without_dropdown'] = true; } } // Clean data $formTitle = Str::of($formTitle)->replace('"', '')->toString(); return Template::create([ 'name' => $formTitle, 'description' => $formDescription, 'short_description' => $formShortDescription, 'questions' => $formQAs, 'structure' => $formData, 'image_url' => $imageUrl, 'publicly_listed' => true, 'industries' => $industry, 'types' => $types, 'related_templates' => $relatedTemplates ]); } private function setReverseRelatedTemplates(Template $newTemplate) { if (!$newTemplate || count($newTemplate->related_templates) === 0) return; $templates = Template::whereIn('slug', $newTemplate->related_templates)->get(); foreach ($templates as $template) { if (count($template->related_templates) < self::MAX_RELATED_TEMPLATES) { $template->update(['related_templates' => array_merge($template->related_templates, [$newTemplate->slug])]); } } } }