Generator Form Schema Service
The GeneratorFormSchemaService dynamically builds the generator form schema based on the active plugin pipeline and enabled plugins. It powers the frontend form that users interact with when configuring a generation run.
Source: packages/agent/src/services/generator-form-schema.service.ts
Overview
The generator form is not static -- it changes based on which pipeline plugin is selected and which data source plugins are enabled. This service resolves the complete form schema by querying the plugin registry, collecting form field definitions, and determining available providers for each capability category.
Dependencies
constructor(
private readonly pluginRegistry: PluginRegistryService,
@Optional() private readonly workPluginRepository?: WorkPluginRepository,
@Optional() private readonly pluginSettingsService?: PluginSettingsService,
)
- PluginRegistryService -- Central registry of all installed plugins and their capabilities.
- WorkPluginRepository -- Tracks which plugins are enabled/active per work.
- PluginSettingsService -- Resolves plugin settings and checks required configuration.
Core Concepts
GeneratorFormSchema
The complete form schema returned to the frontend:
interface GeneratorFormSchema {
resolvedPipelineId: string | undefined;
providers: Record<string, ProviderOption[]>;
pluginFields: FormFieldDefinition[];
pluginGroups?: FormFieldGroup[];
handledConfigFields: readonly string[];
defaultValues?: Record<string, unknown>;
}
ProviderOption
Each selectable provider in the form:
interface ProviderOption {
id: string;
name: string;
description: string;
configured: boolean;
isDefault: boolean;
icon?: string;
}
Getting the Form Schema
The primary method getFormSchema() orchestrates the full schema resolution:
const schema = await formSchemaService.getFormSchema(
'agent-pipeline', // pipelineId (optional)
{ workId, userId } // scope options
);
Resolution Steps
- Resolve pipeline plugin -- Determines which pipeline plugin to use (see resolution order below).
- Get available providers -- For each capability category (AI, search, screenshot, content extraction, pipeline), queries the registry for loaded, enabled, non-supplementary plugins.
- Filter by pipeline -- If the pipeline declares
selectableProviderCategories, categories not in the list are emptied. - Collect pipeline form fields -- If the pipeline implements
FormSchemaProvider, retrievesgetFormFields(),getFormGroups(),handledConfigFields, andgetDefaultValues(). - Collect additional form fields -- From enabled
FORM_SCHEMA_PROVIDERplugins (excluding pipelines), deduplicating by field name. - Merge and return -- Combines all fields, groups, and default values into the final schema.
Pipeline Resolution Order
The service resolves the pipeline plugin through a priority chain:
| Priority | Source | Description |
|---|---|---|
| 1 | Explicit pipelineId parameter | User-selected pipeline |
| 2 | Work's activeCapability for 'pipeline' | Work-level default |
| 3 | Plugin with defaultForCapabilities: ['pipeline'] | System-level default |
| 4 | First loaded pipeline plugin | Fallback |
Provider Filtering
Providers are filtered based on scope:
- Enable status -- Checked via
pluginRegistry.isPluginEnabledForScope()which considers Work > User > autoEnable hierarchy. - Supplementary flag -- Plugins marked as
supplementary(e.g., notion-extractor) are excluded from selectable lists; they activate via URL matching. - Plugin state -- Only
loadedplugins are included.
Default Provider Detection
A provider is marked as isDefault when:
- It matches the work's
activeCapabilityentry for that category, OR - Its manifest includes the capability in
defaultForCapabilities, OR - It is a
systemPlugin(fallback).
Validating Form Values
const result = await formSchemaService.validateFormValues(pipelineId, formValues, { workId, userId });
// result: { valid: true } or { valid: false, errors: [...] }
Validation runs in two phases:
- Pipeline validation -- Calls
validateFormInput()on the pipeline plugin. - Data source plugin validation -- For each enabled
FORM_SCHEMA_PROVIDERplugin, validates the nested values undervalues[pluginId].
Processing Form Configuration
The processFormConfig() method transforms raw form values into structured configuration:
const { config, pluginConfig } = await formSchemaService.processFormConfig(pipelineId, rawFormValues, {
workId,
userId
});
Transformation Steps
- Pipeline transform -- If the pipeline implements
transformFormValues(), it processes the full config first. - Plugin transforms -- Each enabled
FORM_SCHEMA_PROVIDERplugin transforms the config to extract its nested key. - Separation -- Plugin-specific config (nested under
pluginIdkeys) is extracted intopluginConfig, leaving flat config inconfig.
Output Structure
{
config: {
// Flat configuration values (prompt, generation settings, etc.)
max_search_queries: 10,
ai_first_generation_enabled: true,
},
pluginConfig: {
// Per-plugin nested configuration
'notion-extractor': { database_id: 'abc123' },
'custom-scorer': { weight_threshold: 0.8 },
},
}
Validating Plugin Configuration
The validateFormSchemaPlugins() method ensures all enabled form-schema-provider plugins are properly configured:
await formSchemaService.validateFormSchemaPlugins({ workId, userId });
For each enabled plugin:
- Checks if required settings are configured via
getResolvedSettings(). - Validates
requiredfields from the JSON Schema. - Validates
x-requiredGroups-- at least one field in each group must have a value. - Settings with
x-envVar(withoutx-secret) are considered auto-configured.
Throws BadRequestException with a list of errors if any plugins are misconfigured.
Validating Selected Providers
await formSchemaService.validateSelectedProviders({ ai: 'openai', search: 'exa' }, { workId, userId });
Validates each provider through the chain: exists -> loaded -> enabled -> configured. If a provider field is empty, validates the default provider for that category instead.
Plugin Configuration Check
The isPluginConfigured() method determines if a plugin has all required settings:
// Checks:
// 1. Required fields from schema.required[]
// 2. Required groups from schema['x-requiredGroups'][]
// 3. Skips fields with x-envVar (environment-provided)
This enables the frontend to show "not configured" badges on providers that need setup.