AI Builder
Generate forms from chat, then review the live result.
AI Builder turns a prompt into Formedible code, parses it locally, and shows the form beside the chat so teams can review it before shipping.
Public exports
Import AI builder pieces from the package root. It exports AIBuilder, parser pieces, provider helpers, TanStack AI adapters, storage helpers, and public types.
- AIBuilder, ProviderSelection, ParserSettings, ChatInterface, and AiFormRenderer are root exports.
- createDefaultProviderSettings, createDefaultProviderSecrets, providerOptions, and validateProviderAccess are exported from provider-selection.
- createTanStackTextAdapter, DEFAULT_TANSTACK_AI_MODELS, and SUPPORTED_TANSTACK_AI_PROVIDERS are exported from ai-adapters.
- Storage exports include readPersistedAIBuilderState, persistProviderSettings, persistProviderSecrets, persistConversations, exportConversation, and STORAGE_KEYS.
AIBuilder provider setup
AIBuilder can own provider state or accept controlled providerSettings and providerSecrets. It passes provider access, system prompt, parser config, messages, and form callbacks into ChatInterface.
- mode defaults to client.
- resolveInitialProviderAccess reads persisted settings, then chooses controlled secrets, stored secrets, or an empty key for the active provider.
- Provider settings are persisted only when providerSettings is not controlled.
- Provider secrets are persisted only when providerSecrets is not controlled.
- The preview side renders AiFormRenderer only after formCode exists.
| Property | Type | Default | Description |
|---|---|---|---|
mode | AIBuilderMode | 'client' | Only client mode is defined by AIBuilderMode. |
providerSettings | ProviderSettings | Persisted/default | Optional controlled provider, model, temperature, maxTokens, and Anthropic thinking budget. |
providerSecrets | ProviderSecrets | Stored/empty | Optional controlled provider key object paired with the selected provider. |
onFormGenerated | (formCode: string) => void | undefined | Called when AIBuilder stores the latest generated form code. |
onFormSubmit | (formData: FormedibleFormValues) => void | Promise<void> | undefined | Passed through to AiFormRenderer submit handling. |
Provider and model lists
Provider support lives in providerOptions, ai-adapters, and the provider model catalog fetcher.
- providerOptions contains openai, anthropic, and openrouter, all requiring keys.
- DEFAULT_TANSTACK_AI_MODELS sets openai to gpt-5.4-mini, anthropic to claude-sonnet-4-6, and openrouter to minimax/minimax-m2.7.
- createTanStackTextAdapter preserves custom model strings instead of falling back silently.
- Model catalogs are fetched from provider APIs, cached locally, and refreshed on demand.
- createTanStackModelOptions only returns Anthropic thinking options when thinkingBudgetTokens is positive.
| Provider | Default model | Model catalog source |
|---|---|---|
| openai | gpt-5.4-mini | GET https://api.openai.com/v1/models |
| anthropic | claude-sonnet-4-6 | GET https://api.anthropic.com/v1/models |
| openrouter | minimax/minimax-m2.7 | GET https://openrouter.ai/api/v1/models?output_modalities=text |
Provider validation and model options
Provider selection rejects mismatched secrets, unsupported providers, custom endpoints, non-Anthropic thinking budgets, and missing API keys.
- createDefaultProviderSettings picks the selected provider option and copies its defaultModel.
- validateProviderAccess requires settings and secrets to target the same provider.
- endpoint and baseURL keys are rejected by both validateProviderAccess and assertNoUnsupportedRuntimeOptions.
- thinkingBudgetTokens is accepted only for Anthropic provider settings.
Chat streaming and parser handoff
ChatInterface streams events into one assistant message. When the stream completes, it extracts a Formedible fence, parses it, stores parse errors on the message, and calls onFormGenerated.
- Enter submits the prompt and Shift+Enter keeps the newline because the handler only submits when event.key is Enter and shiftKey is false.
- streamAiResponse events are scheduled through createAiStreamScheduler before the assistant message is updated.
- extractFormCode runs only when the final status is completed.
- parseAiToFormedible writes formConfig on success and parseErrors on failure.
Parser integration and preview
AI Builder does not render generated text directly. parseAiToFormedible calls FormedibleParser.parseAiOutput, and AiFormRenderer passes parsed options into useFormedible.
- parseAiToFormedible forwards strictValidation and allowed key/type lists into FormedibleParser.parseAiOutput.
- inferDefaultValues fills booleans with false, number-like fields with 0, multiSelect/array with [], object/location with {}, and other fields with an empty string.
- AiFormRenderer calls onParseComplete after reparsing non-streaming code.
- Preview submits through the parsed onSubmit handler first, then through AIBuilder onFormSubmit.
Storage and export
AI Builder uses separate browser keys for provider settings, provider secrets, model catalogs, conversations, and UI state. Secret persistence can be memory, session, or local.
- STORAGE_KEYS names five independent storage entries, including the cached provider model catalogs.
- persistProviderSecrets clears both storage areas first; memory mode stores nothing.
- rememberKey false stores the preference but not the secret value.
- persistConversations writes sanitized conversations, and exportConversation returns a sanitized export envelope.
AI Builder shell path
AI generation returns the same field model used by hand-written forms.
import { useState } from 'react';
import { AIBuilder } from '@/components/ui/formedible/ai/ai-builder';
import { createDefaultProviderSecrets, createDefaultProviderSettings, ProviderSelection } from '@/components/ui/formedible/ai/provider-selection';
export function AiBuilderWorkspace() {
const [providerSettings, setProviderSettings] = useState(() => createDefaultProviderSettings('openrouter'));
const [providerSecrets, setProviderSecrets] = useState(() => createDefaultProviderSecrets('openrouter'));
return (
<main className="grid gap-8 lg:grid-cols-[22rem_1fr]">
<ProviderSelection settings={providerSettings} secrets={providerSecrets} onChange={(settings, secrets) => {
setProviderSettings(settings);
setProviderSecrets(secrets);
}} />
<AIBuilder
providerSettings={providerSettings}
providerSecrets={providerSecrets}
onProviderSettingsChange={setProviderSettings}
onProviderSecretsChange={setProviderSecrets}
/>
</main>
);
}