Repository files navigation

synthdata-gen

Generate, validate, deduplicate, and export synthetic training data for LLM fine-tuning and evaluation.

npm versionnpm downloadslicensenodetypes


Description

synthdata-gen is a complete pipeline for producing synthetic training data. Define a schema describing the shape of each example, optionally plug in any LLM, and the library handles generation, output parsing, schema validation, quality heuristics, deduplication, and export to training-ready formats (OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, plain JSONL).

The library works in three modes:

  • Template-based generation -- no LLM required. A built-in deterministic generator produces examples matching your schema using seeded pseudo-random values. Useful for testing pipelines, prototyping schemas, and generating placeholder data.
  • LLM-based generation -- provide any async function that calls an LLM. The pipeline builds prompts from your schema, parses structured output from LLM responses (including JSON embedded in markdown fences), retries on failure, and tracks token usage and cost.
  • Custom generation -- provide your own generateFn callback for full control over how examples are produced, while still benefiting from the validation, dedup, and export stages.

Each pipeline stage (generation, validation, deduplication, export) is independently usable as a standalone function.


Installation

npm install synthdata-gen

Requires Node.js >= 18.


Quick Start

Template-based generation (no LLM required)

import{generate}from'synthdata-gen';importtype{ExampleSchema}from'synthdata-gen';constschema: ExampleSchema={fields: {instruction: {type: 'string',min: 10,max: 200,description: 'A clear instruction'},output: {type: 'string',min: 20,max: 1000,description: 'The expected response'},category: {type: 'enum',enum: ['coding','writing','reasoning']},},};constresult=awaitgenerate(schema,{count: 100});console.log(result.data);// GeneratedExample[]console.log(result.stats);// GenerationStats

LLM-based generation

import{generate}from'synthdata-gen';importtype{LlmFunction}from'synthdata-gen';constmyLlm: LlmFunction=async(messages,options)=>{constresponse=awaitcallMyProvider(messages,options);return{content: response.text,usage: {promptTokens: response.usage.prompt_tokens,completionTokens: response.usage.completion_tokens,totalTokens: response.usage.total_tokens,},};};constresult=awaitgenerate(schema,{llm: myLlm,count: 500,batchSize: 5,format: 'openai',seeds: [{instruction: 'Explain recursion',output: 'Recursion is when a function calls itself...',category: 'coding'},],costTracking: {promptTokenCost: 0.000003,completionTokenCost: 0.000015,currency: 'USD',},});console.log(result.exported);// OpenAI fine-tuning JSONL stringconsole.log(result.stats.cost);// { promptTokens, completionTokens, totalCost, currency }console.log(result.stats.durationMs);// wall-clock time

Features

  • Schema-driven generation -- define field types, constraints, descriptions, and required fields; the library compiles schemas into LLM prompts and validates output against them.
  • Three generation modes -- template-based (no LLM), LLM-based (any provider), or custom callback.
  • Robust LLM output parsing -- extracts JSON from bare responses, markdown code fences, and mixed text.
  • Schema validation -- type checking, string length constraints, numeric ranges, regex patterns, enum membership, array bounds, and nested object validation.
  • Quality heuristics -- detect empty fields, placeholder text (lorem ipsum, TODO, N/A), duplicate field values, and enforce minimum word counts.
  • Custom validators -- plug in arbitrary validation functions alongside built-in checks.
  • Three deduplication strategies -- exact match (normalized hash), near-duplicate (Jaccard similarity on n-grams), and semantic (cosine similarity on embeddings via a pluggable embedder).
  • Cross-set deduplication -- remove generated examples that overlap with an existing dataset.
  • Five export formats -- OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, and plain JSONL, with configurable field mappings.
  • Diversity controls -- temperature variation (linear, cycle, random), topic rotation, seed example rotation, negative example generation, and constraint variation.
  • Cost tracking -- track prompt and completion tokens, compute estimated cost per run.
  • Deterministic generation -- seeded PRNG for reproducible template-based output.
  • Full TypeScript support -- all types exported, strict mode compatible.

API Reference

generate(schema, options)

Main pipeline function. Generates examples, validates, deduplicates, and optionally exports.

functiongenerate(schema: ExampleSchema,options: GenerateOptions): Promise<GenerateResult>

Parameters:

ParameterTypeDescription
schemaExampleSchemaSchema defining the shape of each example
optionsGenerateOptionsPipeline configuration (see below)

GenerateOptions:

OptionTypeDefaultDescription
countnumberrequiredNumber of examples to generate
llmLlmFunctionundefinedAsync function that calls an LLM
generateFn(schema, batchIndex) => Record[]undefinedCustom generation callback
batchSizenumber1Examples per LLM call
systemPromptstringundefinedCustom system prompt (use {schema_description} placeholder)
additionalInstructionsstringundefinedExtra instructions appended to the system prompt
seedsRecord<string, unknown>[]undefinedFew-shot seed examples
diversityDiversityConfigundefinedDiversity strategy configuration
validationValidationConfigundefinedValidation and heuristics configuration
retryRetryConfig{ maxRetries: 3 }Retry configuration for LLM failures
dedupDedupOptions{ strategy: 'exact' }Deduplication configuration
invalidHandling'discard' | 'log' | 'repair''discard'How to handle invalid examples
structuredOutputbooleanundefinedRequest JSON mode from the LLM provider
costTrackingCostConfigundefinedToken cost tracking configuration
formatExportFormatundefinedExport format for the exported field in the result

Returns GenerateResult:

FieldTypeDescription
dataGeneratedExample[]Final validated, deduplicated examples
statsGenerationStatsPipeline statistics
exportedstring | undefinedFormatted output string (if format was specified)

validate(examples, schema, config?)

Validate an array of examples against a schema. Returns a ValidationResult for each example.

functionvalidate(examples: Record<string,unknown>[],schema: ExampleSchema,config?: ValidationConfig,): ValidationResult[]

Returns an array of:

interfaceValidationResult{valid: boolean;index: number;errors: ValidationError[];}interfaceValidationError{path: string[];message: string;code: string;}

Validation error codes:required, invalid_type, too_small, too_big, invalid_string, invalid_enum_value, heuristic_non_empty, heuristic_placeholder, heuristic_duplicate_fields, heuristic_min_words, global_min_length, global_max_length, custom_<name>.


validateExample(example, schema, config?)

Validate a single example. Returns an array of ValidationError objects (empty array means valid).

functionvalidateExample(example: Record<string,unknown>,schema: ExampleSchema,config?: ValidationConfig,): ValidationError[]

deduplicate(examples, options?)

Deduplicate an array of examples. Supports exact, near-duplicate, and semantic strategies.

functiondeduplicate(examples: Record<string,unknown>[],options?: Partial<DedupOptions>,): Promise<DedupResult>

DedupOptions:

OptionTypeDefaultDescription
strategy'exact' | 'near' | 'semantic' | 'none''exact'Deduplication strategy
thresholdnumber0.85 (near) / 0.92 (semantic)Similarity threshold for near/semantic dedup
ngramSizenumber2N-gram size for near-duplicate detection
fieldsstring[]all fieldsSubset of fields to compare
embedder(text: string) => Promise<number[]>undefinedEmbedding function (required for semantic strategy)
existingDataRecord<string, unknown>[]undefinedExisting dataset for cross-set deduplication

Returns DedupResult:

interfaceDedupResult{data: Record<string,unknown>[];// Deduplicated examplesremoved: number;// Number of duplicates removedpairs: Array<[number,number,number]>;// [indexA, indexB, similarity]}

exportAs(examples, format, options?)

Export examples to a training-ready format string.

functionexportAs(examples: Record<string,unknown>[],format: ExportFormat,options?: ExportOptions,): string

ExportFormat:'openai' | 'alpaca' | 'sharegpt' | 'csv' | 'jsonl'

ExportOptions:

OptionTypeDefaultDescription
fieldMapRecord<string, string>undefinedMap format roles to your field names
systemPromptstringundefinedStatic system prompt (OpenAI/ShareGPT formats)
delimiterstring','CSV column delimiter
quotestring'"'CSV quote character
headerbooleantrueInclude CSV header row
fieldsstring[]all fieldsSubset of fields to include

Individual Exporters

Each export format is available as a standalone function:

import{exportOpenAI,exportAlpaca,exportShareGPT,exportCSV,exportJSONL}from'synthdata-gen';
FunctionOutput format
exportOpenAI(examples, options?){"messages": [{"role": "system", ...}, {"role": "user", ...}, {"role": "assistant", ...}]} per line
exportAlpaca(examples, options?){"instruction": "...", "input": "...", "output": "..."} per line
exportShareGPT(examples, options?){"conversations": [{"from": "human", "value": "..."}, {"from": "gpt", "value": "..."}]} per line
exportCSV(examples, options?)Comma-separated values with header row
exportJSONL(examples, options?)One JSON object per line

All exporters automatically exclude _meta fields from output.


Generator Utilities

Low-level functions for template-based generation and prompt construction:

import{generateExample,generateExamples,buildSchemaPrompt,buildSystemPrompt,parseJsonResponse,}from'synthdata-gen';
FunctionDescription
generateExample(schema, seed?)Generate a single example from a schema using the built-in template generator. Deterministic when a seed is provided.
generateExamples(schema, count, baseSeed?)Generate multiple examples. Each example uses baseSeed + index for its seed.
buildSchemaPrompt(schema)Compile a schema into a natural-language prompt describing the expected JSON structure.
buildSystemPrompt(schema, customPrompt?, additionalInstructions?)Build the full system prompt for LLM-based generation. Supports a custom prompt template with {schema_description} placeholder.
parseJsonResponse(text)Extract JSON objects/arrays from an LLM response. Handles bare JSON, markdown code fences, and JSON embedded in explanatory text.

Configuration

Schema Definition

A schema defines the structure of each generated example using ExampleSchema:

constschema: ExampleSchema={fields: {question: {type: 'string',min: 10,max: 500,description: 'A natural language question'},answer: {type: 'string',min: 20,max: 2000,pattern: '^[A-Z]'},category: {type: 'enum',enum: ['science','history','technology']},difficulty: {type: 'integer',min: 1,max: 5},score: {type: 'number',min: 0,max: 100},active: {type: 'boolean'},tags: {type: 'array',items: {type: 'string'},min: 1,max: 5},metadata: {type: 'object',properties: {source: {type: 'string'},verified: {type: 'boolean'},},requiredFields: ['source'],},},required: ['question','answer','category'],};

Supported field types:

TypeSchemaField properties
stringmin (min length), max (max length), pattern (regex), description
numbermin, max, description
integermin, max, description
booleandescription
enumenum (valid values array), description
arrayitems (element schema), min (min items), max (max items), description
objectproperties (nested fields), requiredFields (required property names), description

All fields support required (default: true) and default (default value when field is omitted).


Validation Configuration

constconfig: ValidationConfig={// Global string field length constraintsminFieldLength: 10,maxFieldLength: 5000,// Quality heuristicsheuristics: {nonEmpty: true,// Reject empty/whitespace-only required string fieldsnoPlaceholder: true,// Reject placeholder text (lorem ipsum, TODO, TBD, N/A, etc.)noDuplicateFields: {// Reject examples where specified field pairs are identicalpairs: [['question','answer']],},minWordCount: {// Enforce minimum word count on specified fieldsfields: ['answer'],min: 5,},},// Custom validatorscustom: [{name: 'no-question-in-output',validate: (example)=>({valid: !String(example.answer).endsWith('?'),message: 'Answer should not end with a question mark',}),},],};

The noDuplicateFields and minWordCount heuristics also accept true to use automatic inference: noDuplicateFields: true pairs common field name patterns (question/answer, instruction/output, input/output, prompt/response, query/response), and minWordCount: true applies a default minimum of 3 words to all string fields.


Diversity Configuration

constdiversity: DiversityConfig={temperature: {min: 0.3,max: 1.2,strategy: 'cycle',// 'linear' | 'cycle' | 'random'},topics: ['algorithms','databases','networking','security'],negativeExampleRatio: 0.1,negativeInstructions: 'Generate an example with a subtle factual error.',constraintVariation: [{instruction: 'Write in a formal academic tone.'},{instruction: 'Write in a casual conversational tone.'},],};

Retry Configuration

constretry: RetryConfig={maxRetries: 3,// Maximum retry attempts per batchincludeFeedback: true,// Include validation error feedback in retry promptbackoff: 'exponential',// 'none' | 'linear' | 'exponential'backoffMs: 1000,// Base backoff delay in milliseconds};

Cost Tracking

constcostTracking: CostConfig={promptTokenCost: 0.000003,// Cost per prompt tokencompletionTokenCost: 0.000015,// Cost per completion tokencurrency: 'USD',};

The GenerationStats.cost field in the result contains promptTokens, completionTokens, totalCost, and currency.


Error Handling

Validation Errors

The validate and validateExample functions return structured error objects rather than throwing. Each ValidationError includes:

  • path -- array of field names locating the error (e.g., ['address', 'zip'] for nested fields, ['tags', '0'] for array elements)
  • message -- human-readable description of the failure
  • code -- machine-readable error code for programmatic handling
import{validateExample}from'synthdata-gen';consterrors=validateExample({instruction: 'Hi',output: 123,category: 'invalid'},schema,);for(consterroferrors){console.log(`[${err.code}] ${err.path.join('.')}: ${err.message}`);}// [too_small] instruction: String must contain at least 10 character(s), received 2// [invalid_type] output: Expected string, received number// [invalid_enum_value] category: Invalid enum value. Expected one of ["coding", ...], received "invalid"

Pipeline Error Handling

The generate function handles LLM failures internally using the retry configuration. Invalid examples are handled according to the invalidHandling option:

  • 'discard' (default) -- silently drops invalid examples
  • 'log' -- discards but records invalid examples and their errors in stats.invalidExamples
  • 'repair' -- includes invalid examples in the output with _meta.repaired: true

Validation error counts are always available in stats.validationErrors regardless of the handling mode.

Deduplication Errors

The deduplicate function throws an Error if the semantic strategy is used without providing an embedder function:

// Throws: "Semantic dedup requires an embedder function"awaitdeduplicate(examples,{strategy: 'semantic'});

Export Errors

The exportAs function throws an Error for unsupported format strings:

// Throws: "Unsupported export format: xml"exportAs(examples,'xml'asExportFormat);

Advanced Usage

Custom System Prompt

Override the default system prompt using the {schema_description} placeholder:

constresult=awaitgenerate(schema,{count: 100,llm: myLlm,systemPrompt: 'You are a medical expert generating training data.\n\n{schema_description}',additionalInstructions: 'All examples must be about cardiology.',});

Cross-Set Deduplication

Remove generated examples that duplicate entries in an existing dataset:

import{deduplicate}from'synthdata-gen';constresult=awaitdeduplicate(newExamples,{strategy: 'exact',existingData: existingDataset,});console.log(`Removed ${result.removed} duplicates of existing data`);

Semantic Deduplication

Provide an embedding function for meaning-level deduplication:

constresult=awaitdeduplicate(examples,{strategy: 'semantic',threshold: 0.92,embedder: async(text)=>{constresponse=awaitopenai.embeddings.create({model: 'text-embedding-3-small',input: text,});returnresponse.data[0].embedding;},});

Field-Specific Deduplication

Deduplicate based on a subset of fields:

constresult=awaitdeduplicate(examples,{strategy: 'near',threshold: 0.85,ngramSize: 2,fields: ['instruction'],// Only compare instruction fields});

Custom Field Mapping for Export

Map your schema fields to the roles expected by each export format:

import{exportOpenAI,exportAlpaca}from'synthdata-gen';constqaData=[{question: 'What is TCP?',answer: 'TCP is a connection-oriented protocol.'},];// Map question -> user, answer -> assistantconstopenai=exportOpenAI(qaData,{fieldMap: {user: 'question',assistant: 'answer'},systemPrompt: 'You are a networking expert.',});// Map question -> instruction, answer -> outputconstalpaca=exportAlpaca(qaData,{fieldMap: {instruction: 'question',output: 'answer'},});

Batch Generation with LLM

Request multiple examples per LLM call to reduce API costs:

constresult=awaitgenerate(schema,{llm: myLlm,count: 1000,batchSize: 10,// 10 examples per LLM callstructuredOutput: true,// Request JSON mode if provider supports it});

Standalone Template Generation

Use the template generator directly without the full pipeline:

import{generateExample,generateExamples}from'synthdata-gen';// Single example, deterministic with seedconstexample=generateExample(schema,42);// Multiple examples, deterministic with base seedconstexamples=generateExamples(schema,100,42);

Building Prompts for External Use

Generate the prompt that would be sent to an LLM, without calling one:

import{buildSchemaPrompt,buildSystemPrompt}from'synthdata-gen';constschemaPrompt=buildSchemaPrompt(schema);// "Generate a JSON object with the following structure:\n{ ... }"constsystemPrompt=buildSystemPrompt(schema,undefined,'Focus on edge cases.');// Full system prompt with schema description and additional instructions

Parsing LLM Responses

Extract JSON from messy LLM output:

import{parseJsonResponse}from'synthdata-gen';constobjects=parseJsonResponse('Here is the result:\n```json\n{"key": "value"}\n```\nDone!');// [{ key: "value" }]constarray=parseJsonResponse('[{"a": 1}, {"b": 2}]');// [{ a: 1 }, { b: 2 }]

TypeScript

All types are exported from the package entry point:

importtype{// LLM interfaceMessage,LlmCallOptions,LlmResponse,LlmFunction,// SchemaFieldType,SchemaField,ExampleSchema,// GenerationGeneratedExample,DiversityConfig,HeuristicsConfig,CustomValidator,ValidationConfig,RetryConfig,CostConfig,DedupOptions,GenerateOptions,ExportFormat,ExportOptions,// ResultsGenerateResult,GenerationStats,ValidationResult,ValidationError,DedupResult,}from'synthdata-gen';

The GeneratedExample<T> type is generic. By default it is Record<string, unknown> & { _meta?: ... }. You can narrow it with your own type:

interfaceQAPair{question: string;answer: string;category: string;}constresult=awaitgenerate(schema,{count: 10});constdata=result.dataasGeneratedExample<QAPair>[];

License

MIT

About

Generate and validate synthetic training data using any LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

synthdata-gen

Generate, validate, deduplicate, and export synthetic training data for LLM fine-tuning and evaluation.

npm versionnpm downloadslicensenodetypes


Description

synthdata-gen is a complete pipeline for producing synthetic training data. Define a schema describing the shape of each example, optionally plug in any LLM, and the library handles generation, output parsing, schema validation, quality heuristics, deduplication, and export to training-ready formats (OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, plain JSONL).

The library works in three modes:

  • Template-based generation -- no LLM required. A built-in deterministic generator produces examples matching your schema using seeded pseudo-random values. Useful for testing pipelines, prototyping schemas, and generating placeholder data.
  • LLM-based generation -- provide any async function that calls an LLM. The pipeline builds prompts from your schema, parses structured output from LLM responses (including JSON embedded in markdown fences), retries on failure, and tracks token usage and cost.
  • Custom generation -- provide your own generateFn callback for full control over how examples are produced, while still benefiting from the validation, dedup, and export stages.

Each pipeline stage (generation, validation, deduplication, export) is independently usable as a standalone function.


Installation

npm install synthdata-gen

Requires Node.js >= 18.


Quick Start

Template-based generation (no LLM required)

import{generate}from'synthdata-gen';importtype{ExampleSchema}from'synthdata-gen';constschema: ExampleSchema={fields: {instruction: {type: 'string',min: 10,max: 200,description: 'A clear instruction'},output: {type: 'string',min: 20,max: 1000,description: 'The expected response'},category: {type: 'enum',enum: ['coding','writing','reasoning']},},};constresult=awaitgenerate(schema,{count: 100});console.log(result.data);// GeneratedExample[]console.log(result.stats);// GenerationStats

LLM-based generation

import{generate}from'synthdata-gen';importtype{LlmFunction}from'synthdata-gen';constmyLlm: LlmFunction=async(messages,options)=>{constresponse=awaitcallMyProvider(messages,options);return{content: response.text,usage: {promptTokens: response.usage.prompt_tokens,completionTokens: response.usage.completion_tokens,totalTokens: response.usage.total_tokens,},};};constresult=awaitgenerate(schema,{llm: myLlm,count: 500,batchSize: 5,format: 'openai',seeds: [{instruction: 'Explain recursion',output: 'Recursion is when a function calls itself...',category: 'coding'},],costTracking: {promptTokenCost: 0.000003,completionTokenCost: 0.000015,currency: 'USD',},});console.log(result.exported);// OpenAI fine-tuning JSONL stringconsole.log(result.stats.cost);// { promptTokens, completionTokens, totalCost, currency }console.log(result.stats.durationMs);// wall-clock time

Features

  • Schema-driven generation -- define field types, constraints, descriptions, and required fields; the library compiles schemas into LLM prompts and validates output against them.
  • Three generation modes -- template-based (no LLM), LLM-based (any provider), or custom callback.
  • Robust LLM output parsing -- extracts JSON from bare responses, markdown code fences, and mixed text.
  • Schema validation -- type checking, string length constraints, numeric ranges, regex patterns, enum membership, array bounds, and nested object validation.
  • Quality heuristics -- detect empty fields, placeholder text (lorem ipsum, TODO, N/A), duplicate field values, and enforce minimum word counts.
  • Custom validators -- plug in arbitrary validation functions alongside built-in checks.
  • Three deduplication strategies -- exact match (normalized hash), near-duplicate (Jaccard similarity on n-grams), and semantic (cosine similarity on embeddings via a pluggable embedder).
  • Cross-set deduplication -- remove generated examples that overlap with an existing dataset.
  • Five export formats -- OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, and plain JSONL, with configurable field mappings.
  • Diversity controls -- temperature variation (linear, cycle, random), topic rotation, seed example rotation, negative example generation, and constraint variation.
  • Cost tracking -- track prompt and completion tokens, compute estimated cost per run.
  • Deterministic generation -- seeded PRNG for reproducible template-based output.
  • Full TypeScript support -- all types exported, strict mode compatible.

API Reference

generate(schema, options)

Main pipeline function. Generates examples, validates, deduplicates, and optionally exports.

functiongenerate(schema: ExampleSchema,options: GenerateOptions): Promise<GenerateResult>

Parameters:

ParameterTypeDescription
schemaExampleSchemaSchema defining the shape of each example
optionsGenerateOptionsPipeline configuration (see below)

GenerateOptions:

OptionTypeDefaultDescription
countnumberrequiredNumber of examples to generate
llmLlmFunctionundefinedAsync function that calls an LLM
generateFn(schema, batchIndex) => Record[]undefinedCustom generation callback
batchSizenumber1Examples per LLM call
systemPromptstringundefinedCustom system prompt (use {schema_description} placeholder)
additionalInstructionsstringundefinedExtra instructions appended to the system prompt
seedsRecord<string, unknown>[]undefinedFew-shot seed examples
diversityDiversityConfigundefinedDiversity strategy configuration
validationValidationConfigundefinedValidation and heuristics configuration
retryRetryConfig{ maxRetries: 3 }Retry configuration for LLM failures
dedupDedupOptions{ strategy: 'exact' }Deduplication configuration
invalidHandling'discard' | 'log' | 'repair''discard'How to handle invalid examples
structuredOutputbooleanundefinedRequest JSON mode from the LLM provider
costTrackingCostConfigundefinedToken cost tracking configuration
formatExportFormatundefinedExport format for the exported field in the result

Returns GenerateResult:

FieldTypeDescription
dataGeneratedExample[]Final validated, deduplicated examples
statsGenerationStatsPipeline statistics
exportedstring | undefinedFormatted output string (if format was specified)

validate(examples, schema, config?)

Validate an array of examples against a schema. Returns a ValidationResult for each example.

functionvalidate(examples: Record<string,unknown>[],schema: ExampleSchema,config?: ValidationConfig,): ValidationResult[]

Returns an array of:

interfaceValidationResult{valid: boolean;index: number;errors: ValidationError[];}interfaceValidationError{path: string[];message: string;code: string;}

Validation error codes:required, invalid_type, too_small, too_big, invalid_string, invalid_enum_value, heuristic_non_empty, heuristic_placeholder, heuristic_duplicate_fields, heuristic_min_words, global_min_length, global_max_length, custom_<name>.


validateExample(example, schema, config?)

Validate a single example. Returns an array of ValidationError objects (empty array means valid).

functionvalidateExample(example: Record<string,unknown>,schema: ExampleSchema,config?: ValidationConfig,): ValidationError[]

deduplicate(examples, options?)

Deduplicate an array of examples. Supports exact, near-duplicate, and semantic strategies.

functiondeduplicate(examples: Record<string,unknown>[],options?: Partial<DedupOptions>,): Promise<DedupResult>

DedupOptions:

OptionTypeDefaultDescription
strategy'exact' | 'near' | 'semantic' | 'none''exact'Deduplication strategy
thresholdnumber0.85 (near) / 0.92 (semantic)Similarity threshold for near/semantic dedup
ngramSizenumber2N-gram size for near-duplicate detection
fieldsstring[]all fieldsSubset of fields to compare
embedder(text: string) => Promise<number[]>undefinedEmbedding function (required for semantic strategy)
existingDataRecord<string, unknown>[]undefinedExisting dataset for cross-set deduplication

Returns DedupResult:

interfaceDedupResult{data: Record<string,unknown>[];// Deduplicated examplesremoved: number;// Number of duplicates removedpairs: Array<[number,number,number]>;// [indexA, indexB, similarity]}

exportAs(examples, format, options?)

Export examples to a training-ready format string.

functionexportAs(examples: Record<string,unknown>[],format: ExportFormat,options?: ExportOptions,): string

ExportFormat:'openai' | 'alpaca' | 'sharegpt' | 'csv' | 'jsonl'

ExportOptions:

OptionTypeDefaultDescription
fieldMapRecord<string, string>undefinedMap format roles to your field names
systemPromptstringundefinedStatic system prompt (OpenAI/ShareGPT formats)
delimiterstring','CSV column delimiter
quotestring'"'CSV quote character
headerbooleantrueInclude CSV header row
fieldsstring[]all fieldsSubset of fields to include

Individual Exporters

Each export format is available as a standalone function:

import{exportOpenAI,exportAlpaca,exportShareGPT,exportCSV,exportJSONL}from'synthdata-gen';
FunctionOutput format
exportOpenAI(examples, options?){"messages": [{"role": "system", ...}, {"role": "user", ...}, {"role": "assistant", ...}]} per line
exportAlpaca(examples, options?){"instruction": "...", "input": "...", "output": "..."} per line
exportShareGPT(examples, options?){"conversations": [{"from": "human", "value": "..."}, {"from": "gpt", "value": "..."}]} per line
exportCSV(examples, options?)Comma-separated values with header row
exportJSONL(examples, options?)One JSON object per line

All exporters automatically exclude _meta fields from output.


Generator Utilities

Low-level functions for template-based generation and prompt construction:

import{generateExample,generateExamples,buildSchemaPrompt,buildSystemPrompt,parseJsonResponse,}from'synthdata-gen';
FunctionDescription
generateExample(schema, seed?)Generate a single example from a schema using the built-in template generator. Deterministic when a seed is provided.
generateExamples(schema, count, baseSeed?)Generate multiple examples. Each example uses baseSeed + index for its seed.
buildSchemaPrompt(schema)Compile a schema into a natural-language prompt describing the expected JSON structure.
buildSystemPrompt(schema, customPrompt?, additionalInstructions?)Build the full system prompt for LLM-based generation. Supports a custom prompt template with {schema_description} placeholder.
parseJsonResponse(text)Extract JSON objects/arrays from an LLM response. Handles bare JSON, markdown code fences, and JSON embedded in explanatory text.

Configuration

Schema Definition

A schema defines the structure of each generated example using ExampleSchema:

constschema: ExampleSchema={fields: {question: {type: 'string',min: 10,max: 500,description: 'A natural language question'},answer: {type: 'string',min: 20,max: 2000,pattern: '^[A-Z]'},category: {type: 'enum',enum: ['science','history','technology']},difficulty: {type: 'integer',min: 1,max: 5},score: {type: 'number',min: 0,max: 100},active: {type: 'boolean'},tags: {type: 'array',items: {type: 'string'},min: 1,max: 5},metadata: {type: 'object',properties: {source: {type: 'string'},verified: {type: 'boolean'},},requiredFields: ['source'],},},required: ['question','answer','category'],};

Supported field types:

TypeSchemaField properties
stringmin (min length), max (max length), pattern (regex), description
numbermin, max, description
integermin, max, description
booleandescription
enumenum (valid values array), description
arrayitems (element schema), min (min items), max (max items), description
objectproperties (nested fields), requiredFields (required property names), description

All fields support required (default: true) and default (default value when field is omitted).


Validation Configuration

constconfig: ValidationConfig={// Global string field length constraintsminFieldLength: 10,maxFieldLength: 5000,// Quality heuristicsheuristics: {nonEmpty: true,// Reject empty/whitespace-only required string fieldsnoPlaceholder: true,// Reject placeholder text (lorem ipsum, TODO, TBD, N/A, etc.)noDuplicateFields: {// Reject examples where specified field pairs are identicalpairs: [['question','answer']],},minWordCount: {// Enforce minimum word count on specified fieldsfields: ['answer'],min: 5,},},// Custom validatorscustom: [{name: 'no-question-in-output',validate: (example)=>({valid: !String(example.answer).endsWith('?'),message: 'Answer should not end with a question mark',}),},],};

The noDuplicateFields and minWordCount heuristics also accept true to use automatic inference: noDuplicateFields: true pairs common field name patterns (question/answer, instruction/output, input/output, prompt/response, query/response), and minWordCount: true applies a default minimum of 3 words to all string fields.


Diversity Configuration

constdiversity: DiversityConfig={temperature: {min: 0.3,max: 1.2,strategy: 'cycle',// 'linear' | 'cycle' | 'random'},topics: ['algorithms','databases','networking','security'],negativeExampleRatio: 0.1,negativeInstructions: 'Generate an example with a subtle factual error.',constraintVariation: [{instruction: 'Write in a formal academic tone.'},{instruction: 'Write in a casual conversational tone.'},],};

Retry Configuration

constretry: RetryConfig={maxRetries: 3,// Maximum retry attempts per batchincludeFeedback: true,// Include validation error feedback in retry promptbackoff: 'exponential',// 'none' | 'linear' | 'exponential'backoffMs: 1000,// Base backoff delay in milliseconds};

Cost Tracking

constcostTracking: CostConfig={promptTokenCost: 0.000003,// Cost per prompt tokencompletionTokenCost: 0.000015,// Cost per completion tokencurrency: 'USD',};

The GenerationStats.cost field in the result contains promptTokens, completionTokens, totalCost, and currency.


Error Handling

Validation Errors

The validate and validateExample functions return structured error objects rather than throwing. Each ValidationError includes:

  • path -- array of field names locating the error (e.g., ['address', 'zip'] for nested fields, ['tags', '0'] for array elements)
  • message -- human-readable description of the failure
  • code -- machine-readable error code for programmatic handling
import{validateExample}from'synthdata-gen';consterrors=validateExample({instruction: 'Hi',output: 123,category: 'invalid'},schema,);for(consterroferrors){console.log(`[${err.code}] ${err.path.join('.')}: ${err.message}`);}// [too_small] instruction: String must contain at least 10 character(s), received 2// [invalid_type] output: Expected string, received number// [invalid_enum_value] category: Invalid enum value. Expected one of ["coding", ...], received "invalid"

Pipeline Error Handling

The generate function handles LLM failures internally using the retry configuration. Invalid examples are handled according to the invalidHandling option:

  • 'discard' (default) -- silently drops invalid examples
  • 'log' -- discards but records invalid examples and their errors in stats.invalidExamples
  • 'repair' -- includes invalid examples in the output with _meta.repaired: true

Validation error counts are always available in stats.validationErrors regardless of the handling mode.

Deduplication Errors

The deduplicate function throws an Error if the semantic strategy is used without providing an embedder function:

// Throws: "Semantic dedup requires an embedder function"awaitdeduplicate(examples,{strategy: 'semantic'});

Export Errors

The exportAs function throws an Error for unsupported format strings:

// Throws: "Unsupported export format: xml"exportAs(examples,'xml'asExportFormat);

Advanced Usage

Custom System Prompt

Override the default system prompt using the {schema_description} placeholder:

constresult=awaitgenerate(schema,{count: 100,llm: myLlm,systemPrompt: 'You are a medical expert generating training data.\n\n{schema_description}',additionalInstructions: 'All examples must be about cardiology.',});

Cross-Set Deduplication

Remove generated examples that duplicate entries in an existing dataset:

import{deduplicate}from'synthdata-gen';constresult=awaitdeduplicate(newExamples,{strategy: 'exact',existingData: existingDataset,});console.log(`Removed ${result.removed} duplicates of existing data`);

Semantic Deduplication

Provide an embedding function for meaning-level deduplication:

constresult=awaitdeduplicate(examples,{strategy: 'semantic',threshold: 0.92,embedder: async(text)=>{constresponse=awaitopenai.embeddings.create({model: 'text-embedding-3-small',input: text,});returnresponse.data[0].embedding;},});

Field-Specific Deduplication

Deduplicate based on a subset of fields:

constresult=awaitdeduplicate(examples,{strategy: 'near',threshold: 0.85,ngramSize: 2,fields: ['instruction'],// Only compare instruction fields});

Custom Field Mapping for Export

Map your schema fields to the roles expected by each export format:

import{exportOpenAI,exportAlpaca}from'synthdata-gen';constqaData=[{question: 'What is TCP?',answer: 'TCP is a connection-oriented protocol.'},];// Map question -> user, answer -> assistantconstopenai=exportOpenAI(qaData,{fieldMap: {user: 'question',assistant: 'answer'},systemPrompt: 'You are a networking expert.',});// Map question -> instruction, answer -> outputconstalpaca=exportAlpaca(qaData,{fieldMap: {instruction: 'question',output: 'answer'},});

Batch Generation with LLM

Request multiple examples per LLM call to reduce API costs:

constresult=awaitgenerate(schema,{llm: myLlm,count: 1000,batchSize: 10,// 10 examples per LLM callstructuredOutput: true,// Request JSON mode if provider supports it});

Standalone Template Generation

Use the template generator directly without the full pipeline:

import{generateExample,generateExamples}from'synthdata-gen';// Single example, deterministic with seedconstexample=generateExample(schema,42);// Multiple examples, deterministic with base seedconstexamples=generateExamples(schema,100,42);

Building Prompts for External Use

Generate the prompt that would be sent to an LLM, without calling one:

import{buildSchemaPrompt,buildSystemPrompt}from'synthdata-gen';constschemaPrompt=buildSchemaPrompt(schema);// "Generate a JSON object with the following structure:\n{ ... }"constsystemPrompt=buildSystemPrompt(schema,undefined,'Focus on edge cases.');// Full system prompt with schema description and additional instructions

Parsing LLM Responses

Extract JSON from messy LLM output:

import{parseJsonResponse}from'synthdata-gen';constobjects=parseJsonResponse('Here is the result:\n```json\n{"key": "value"}\n```\nDone!');// [{ key: "value" }]constarray=parseJsonResponse('[{"a": 1}, {"b": 2}]');// [{ a: 1 }, { b: 2 }]

TypeScript

All types are exported from the package entry point:

importtype{// LLM interfaceMessage,LlmCallOptions,LlmResponse,LlmFunction,// SchemaFieldType,SchemaField,ExampleSchema,// GenerationGeneratedExample,DiversityConfig,HeuristicsConfig,CustomValidator,ValidationConfig,RetryConfig,CostConfig,DedupOptions,GenerateOptions,ExportFormat,ExportOptions,// ResultsGenerateResult,GenerationStats,ValidationResult,ValidationError,DedupResult,}from'synthdata-gen';

The GeneratedExample<T> type is generic. By default it is Record<string, unknown> & { _meta?: ... }. You can narrow it with your own type:

interfaceQAPair{question: string;answer: string;category: string;}constresult=awaitgenerate(schema,{count: 10});constdata=result.dataasGeneratedExample<QAPair>[];

License

MIT

About

Generate and validate synthetic training data using any LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

synthdata-gen

Generate, validate, deduplicate, and export synthetic training data for LLM fine-tuning and evaluation.

npm versionnpm downloadslicensenodetypes


Description

synthdata-gen is a complete pipeline for producing synthetic training data. Define a schema describing the shape of each example, optionally plug in any LLM, and the library handles generation, output parsing, schema validation, quality heuristics, deduplication, and export to training-ready formats (OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, plain JSONL).

The library works in three modes:

  • Template-based generation -- no LLM required. A built-in deterministic generator produces examples matching your schema using seeded pseudo-random values. Useful for testing pipelines, prototyping schemas, and generating placeholder data.
  • LLM-based generation -- provide any async function that calls an LLM. The pipeline builds prompts from your schema, parses structured output from LLM responses (including JSON embedded in markdown fences), retries on failure, and tracks token usage and cost.
  • Custom generation -- provide your own generateFn callback for full control over how examples are produced, while still benefiting from the validation, dedup, and export stages.

Each pipeline stage (generation, validation, deduplication, export) is independently usable as a standalone function.


Installation

npm install synthdata-gen

Requires Node.js >= 18.


Quick Start

Template-based generation (no LLM required)

import{generate}from'synthdata-gen';importtype{ExampleSchema}from'synthdata-gen';constschema: ExampleSchema={fields: {instruction: {type: 'string',min: 10,max: 200,description: 'A clear instruction'},output: {type: 'string',min: 20,max: 1000,description: 'The expected response'},category: {type: 'enum',enum: ['coding','writing','reasoning']},},};constresult=awaitgenerate(schema,{count: 100});console.log(result.data);// GeneratedExample[]console.log(result.stats);// GenerationStats

LLM-based generation

import{generate}from'synthdata-gen';importtype{LlmFunction}from'synthdata-gen';constmyLlm: LlmFunction=async(messages,options)=>{constresponse=awaitcallMyProvider(messages,options);return{content: response.text,usage: {promptTokens: response.usage.prompt_tokens,completionTokens: response.usage.completion_tokens,totalTokens: response.usage.total_tokens,},};};constresult=awaitgenerate(schema,{llm: myLlm,count: 500,batchSize: 5,format: 'openai',seeds: [{instruction: 'Explain recursion',output: 'Recursion is when a function calls itself...',category: 'coding'},],costTracking: {promptTokenCost: 0.000003,completionTokenCost: 0.000015,currency: 'USD',},});console.log(result.exported);// OpenAI fine-tuning JSONL stringconsole.log(result.stats.cost);// { promptTokens, completionTokens, totalCost, currency }console.log(result.stats.durationMs);// wall-clock time

Features

  • Schema-driven generation -- define field types, constraints, descriptions, and required fields; the library compiles schemas into LLM prompts and validates output against them.
  • Three generation modes -- template-based (no LLM), LLM-based (any provider), or custom callback.
  • Robust LLM output parsing -- extracts JSON from bare responses, markdown code fences, and mixed text.
  • Schema validation -- type checking, string length constraints, numeric ranges, regex patterns, enum membership, array bounds, and nested object validation.
  • Quality heuristics -- detect empty fields, placeholder text (lorem ipsum, TODO, N/A), duplicate field values, and enforce minimum word counts.
  • Custom validators -- plug in arbitrary validation functions alongside built-in checks.
  • Three deduplication strategies -- exact match (normalized hash), near-duplicate (Jaccard similarity on n-grams), and semantic (cosine similarity on embeddings via a pluggable embedder).
  • Cross-set deduplication -- remove generated examples that overlap with an existing dataset.
  • Five export formats -- OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, and plain JSONL, with configurable field mappings.
  • Diversity controls -- temperature variation (linear, cycle, random), topic rotation, seed example rotation, negative example generation, and constraint variation.
  • Cost tracking -- track prompt and completion tokens, compute estimated cost per run.
  • Deterministic generation -- seeded PRNG for reproducible template-based output.
  • Full TypeScript support -- all types exported, strict mode compatible.

API Reference

generate(schema, options)

Main pipeline function. Generates examples, validates, deduplicates, and optionally exports.

functiongenerate(schema: ExampleSchema,options: GenerateOptions): Promise<GenerateResult>

Parameters:

ParameterTypeDescription
schemaExampleSchemaSchema defining the shape of each example
optionsGenerateOptionsPipeline configuration (see below)

GenerateOptions:

OptionTypeDefaultDescription
countnumberrequiredNumber of examples to generate
llmLlmFunctionundefinedAsync function that calls an LLM
generateFn(schema, batchIndex) => Record[]undefinedCustom generation callback
batchSizenumber1Examples per LLM call
systemPromptstringundefinedCustom system prompt (use {schema_description} placeholder)
additionalInstructionsstringundefinedExtra instructions appended to the system prompt
seedsRecord<string, unknown>[]undefinedFew-shot seed examples
diversityDiversityConfigundefinedDiversity strategy configuration
validationValidationConfigundefinedValidation and heuristics configuration
retryRetryConfig{ maxRetries: 3 }Retry configuration for LLM failures
dedupDedupOptions{ strategy: 'exact' }Deduplication configuration
invalidHandling'discard' | 'log' | 'repair''discard'How to handle invalid examples
structuredOutputbooleanundefinedRequest JSON mode from the LLM provider
costTrackingCostConfigundefinedToken cost tracking configuration
formatExportFormatundefinedExport format for the exported field in the result

Returns GenerateResult:

FieldTypeDescription
dataGeneratedExample[]Final validated, deduplicated examples
statsGenerationStatsPipeline statistics
exportedstring | undefinedFormatted output string (if format was specified)

validate(examples, schema, config?)

Validate an array of examples against a schema. Returns a ValidationResult for each example.

functionvalidate(examples: Record<string,unknown>[],schema: ExampleSchema,config?: ValidationConfig,): ValidationResult[]

Returns an array of:

interfaceValidationResult{valid: boolean;index: number;errors: ValidationError[];}interfaceValidationError{path: string[];message: string;code: string;}

Validation error codes:required, invalid_type, too_small, too_big, invalid_string, invalid_enum_value, heuristic_non_empty, heuristic_placeholder, heuristic_duplicate_fields, heuristic_min_words, global_min_length, global_max_length, custom_<name>.


validateExample(example, schema, config?)

Validate a single example. Returns an array of ValidationError objects (empty array means valid).

functionvalidateExample(example: Record<string,unknown>,schema: ExampleSchema,config?: ValidationConfig,): ValidationError[]

deduplicate(examples, options?)

Deduplicate an array of examples. Supports exact, near-duplicate, and semantic strategies.

functiondeduplicate(examples: Record<string,unknown>[],options?: Partial<DedupOptions>,): Promise<DedupResult>

DedupOptions:

OptionTypeDefaultDescription
strategy'exact' | 'near' | 'semantic' | 'none''exact'Deduplication strategy
thresholdnumber0.85 (near) / 0.92 (semantic)Similarity threshold for near/semantic dedup
ngramSizenumber2N-gram size for near-duplicate detection
fieldsstring[]all fieldsSubset of fields to compare
embedder(text: string) => Promise<number[]>undefinedEmbedding function (required for semantic strategy)
existingDataRecord<string, unknown>[]undefinedExisting dataset for cross-set deduplication

Returns DedupResult:

interfaceDedupResult{data: Record<string,unknown>[];// Deduplicated examplesremoved: number;// Number of duplicates removedpairs: Array<[number,number,number]>;// [indexA, indexB, similarity]}

exportAs(examples, format, options?)

Export examples to a training-ready format string.

functionexportAs(examples: Record<string,unknown>[],format: ExportFormat,options?: ExportOptions,): string

ExportFormat:'openai' | 'alpaca' | 'sharegpt' | 'csv' | 'jsonl'

ExportOptions:

OptionTypeDefaultDescription
fieldMapRecord<string, string>undefinedMap format roles to your field names
systemPromptstringundefinedStatic system prompt (OpenAI/ShareGPT formats)
delimiterstring','CSV column delimiter
quotestring'"'CSV quote character
headerbooleantrueInclude CSV header row
fieldsstring[]all fieldsSubset of fields to include

Individual Exporters

Each export format is available as a standalone function:

import{exportOpenAI,exportAlpaca,exportShareGPT,exportCSV,exportJSONL}from'synthdata-gen';
FunctionOutput format
exportOpenAI(examples, options?){"messages": [{"role": "system", ...}, {"role": "user", ...}, {"role": "assistant", ...}]} per line
exportAlpaca(examples, options?){"instruction": "...", "input": "...", "output": "..."} per line
exportShareGPT(examples, options?){"conversations": [{"from": "human", "value": "..."}, {"from": "gpt", "value": "..."}]} per line
exportCSV(examples, options?)Comma-separated values with header row
exportJSONL(examples, options?)One JSON object per line

All exporters automatically exclude _meta fields from output.


Generator Utilities

Low-level functions for template-based generation and prompt construction:

import{generateExample,generateExamples,buildSchemaPrompt,buildSystemPrompt,parseJsonResponse,}from'synthdata-gen';
FunctionDescription
generateExample(schema, seed?)Generate a single example from a schema using the built-in template generator. Deterministic when a seed is provided.
generateExamples(schema, count, baseSeed?)Generate multiple examples. Each example uses baseSeed + index for its seed.
buildSchemaPrompt(schema)Compile a schema into a natural-language prompt describing the expected JSON structure.
buildSystemPrompt(schema, customPrompt?, additionalInstructions?)Build the full system prompt for LLM-based generation. Supports a custom prompt template with {schema_description} placeholder.
parseJsonResponse(text)Extract JSON objects/arrays from an LLM response. Handles bare JSON, markdown code fences, and JSON embedded in explanatory text.

Configuration

Schema Definition

A schema defines the structure of each generated example using ExampleSchema:

constschema: ExampleSchema={fields: {question: {type: 'string',min: 10,max: 500,description: 'A natural language question'},answer: {type: 'string',min: 20,max: 2000,pattern: '^[A-Z]'},category: {type: 'enum',enum: ['science','history','technology']},difficulty: {type: 'integer',min: 1,max: 5},score: {type: 'number',min: 0,max: 100},active: {type: 'boolean'},tags: {type: 'array',items: {type: 'string'},min: 1,max: 5},metadata: {type: 'object',properties: {source: {type: 'string'},verified: {type: 'boolean'},},requiredFields: ['source'],},},required: ['question','answer','category'],};

Supported field types:

TypeSchemaField properties
stringmin (min length), max (max length), pattern (regex), description
numbermin, max, description
integermin, max, description
booleandescription
enumenum (valid values array), description
arrayitems (element schema), min (min items), max (max items), description
objectproperties (nested fields), requiredFields (required property names), description

All fields support required (default: true) and default (default value when field is omitted).


Validation Configuration

constconfig: ValidationConfig={// Global string field length constraintsminFieldLength: 10,maxFieldLength: 5000,// Quality heuristicsheuristics: {nonEmpty: true,// Reject empty/whitespace-only required string fieldsnoPlaceholder: true,// Reject placeholder text (lorem ipsum, TODO, TBD, N/A, etc.)noDuplicateFields: {// Reject examples where specified field pairs are identicalpairs: [['question','answer']],},minWordCount: {// Enforce minimum word count on specified fieldsfields: ['answer'],min: 5,},},// Custom validatorscustom: [{name: 'no-question-in-output',validate: (example)=>({valid: !String(example.answer).endsWith('?'),message: 'Answer should not end with a question mark',}),},],};

The noDuplicateFields and minWordCount heuristics also accept true to use automatic inference: noDuplicateFields: true pairs common field name patterns (question/answer, instruction/output, input/output, prompt/response, query/response), and minWordCount: true applies a default minimum of 3 words to all string fields.


Diversity Configuration

constdiversity: DiversityConfig={temperature: {min: 0.3,max: 1.2,strategy: 'cycle',// 'linear' | 'cycle' | 'random'},topics: ['algorithms','databases','networking','security'],negativeExampleRatio: 0.1,negativeInstructions: 'Generate an example with a subtle factual error.',constraintVariation: [{instruction: 'Write in a formal academic tone.'},{instruction: 'Write in a casual conversational tone.'},],};

Retry Configuration

constretry: RetryConfig={maxRetries: 3,// Maximum retry attempts per batchincludeFeedback: true,// Include validation error feedback in retry promptbackoff: 'exponential',// 'none' | 'linear' | 'exponential'backoffMs: 1000,// Base backoff delay in milliseconds};

Cost Tracking

constcostTracking: CostConfig={promptTokenCost: 0.000003,// Cost per prompt tokencompletionTokenCost: 0.000015,// Cost per completion tokencurrency: 'USD',};

The GenerationStats.cost field in the result contains promptTokens, completionTokens, totalCost, and currency.


Error Handling

Validation Errors

The validate and validateExample functions return structured error objects rather than throwing. Each ValidationError includes:

  • path -- array of field names locating the error (e.g., ['address', 'zip'] for nested fields, ['tags', '0'] for array elements)
  • message -- human-readable description of the failure
  • code -- machine-readable error code for programmatic handling
import{validateExample}from'synthdata-gen';consterrors=validateExample({instruction: 'Hi',output: 123,category: 'invalid'},schema,);for(consterroferrors){console.log(`[${err.code}] ${err.path.join('.')}: ${err.message}`);}// [too_small] instruction: String must contain at least 10 character(s), received 2// [invalid_type] output: Expected string, received number// [invalid_enum_value] category: Invalid enum value. Expected one of ["coding", ...], received "invalid"

Pipeline Error Handling

The generate function handles LLM failures internally using the retry configuration. Invalid examples are handled according to the invalidHandling option:

  • 'discard' (default) -- silently drops invalid examples
  • 'log' -- discards but records invalid examples and their errors in stats.invalidExamples
  • 'repair' -- includes invalid examples in the output with _meta.repaired: true

Validation error counts are always available in stats.validationErrors regardless of the handling mode.

Deduplication Errors

The deduplicate function throws an Error if the semantic strategy is used without providing an embedder function:

// Throws: "Semantic dedup requires an embedder function"awaitdeduplicate(examples,{strategy: 'semantic'});

Export Errors

The exportAs function throws an Error for unsupported format strings:

// Throws: "Unsupported export format: xml"exportAs(examples,'xml'asExportFormat);

Advanced Usage

Custom System Prompt

Override the default system prompt using the {schema_description} placeholder:

constresult=awaitgenerate(schema,{count: 100,llm: myLlm,systemPrompt: 'You are a medical expert generating training data.\n\n{schema_description}',additionalInstructions: 'All examples must be about cardiology.',});

Cross-Set Deduplication

Remove generated examples that duplicate entries in an existing dataset:

import{deduplicate}from'synthdata-gen';constresult=awaitdeduplicate(newExamples,{strategy: 'exact',existingData: existingDataset,});console.log(`Removed ${result.removed} duplicates of existing data`);

Semantic Deduplication

Provide an embedding function for meaning-level deduplication:

constresult=awaitdeduplicate(examples,{strategy: 'semantic',threshold: 0.92,embedder: async(text)=>{constresponse=awaitopenai.embeddings.create({model: 'text-embedding-3-small',input: text,});returnresponse.data[0].embedding;},});

Field-Specific Deduplication

Deduplicate based on a subset of fields:

constresult=awaitdeduplicate(examples,{strategy: 'near',threshold: 0.85,ngramSize: 2,fields: ['instruction'],// Only compare instruction fields});

Custom Field Mapping for Export

Map your schema fields to the roles expected by each export format:

import{exportOpenAI,exportAlpaca}from'synthdata-gen';constqaData=[{question: 'What is TCP?',answer: 'TCP is a connection-oriented protocol.'},];// Map question -> user, answer -> assistantconstopenai=exportOpenAI(qaData,{fieldMap: {user: 'question',assistant: 'answer'},systemPrompt: 'You are a networking expert.',});// Map question -> instruction, answer -> outputconstalpaca=exportAlpaca(qaData,{fieldMap: {instruction: 'question',output: 'answer'},});

Batch Generation with LLM

Request multiple examples per LLM call to reduce API costs:

constresult=awaitgenerate(schema,{llm: myLlm,count: 1000,batchSize: 10,// 10 examples per LLM callstructuredOutput: true,// Request JSON mode if provider supports it});

Standalone Template Generation

Use the template generator directly without the full pipeline:

import{generateExample,generateExamples}from'synthdata-gen';// Single example, deterministic with seedconstexample=generateExample(schema,42);// Multiple examples, deterministic with base seedconstexamples=generateExamples(schema,100,42);

Building Prompts for External Use

Generate the prompt that would be sent to an LLM, without calling one:

import{buildSchemaPrompt,buildSystemPrompt}from'synthdata-gen';constschemaPrompt=buildSchemaPrompt(schema);// "Generate a JSON object with the following structure:\n{ ... }"constsystemPrompt=buildSystemPrompt(schema,undefined,'Focus on edge cases.');// Full system prompt with schema description and additional instructions

Parsing LLM Responses

Extract JSON from messy LLM output:

import{parseJsonResponse}from'synthdata-gen';constobjects=parseJsonResponse('Here is the result:\n```json\n{"key": "value"}\n```\nDone!');// [{ key: "value" }]constarray=parseJsonResponse('[{"a": 1}, {"b": 2}]');// [{ a: 1 }, { b: 2 }]

TypeScript

All types are exported from the package entry point:

importtype{// LLM interfaceMessage,LlmCallOptions,LlmResponse,LlmFunction,// SchemaFieldType,SchemaField,ExampleSchema,// GenerationGeneratedExample,DiversityConfig,HeuristicsConfig,CustomValidator,ValidationConfig,RetryConfig,CostConfig,DedupOptions,GenerateOptions,ExportFormat,ExportOptions,// ResultsGenerateResult,GenerationStats,ValidationResult,ValidationError,DedupResult,}from'synthdata-gen';

The GeneratedExample<T> type is generic. By default it is Record<string, unknown> & { _meta?: ... }. You can narrow it with your own type:

interfaceQAPair{question: string;answer: string;category: string;}constresult=awaitgenerate(schema,{count: 10});constdata=result.dataasGeneratedExample<QAPair>[];

License

MIT

About

Generate and validate synthetic training data using any LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

synthdata-gen

Generate, validate, deduplicate, and export synthetic training data for LLM fine-tuning and evaluation.

npm versionnpm downloadslicensenodetypes


Description

synthdata-gen is a complete pipeline for producing synthetic training data. Define a schema describing the shape of each example, optionally plug in any LLM, and the library handles generation, output parsing, schema validation, quality heuristics, deduplication, and export to training-ready formats (OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, plain JSONL).

The library works in three modes:

  • Template-based generation -- no LLM required. A built-in deterministic generator produces examples matching your schema using seeded pseudo-random values. Useful for testing pipelines, prototyping schemas, and generating placeholder data.
  • LLM-based generation -- provide any async function that calls an LLM. The pipeline builds prompts from your schema, parses structured output from LLM responses (including JSON embedded in markdown fences), retries on failure, and tracks token usage and cost.
  • Custom generation -- provide your own generateFn callback for full control over how examples are produced, while still benefiting from the validation, dedup, and export stages.

Each pipeline stage (generation, validation, deduplication, export) is independently usable as a standalone function.


Installation

npm install synthdata-gen

Requires Node.js >= 18.


Quick Start

Template-based generation (no LLM required)

import{generate}from'synthdata-gen';importtype{ExampleSchema}from'synthdata-gen';constschema: ExampleSchema={fields: {instruction: {type: 'string',min: 10,max: 200,description: 'A clear instruction'},output: {type: 'string',min: 20,max: 1000,description: 'The expected response'},category: {type: 'enum',enum: ['coding','writing','reasoning']},},};constresult=awaitgenerate(schema,{count: 100});console.log(result.data);// GeneratedExample[]console.log(result.stats);// GenerationStats

LLM-based generation

import{generate}from'synthdata-gen';importtype{LlmFunction}from'synthdata-gen';constmyLlm: LlmFunction=async(messages,options)=>{constresponse=awaitcallMyProvider(messages,options);return{content: response.text,usage: {promptTokens: response.usage.prompt_tokens,completionTokens: response.usage.completion_tokens,totalTokens: response.usage.total_tokens,},};};constresult=awaitgenerate(schema,{llm: myLlm,count: 500,batchSize: 5,format: 'openai',seeds: [{instruction: 'Explain recursion',output: 'Recursion is when a function calls itself...',category: 'coding'},],costTracking: {promptTokenCost: 0.000003,completionTokenCost: 0.000015,currency: 'USD',},});console.log(result.exported);// OpenAI fine-tuning JSONL stringconsole.log(result.stats.cost);// { promptTokens, completionTokens, totalCost, currency }console.log(result.stats.durationMs);// wall-clock time

Features

  • Schema-driven generation -- define field types, constraints, descriptions, and required fields; the library compiles schemas into LLM prompts and validates output against them.
  • Three generation modes -- template-based (no LLM), LLM-based (any provider), or custom callback.
  • Robust LLM output parsing -- extracts JSON from bare responses, markdown code fences, and mixed text.
  • Schema validation -- type checking, string length constraints, numeric ranges, regex patterns, enum membership, array bounds, and nested object validation.
  • Quality heuristics -- detect empty fields, placeholder text (lorem ipsum, TODO, N/A), duplicate field values, and enforce minimum word counts.
  • Custom validators -- plug in arbitrary validation functions alongside built-in checks.
  • Three deduplication strategies -- exact match (normalized hash), near-duplicate (Jaccard similarity on n-grams), and semantic (cosine similarity on embeddings via a pluggable embedder).
  • Cross-set deduplication -- remove generated examples that overlap with an existing dataset.
  • Five export formats -- OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, and plain JSONL, with configurable field mappings.
  • Diversity controls -- temperature variation (linear, cycle, random), topic rotation, seed example rotation, negative example generation, and constraint variation.
  • Cost tracking -- track prompt and completion tokens, compute estimated cost per run.
  • Deterministic generation -- seeded PRNG for reproducible template-based output.
  • Full TypeScript support -- all types exported, strict mode compatible.

API Reference

generate(schema, options)

Main pipeline function. Generates examples, validates, deduplicates, and optionally exports.

functiongenerate(schema: ExampleSchema,options: GenerateOptions): Promise<GenerateResult>

Parameters:

ParameterTypeDescription
schemaExampleSchemaSchema defining the shape of each example
optionsGenerateOptionsPipeline configuration (see below)

GenerateOptions:

OptionTypeDefaultDescription
countnumberrequiredNumber of examples to generate
llmLlmFunctionundefinedAsync function that calls an LLM
generateFn(schema, batchIndex) => Record[]undefinedCustom generation callback
batchSizenumber1Examples per LLM call
systemPromptstringundefinedCustom system prompt (use {schema_description} placeholder)
additionalInstructionsstringundefinedExtra instructions appended to the system prompt
seedsRecord<string, unknown>[]undefinedFew-shot seed examples
diversityDiversityConfigundefinedDiversity strategy configuration
validationValidationConfigundefinedValidation and heuristics configuration
retryRetryConfig{ maxRetries: 3 }Retry configuration for LLM failures
dedupDedupOptions{ strategy: 'exact' }Deduplication configuration
invalidHandling'discard' | 'log' | 'repair''discard'How to handle invalid examples
structuredOutputbooleanundefinedRequest JSON mode from the LLM provider
costTrackingCostConfigundefinedToken cost tracking configuration
formatExportFormatundefinedExport format for the exported field in the result

Returns GenerateResult:

FieldTypeDescription
dataGeneratedExample[]Final validated, deduplicated examples
statsGenerationStatsPipeline statistics
exportedstring | undefinedFormatted output string (if format was specified)

validate(examples, schema, config?)

Validate an array of examples against a schema. Returns a ValidationResult for each example.

functionvalidate(examples: Record<string,unknown>[],schema: ExampleSchema,config?: ValidationConfig,): ValidationResult[]

Returns an array of:

interfaceValidationResult{valid: boolean;index: number;errors: ValidationError[];}interfaceValidationError{path: string[];message: string;code: string;}

Validation error codes:required, invalid_type, too_small, too_big, invalid_string, invalid_enum_value, heuristic_non_empty, heuristic_placeholder, heuristic_duplicate_fields, heuristic_min_words, global_min_length, global_max_length, custom_<name>.


validateExample(example, schema, config?)

Validate a single example. Returns an array of ValidationError objects (empty array means valid).

functionvalidateExample(example: Record<string,unknown>,schema: ExampleSchema,config?: ValidationConfig,): ValidationError[]

deduplicate(examples, options?)

Deduplicate an array of examples. Supports exact, near-duplicate, and semantic strategies.

functiondeduplicate(examples: Record<string,unknown>[],options?: Partial<DedupOptions>,): Promise<DedupResult>

DedupOptions:

OptionTypeDefaultDescription
strategy'exact' | 'near' | 'semantic' | 'none''exact'Deduplication strategy
thresholdnumber0.85 (near) / 0.92 (semantic)Similarity threshold for near/semantic dedup
ngramSizenumber2N-gram size for near-duplicate detection
fieldsstring[]all fieldsSubset of fields to compare
embedder(text: string) => Promise<number[]>undefinedEmbedding function (required for semantic strategy)
existingDataRecord<string, unknown>[]undefinedExisting dataset for cross-set deduplication

Returns DedupResult:

interfaceDedupResult{data: Record<string,unknown>[];// Deduplicated examplesremoved: number;// Number of duplicates removedpairs: Array<[number,number,number]>;// [indexA, indexB, similarity]}

exportAs(examples, format, options?)

Export examples to a training-ready format string.

functionexportAs(examples: Record<string,unknown>[],format: ExportFormat,options?: ExportOptions,): string

ExportFormat:'openai' | 'alpaca' | 'sharegpt' | 'csv' | 'jsonl'

ExportOptions:

OptionTypeDefaultDescription
fieldMapRecord<string, string>undefinedMap format roles to your field names
systemPromptstringundefinedStatic system prompt (OpenAI/ShareGPT formats)
delimiterstring','CSV column delimiter
quotestring'"'CSV quote character
headerbooleantrueInclude CSV header row
fieldsstring[]all fieldsSubset of fields to include

Individual Exporters

Each export format is available as a standalone function:

import{exportOpenAI,exportAlpaca,exportShareGPT,exportCSV,exportJSONL}from'synthdata-gen';
FunctionOutput format
exportOpenAI(examples, options?){"messages": [{"role": "system", ...}, {"role": "user", ...}, {"role": "assistant", ...}]} per line
exportAlpaca(examples, options?){"instruction": "...", "input": "...", "output": "..."} per line
exportShareGPT(examples, options?){"conversations": [{"from": "human", "value": "..."}, {"from": "gpt", "value": "..."}]} per line
exportCSV(examples, options?)Comma-separated values with header row
exportJSONL(examples, options?)One JSON object per line

All exporters automatically exclude _meta fields from output.


Generator Utilities

Low-level functions for template-based generation and prompt construction:

import{generateExample,generateExamples,buildSchemaPrompt,buildSystemPrompt,parseJsonResponse,}from'synthdata-gen';
FunctionDescription
generateExample(schema, seed?)Generate a single example from a schema using the built-in template generator. Deterministic when a seed is provided.
generateExamples(schema, count, baseSeed?)Generate multiple examples. Each example uses baseSeed + index for its seed.
buildSchemaPrompt(schema)Compile a schema into a natural-language prompt describing the expected JSON structure.
buildSystemPrompt(schema, customPrompt?, additionalInstructions?)Build the full system prompt for LLM-based generation. Supports a custom prompt template with {schema_description} placeholder.
parseJsonResponse(text)Extract JSON objects/arrays from an LLM response. Handles bare JSON, markdown code fences, and JSON embedded in explanatory text.

Configuration

Schema Definition

A schema defines the structure of each generated example using ExampleSchema:

constschema: ExampleSchema={fields: {question: {type: 'string',min: 10,max: 500,description: 'A natural language question'},answer: {type: 'string',min: 20,max: 2000,pattern: '^[A-Z]'},category: {type: 'enum',enum: ['science','history','technology']},difficulty: {type: 'integer',min: 1,max: 5},score: {type: 'number',min: 0,max: 100},active: {type: 'boolean'},tags: {type: 'array',items: {type: 'string'},min: 1,max: 5},metadata: {type: 'object',properties: {source: {type: 'string'},verified: {type: 'boolean'},},requiredFields: ['source'],},},required: ['question','answer','category'],};

Supported field types:

TypeSchemaField properties
stringmin (min length), max (max length), pattern (regex), description
numbermin, max, description
integermin, max, description
booleandescription
enumenum (valid values array), description
arrayitems (element schema), min (min items), max (max items), description
objectproperties (nested fields), requiredFields (required property names), description

All fields support required (default: true) and default (default value when field is omitted).


Validation Configuration

constconfig: ValidationConfig={// Global string field length constraintsminFieldLength: 10,maxFieldLength: 5000,// Quality heuristicsheuristics: {nonEmpty: true,// Reject empty/whitespace-only required string fieldsnoPlaceholder: true,// Reject placeholder text (lorem ipsum, TODO, TBD, N/A, etc.)noDuplicateFields: {// Reject examples where specified field pairs are identicalpairs: [['question','answer']],},minWordCount: {// Enforce minimum word count on specified fieldsfields: ['answer'],min: 5,},},// Custom validatorscustom: [{name: 'no-question-in-output',validate: (example)=>({valid: !String(example.answer).endsWith('?'),message: 'Answer should not end with a question mark',}),},],};

The noDuplicateFields and minWordCount heuristics also accept true to use automatic inference: noDuplicateFields: true pairs common field name patterns (question/answer, instruction/output, input/output, prompt/response, query/response), and minWordCount: true applies a default minimum of 3 words to all string fields.


Diversity Configuration

constdiversity: DiversityConfig={temperature: {min: 0.3,max: 1.2,strategy: 'cycle',// 'linear' | 'cycle' | 'random'},topics: ['algorithms','databases','networking','security'],negativeExampleRatio: 0.1,negativeInstructions: 'Generate an example with a subtle factual error.',constraintVariation: [{instruction: 'Write in a formal academic tone.'},{instruction: 'Write in a casual conversational tone.'},],};

Retry Configuration

constretry: RetryConfig={maxRetries: 3,// Maximum retry attempts per batchincludeFeedback: true,// Include validation error feedback in retry promptbackoff: 'exponential',// 'none' | 'linear' | 'exponential'backoffMs: 1000,// Base backoff delay in milliseconds};

Cost Tracking

constcostTracking: CostConfig={promptTokenCost: 0.000003,// Cost per prompt tokencompletionTokenCost: 0.000015,// Cost per completion tokencurrency: 'USD',};

The GenerationStats.cost field in the result contains promptTokens, completionTokens, totalCost, and currency.


Error Handling

Validation Errors

The validate and validateExample functions return structured error objects rather than throwing. Each ValidationError includes:

  • path -- array of field names locating the error (e.g., ['address', 'zip'] for nested fields, ['tags', '0'] for array elements)
  • message -- human-readable description of the failure
  • code -- machine-readable error code for programmatic handling
import{validateExample}from'synthdata-gen';consterrors=validateExample({instruction: 'Hi',output: 123,category: 'invalid'},schema,);for(consterroferrors){console.log(`[${err.code}] ${err.path.join('.')}: ${err.message}`);}// [too_small] instruction: String must contain at least 10 character(s), received 2// [invalid_type] output: Expected string, received number// [invalid_enum_value] category: Invalid enum value. Expected one of ["coding", ...], received "invalid"

Pipeline Error Handling

The generate function handles LLM failures internally using the retry configuration. Invalid examples are handled according to the invalidHandling option:

  • 'discard' (default) -- silently drops invalid examples
  • 'log' -- discards but records invalid examples and their errors in stats.invalidExamples
  • 'repair' -- includes invalid examples in the output with _meta.repaired: true

Validation error counts are always available in stats.validationErrors regardless of the handling mode.

Deduplication Errors

The deduplicate function throws an Error if the semantic strategy is used without providing an embedder function:

// Throws: "Semantic dedup requires an embedder function"awaitdeduplicate(examples,{strategy: 'semantic'});

Export Errors

The exportAs function throws an Error for unsupported format strings:

// Throws: "Unsupported export format: xml"exportAs(examples,'xml'asExportFormat);

Advanced Usage

Custom System Prompt

Override the default system prompt using the {schema_description} placeholder:

constresult=awaitgenerate(schema,{count: 100,llm: myLlm,systemPrompt: 'You are a medical expert generating training data.\n\n{schema_description}',additionalInstructions: 'All examples must be about cardiology.',});

Cross-Set Deduplication

Remove generated examples that duplicate entries in an existing dataset:

import{deduplicate}from'synthdata-gen';constresult=awaitdeduplicate(newExamples,{strategy: 'exact',existingData: existingDataset,});console.log(`Removed ${result.removed} duplicates of existing data`);

Semantic Deduplication

Provide an embedding function for meaning-level deduplication:

constresult=awaitdeduplicate(examples,{strategy: 'semantic',threshold: 0.92,embedder: async(text)=>{constresponse=awaitopenai.embeddings.create({model: 'text-embedding-3-small',input: text,});returnresponse.data[0].embedding;},});

Field-Specific Deduplication

Deduplicate based on a subset of fields:

constresult=awaitdeduplicate(examples,{strategy: 'near',threshold: 0.85,ngramSize: 2,fields: ['instruction'],// Only compare instruction fields});

Custom Field Mapping for Export

Map your schema fields to the roles expected by each export format:

import{exportOpenAI,exportAlpaca}from'synthdata-gen';constqaData=[{question: 'What is TCP?',answer: 'TCP is a connection-oriented protocol.'},];// Map question -> user, answer -> assistantconstopenai=exportOpenAI(qaData,{fieldMap: {user: 'question',assistant: 'answer'},systemPrompt: 'You are a networking expert.',});// Map question -> instruction, answer -> outputconstalpaca=exportAlpaca(qaData,{fieldMap: {instruction: 'question',output: 'answer'},});

Batch Generation with LLM

Request multiple examples per LLM call to reduce API costs:

constresult=awaitgenerate(schema,{llm: myLlm,count: 1000,batchSize: 10,// 10 examples per LLM callstructuredOutput: true,// Request JSON mode if provider supports it});

Standalone Template Generation

Use the template generator directly without the full pipeline:

import{generateExample,generateExamples}from'synthdata-gen';// Single example, deterministic with seedconstexample=generateExample(schema,42);// Multiple examples, deterministic with base seedconstexamples=generateExamples(schema,100,42);

Building Prompts for External Use

Generate the prompt that would be sent to an LLM, without calling one:

import{buildSchemaPrompt,buildSystemPrompt}from'synthdata-gen';constschemaPrompt=buildSchemaPrompt(schema);// "Generate a JSON object with the following structure:\n{ ... }"constsystemPrompt=buildSystemPrompt(schema,undefined,'Focus on edge cases.');// Full system prompt with schema description and additional instructions

Parsing LLM Responses

Extract JSON from messy LLM output:

import{parseJsonResponse}from'synthdata-gen';constobjects=parseJsonResponse('Here is the result:\n```json\n{"key": "value"}\n```\nDone!');// [{ key: "value" }]constarray=parseJsonResponse('[{"a": 1}, {"b": 2}]');// [{ a: 1 }, { b: 2 }]

TypeScript

All types are exported from the package entry point:

importtype{// LLM interfaceMessage,LlmCallOptions,LlmResponse,LlmFunction,// SchemaFieldType,SchemaField,ExampleSchema,// GenerationGeneratedExample,DiversityConfig,HeuristicsConfig,CustomValidator,ValidationConfig,RetryConfig,CostConfig,DedupOptions,GenerateOptions,ExportFormat,ExportOptions,// ResultsGenerateResult,GenerationStats,ValidationResult,ValidationError,DedupResult,}from'synthdata-gen';

The GeneratedExample<T> type is generic. By default it is Record<string, unknown> & { _meta?: ... }. You can narrow it with your own type:

interfaceQAPair{question: string;answer: string;category: string;}constresult=awaitgenerate(schema,{count: 10});constdata=result.dataasGeneratedExample<QAPair>[];

License

MIT

About

Generate and validate synthetic training data using any LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

synthdata-gen

Generate, validate, deduplicate, and export synthetic training data for LLM fine-tuning and evaluation.

npm versionnpm downloadslicensenodetypes


Description

synthdata-gen is a complete pipeline for producing synthetic training data. Define a schema describing the shape of each example, optionally plug in any LLM, and the library handles generation, output parsing, schema validation, quality heuristics, deduplication, and export to training-ready formats (OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, plain JSONL).

The library works in three modes:

  • Template-based generation -- no LLM required. A built-in deterministic generator produces examples matching your schema using seeded pseudo-random values. Useful for testing pipelines, prototyping schemas, and generating placeholder data.
  • LLM-based generation -- provide any async function that calls an LLM. The pipeline builds prompts from your schema, parses structured output from LLM responses (including JSON embedded in markdown fences), retries on failure, and tracks token usage and cost.
  • Custom generation -- provide your own generateFn callback for full control over how examples are produced, while still benefiting from the validation, dedup, and export stages.

Each pipeline stage (generation, validation, deduplication, export) is independently usable as a standalone function.


Installation

npm install synthdata-gen

Requires Node.js >= 18.


Quick Start

Template-based generation (no LLM required)

import{generate}from'synthdata-gen';importtype{ExampleSchema}from'synthdata-gen';constschema: ExampleSchema={fields: {instruction: {type: 'string',min: 10,max: 200,description: 'A clear instruction'},output: {type: 'string',min: 20,max: 1000,description: 'The expected response'},category: {type: 'enum',enum: ['coding','writing','reasoning']},},};constresult=awaitgenerate(schema,{count: 100});console.log(result.data);// GeneratedExample[]console.log(result.stats);// GenerationStats

LLM-based generation

import{generate}from'synthdata-gen';importtype{LlmFunction}from'synthdata-gen';constmyLlm: LlmFunction=async(messages,options)=>{constresponse=awaitcallMyProvider(messages,options);return{content: response.text,usage: {promptTokens: response.usage.prompt_tokens,completionTokens: response.usage.completion_tokens,totalTokens: response.usage.total_tokens,},};};constresult=awaitgenerate(schema,{llm: myLlm,count: 500,batchSize: 5,format: 'openai',seeds: [{instruction: 'Explain recursion',output: 'Recursion is when a function calls itself...',category: 'coding'},],costTracking: {promptTokenCost: 0.000003,completionTokenCost: 0.000015,currency: 'USD',},});console.log(result.exported);// OpenAI fine-tuning JSONL stringconsole.log(result.stats.cost);// { promptTokens, completionTokens, totalCost, currency }console.log(result.stats.durationMs);// wall-clock time

Features

  • Schema-driven generation -- define field types, constraints, descriptions, and required fields; the library compiles schemas into LLM prompts and validates output against them.
  • Three generation modes -- template-based (no LLM), LLM-based (any provider), or custom callback.
  • Robust LLM output parsing -- extracts JSON from bare responses, markdown code fences, and mixed text.
  • Schema validation -- type checking, string length constraints, numeric ranges, regex patterns, enum membership, array bounds, and nested object validation.
  • Quality heuristics -- detect empty fields, placeholder text (lorem ipsum, TODO, N/A), duplicate field values, and enforce minimum word counts.
  • Custom validators -- plug in arbitrary validation functions alongside built-in checks.
  • Three deduplication strategies -- exact match (normalized hash), near-duplicate (Jaccard similarity on n-grams), and semantic (cosine similarity on embeddings via a pluggable embedder).
  • Cross-set deduplication -- remove generated examples that overlap with an existing dataset.
  • Five export formats -- OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, and plain JSONL, with configurable field mappings.
  • Diversity controls -- temperature variation (linear, cycle, random), topic rotation, seed example rotation, negative example generation, and constraint variation.
  • Cost tracking -- track prompt and completion tokens, compute estimated cost per run.
  • Deterministic generation -- seeded PRNG for reproducible template-based output.
  • Full TypeScript support -- all types exported, strict mode compatible.

API Reference

generate(schema, options)

Main pipeline function. Generates examples, validates, deduplicates, and optionally exports.

functiongenerate(schema: ExampleSchema,options: GenerateOptions): Promise<GenerateResult>

Parameters:

ParameterTypeDescription
schemaExampleSchemaSchema defining the shape of each example
optionsGenerateOptionsPipeline configuration (see below)

GenerateOptions:

OptionTypeDefaultDescription
countnumberrequiredNumber of examples to generate
llmLlmFunctionundefinedAsync function that calls an LLM
generateFn(schema, batchIndex) => Record[]undefinedCustom generation callback
batchSizenumber1Examples per LLM call
systemPromptstringundefinedCustom system prompt (use {schema_description} placeholder)
additionalInstructionsstringundefinedExtra instructions appended to the system prompt
seedsRecord<string, unknown>[]undefinedFew-shot seed examples
diversityDiversityConfigundefinedDiversity strategy configuration
validationValidationConfigundefinedValidation and heuristics configuration
retryRetryConfig{ maxRetries: 3 }Retry configuration for LLM failures
dedupDedupOptions{ strategy: 'exact' }Deduplication configuration
invalidHandling'discard' | 'log' | 'repair''discard'How to handle invalid examples
structuredOutputbooleanundefinedRequest JSON mode from the LLM provider
costTrackingCostConfigundefinedToken cost tracking configuration
formatExportFormatundefinedExport format for the exported field in the result

Returns GenerateResult:

FieldTypeDescription
dataGeneratedExample[]Final validated, deduplicated examples
statsGenerationStatsPipeline statistics
exportedstring | undefinedFormatted output string (if format was specified)

validate(examples, schema, config?)

Validate an array of examples against a schema. Returns a ValidationResult for each example.

functionvalidate(examples: Record<string,unknown>[],schema: ExampleSchema,config?: ValidationConfig,): ValidationResult[]

Returns an array of:

interfaceValidationResult{valid: boolean;index: number;errors: ValidationError[];}interfaceValidationError{path: string[];message: string;code: string;}

Validation error codes:required, invalid_type, too_small, too_big, invalid_string, invalid_enum_value, heuristic_non_empty, heuristic_placeholder, heuristic_duplicate_fields, heuristic_min_words, global_min_length, global_max_length, custom_<name>.


validateExample(example, schema, config?)

Validate a single example. Returns an array of ValidationError objects (empty array means valid).

functionvalidateExample(example: Record<string,unknown>,schema: ExampleSchema,config?: ValidationConfig,): ValidationError[]

deduplicate(examples, options?)

Deduplicate an array of examples. Supports exact, near-duplicate, and semantic strategies.

functiondeduplicate(examples: Record<string,unknown>[],options?: Partial<DedupOptions>,): Promise<DedupResult>

DedupOptions:

OptionTypeDefaultDescription
strategy'exact' | 'near' | 'semantic' | 'none''exact'Deduplication strategy
thresholdnumber0.85 (near) / 0.92 (semantic)Similarity threshold for near/semantic dedup
ngramSizenumber2N-gram size for near-duplicate detection
fieldsstring[]all fieldsSubset of fields to compare
embedder(text: string) => Promise<number[]>undefinedEmbedding function (required for semantic strategy)
existingDataRecord<string, unknown>[]undefinedExisting dataset for cross-set deduplication

Returns DedupResult:

interfaceDedupResult{data: Record<string,unknown>[];// Deduplicated examplesremoved: number;// Number of duplicates removedpairs: Array<[number,number,number]>;// [indexA, indexB, similarity]}

exportAs(examples, format, options?)

Export examples to a training-ready format string.

functionexportAs(examples: Record<string,unknown>[],format: ExportFormat,options?: ExportOptions,): string

ExportFormat:'openai' | 'alpaca' | 'sharegpt' | 'csv' | 'jsonl'

ExportOptions:

OptionTypeDefaultDescription
fieldMapRecord<string, string>undefinedMap format roles to your field names
systemPromptstringundefinedStatic system prompt (OpenAI/ShareGPT formats)
delimiterstring','CSV column delimiter
quotestring'"'CSV quote character
headerbooleantrueInclude CSV header row
fieldsstring[]all fieldsSubset of fields to include

Individual Exporters

Each export format is available as a standalone function:

import{exportOpenAI,exportAlpaca,exportShareGPT,exportCSV,exportJSONL}from'synthdata-gen';
FunctionOutput format
exportOpenAI(examples, options?){"messages": [{"role": "system", ...}, {"role": "user", ...}, {"role": "assistant", ...}]} per line
exportAlpaca(examples, options?){"instruction": "...", "input": "...", "output": "..."} per line
exportShareGPT(examples, options?){"conversations": [{"from": "human", "value": "..."}, {"from": "gpt", "value": "..."}]} per line
exportCSV(examples, options?)Comma-separated values with header row
exportJSONL(examples, options?)One JSON object per line

All exporters automatically exclude _meta fields from output.


Generator Utilities

Low-level functions for template-based generation and prompt construction:

import{generateExample,generateExamples,buildSchemaPrompt,buildSystemPrompt,parseJsonResponse,}from'synthdata-gen';
FunctionDescription
generateExample(schema, seed?)Generate a single example from a schema using the built-in template generator. Deterministic when a seed is provided.
generateExamples(schema, count, baseSeed?)Generate multiple examples. Each example uses baseSeed + index for its seed.
buildSchemaPrompt(schema)Compile a schema into a natural-language prompt describing the expected JSON structure.
buildSystemPrompt(schema, customPrompt?, additionalInstructions?)Build the full system prompt for LLM-based generation. Supports a custom prompt template with {schema_description} placeholder.
parseJsonResponse(text)Extract JSON objects/arrays from an LLM response. Handles bare JSON, markdown code fences, and JSON embedded in explanatory text.

Configuration

Schema Definition

A schema defines the structure of each generated example using ExampleSchema:

constschema: ExampleSchema={fields: {question: {type: 'string',min: 10,max: 500,description: 'A natural language question'},answer: {type: 'string',min: 20,max: 2000,pattern: '^[A-Z]'},category: {type: 'enum',enum: ['science','history','technology']},difficulty: {type: 'integer',min: 1,max: 5},score: {type: 'number',min: 0,max: 100},active: {type: 'boolean'},tags: {type: 'array',items: {type: 'string'},min: 1,max: 5},metadata: {type: 'object',properties: {source: {type: 'string'},verified: {type: 'boolean'},},requiredFields: ['source'],},},required: ['question','answer','category'],};

Supported field types:

TypeSchemaField properties
stringmin (min length), max (max length), pattern (regex), description
numbermin, max, description
integermin, max, description
booleandescription
enumenum (valid values array), description
arrayitems (element schema), min (min items), max (max items), description
objectproperties (nested fields), requiredFields (required property names), description

All fields support required (default: true) and default (default value when field is omitted).


Validation Configuration

constconfig: ValidationConfig={// Global string field length constraintsminFieldLength: 10,maxFieldLength: 5000,// Quality heuristicsheuristics: {nonEmpty: true,// Reject empty/whitespace-only required string fieldsnoPlaceholder: true,// Reject placeholder text (lorem ipsum, TODO, TBD, N/A, etc.)noDuplicateFields: {// Reject examples where specified field pairs are identicalpairs: [['question','answer']],},minWordCount: {// Enforce minimum word count on specified fieldsfields: ['answer'],min: 5,},},// Custom validatorscustom: [{name: 'no-question-in-output',validate: (example)=>({valid: !String(example.answer).endsWith('?'),message: 'Answer should not end with a question mark',}),},],};

The noDuplicateFields and minWordCount heuristics also accept true to use automatic inference: noDuplicateFields: true pairs common field name patterns (question/answer, instruction/output, input/output, prompt/response, query/response), and minWordCount: true applies a default minimum of 3 words to all string fields.


Diversity Configuration

constdiversity: DiversityConfig={temperature: {min: 0.3,max: 1.2,strategy: 'cycle',// 'linear' | 'cycle' | 'random'},topics: ['algorithms','databases','networking','security'],negativeExampleRatio: 0.1,negativeInstructions: 'Generate an example with a subtle factual error.',constraintVariation: [{instruction: 'Write in a formal academic tone.'},{instruction: 'Write in a casual conversational tone.'},],};

Retry Configuration

constretry: RetryConfig={maxRetries: 3,// Maximum retry attempts per batchincludeFeedback: true,// Include validation error feedback in retry promptbackoff: 'exponential',// 'none' | 'linear' | 'exponential'backoffMs: 1000,// Base backoff delay in milliseconds};

Cost Tracking

constcostTracking: CostConfig={promptTokenCost: 0.000003,// Cost per prompt tokencompletionTokenCost: 0.000015,// Cost per completion tokencurrency: 'USD',};

The GenerationStats.cost field in the result contains promptTokens, completionTokens, totalCost, and currency.


Error Handling

Validation Errors

The validate and validateExample functions return structured error objects rather than throwing. Each ValidationError includes:

  • path -- array of field names locating the error (e.g., ['address', 'zip'] for nested fields, ['tags', '0'] for array elements)
  • message -- human-readable description of the failure
  • code -- machine-readable error code for programmatic handling
import{validateExample}from'synthdata-gen';consterrors=validateExample({instruction: 'Hi',output: 123,category: 'invalid'},schema,);for(consterroferrors){console.log(`[${err.code}] ${err.path.join('.')}: ${err.message}`);}// [too_small] instruction: String must contain at least 10 character(s), received 2// [invalid_type] output: Expected string, received number// [invalid_enum_value] category: Invalid enum value. Expected one of ["coding", ...], received "invalid"

Pipeline Error Handling

The generate function handles LLM failures internally using the retry configuration. Invalid examples are handled according to the invalidHandling option:

  • 'discard' (default) -- silently drops invalid examples
  • 'log' -- discards but records invalid examples and their errors in stats.invalidExamples
  • 'repair' -- includes invalid examples in the output with _meta.repaired: true

Validation error counts are always available in stats.validationErrors regardless of the handling mode.

Deduplication Errors

The deduplicate function throws an Error if the semantic strategy is used without providing an embedder function:

// Throws: "Semantic dedup requires an embedder function"awaitdeduplicate(examples,{strategy: 'semantic'});

Export Errors

The exportAs function throws an Error for unsupported format strings:

// Throws: "Unsupported export format: xml"exportAs(examples,'xml'asExportFormat);

Advanced Usage

Custom System Prompt

Override the default system prompt using the {schema_description} placeholder:

constresult=awaitgenerate(schema,{count: 100,llm: myLlm,systemPrompt: 'You are a medical expert generating training data.\n\n{schema_description}',additionalInstructions: 'All examples must be about cardiology.',});

Cross-Set Deduplication

Remove generated examples that duplicate entries in an existing dataset:

import{deduplicate}from'synthdata-gen';constresult=awaitdeduplicate(newExamples,{strategy: 'exact',existingData: existingDataset,});console.log(`Removed ${result.removed} duplicates of existing data`);

Semantic Deduplication

Provide an embedding function for meaning-level deduplication:

constresult=awaitdeduplicate(examples,{strategy: 'semantic',threshold: 0.92,embedder: async(text)=>{constresponse=awaitopenai.embeddings.create({model: 'text-embedding-3-small',input: text,});returnresponse.data[0].embedding;},});

Field-Specific Deduplication

Deduplicate based on a subset of fields:

constresult=awaitdeduplicate(examples,{strategy: 'near',threshold: 0.85,ngramSize: 2,fields: ['instruction'],// Only compare instruction fields});

Custom Field Mapping for Export

Map your schema fields to the roles expected by each export format:

import{exportOpenAI,exportAlpaca}from'synthdata-gen';constqaData=[{question: 'What is TCP?',answer: 'TCP is a connection-oriented protocol.'},];// Map question -> user, answer -> assistantconstopenai=exportOpenAI(qaData,{fieldMap: {user: 'question',assistant: 'answer'},systemPrompt: 'You are a networking expert.',});// Map question -> instruction, answer -> outputconstalpaca=exportAlpaca(qaData,{fieldMap: {instruction: 'question',output: 'answer'},});

Batch Generation with LLM

Request multiple examples per LLM call to reduce API costs:

constresult=awaitgenerate(schema,{llm: myLlm,count: 1000,batchSize: 10,// 10 examples per LLM callstructuredOutput: true,// Request JSON mode if provider supports it});

Standalone Template Generation

Use the template generator directly without the full pipeline:

import{generateExample,generateExamples}from'synthdata-gen';// Single example, deterministic with seedconstexample=generateExample(schema,42);// Multiple examples, deterministic with base seedconstexamples=generateExamples(schema,100,42);

Building Prompts for External Use

Generate the prompt that would be sent to an LLM, without calling one:

import{buildSchemaPrompt,buildSystemPrompt}from'synthdata-gen';constschemaPrompt=buildSchemaPrompt(schema);// "Generate a JSON object with the following structure:\n{ ... }"constsystemPrompt=buildSystemPrompt(schema,undefined,'Focus on edge cases.');// Full system prompt with schema description and additional instructions

Parsing LLM Responses

Extract JSON from messy LLM output:

import{parseJsonResponse}from'synthdata-gen';constobjects=parseJsonResponse('Here is the result:\n```json\n{"key": "value"}\n```\nDone!');// [{ key: "value" }]constarray=parseJsonResponse('[{"a": 1}, {"b": 2}]');// [{ a: 1 }, { b: 2 }]

TypeScript

All types are exported from the package entry point:

importtype{// LLM interfaceMessage,LlmCallOptions,LlmResponse,LlmFunction,// SchemaFieldType,SchemaField,ExampleSchema,// GenerationGeneratedExample,DiversityConfig,HeuristicsConfig,CustomValidator,ValidationConfig,RetryConfig,CostConfig,DedupOptions,GenerateOptions,ExportFormat,ExportOptions,// ResultsGenerateResult,GenerationStats,ValidationResult,ValidationError,DedupResult,}from'synthdata-gen';

The GeneratedExample<T> type is generic. By default it is Record<string, unknown> & { _meta?: ... }. You can narrow it with your own type:

interfaceQAPair{question: string;answer: string;category: string;}constresult=awaitgenerate(schema,{count: 10});constdata=result.dataasGeneratedExample<QAPair>[];

License

MIT

About

Generate and validate synthetic training data using any LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

synthdata-gen

Generate, validate, deduplicate, and export synthetic training data for LLM fine-tuning and evaluation.

npm versionnpm downloadslicensenodetypes


Description

synthdata-gen is a complete pipeline for producing synthetic training data. Define a schema describing the shape of each example, optionally plug in any LLM, and the library handles generation, output parsing, schema validation, quality heuristics, deduplication, and export to training-ready formats (OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, plain JSONL).

The library works in three modes:

  • Template-based generation -- no LLM required. A built-in deterministic generator produces examples matching your schema using seeded pseudo-random values. Useful for testing pipelines, prototyping schemas, and generating placeholder data.
  • LLM-based generation -- provide any async function that calls an LLM. The pipeline builds prompts from your schema, parses structured output from LLM responses (including JSON embedded in markdown fences), retries on failure, and tracks token usage and cost.
  • Custom generation -- provide your own generateFn callback for full control over how examples are produced, while still benefiting from the validation, dedup, and export stages.

Each pipeline stage (generation, validation, deduplication, export) is independently usable as a standalone function.


Installation

npm install synthdata-gen

Requires Node.js >= 18.


Quick Start

Template-based generation (no LLM required)

import{generate}from'synthdata-gen';importtype{ExampleSchema}from'synthdata-gen';constschema: ExampleSchema={fields: {instruction: {type: 'string',min: 10,max: 200,description: 'A clear instruction'},output: {type: 'string',min: 20,max: 1000,description: 'The expected response'},category: {type: 'enum',enum: ['coding','writing','reasoning']},},};constresult=awaitgenerate(schema,{count: 100});console.log(result.data);// GeneratedExample[]console.log(result.stats);// GenerationStats

LLM-based generation

import{generate}from'synthdata-gen';importtype{LlmFunction}from'synthdata-gen';constmyLlm: LlmFunction=async(messages,options)=>{constresponse=awaitcallMyProvider(messages,options);return{content: response.text,usage: {promptTokens: response.usage.prompt_tokens,completionTokens: response.usage.completion_tokens,totalTokens: response.usage.total_tokens,},};};constresult=awaitgenerate(schema,{llm: myLlm,count: 500,batchSize: 5,format: 'openai',seeds: [{instruction: 'Explain recursion',output: 'Recursion is when a function calls itself...',category: 'coding'},],costTracking: {promptTokenCost: 0.000003,completionTokenCost: 0.000015,currency: 'USD',},});console.log(result.exported);// OpenAI fine-tuning JSONL stringconsole.log(result.stats.cost);// { promptTokens, completionTokens, totalCost, currency }console.log(result.stats.durationMs);// wall-clock time

Features

  • Schema-driven generation -- define field types, constraints, descriptions, and required fields; the library compiles schemas into LLM prompts and validates output against them.
  • Three generation modes -- template-based (no LLM), LLM-based (any provider), or custom callback.
  • Robust LLM output parsing -- extracts JSON from bare responses, markdown code fences, and mixed text.
  • Schema validation -- type checking, string length constraints, numeric ranges, regex patterns, enum membership, array bounds, and nested object validation.
  • Quality heuristics -- detect empty fields, placeholder text (lorem ipsum, TODO, N/A), duplicate field values, and enforce minimum word counts.
  • Custom validators -- plug in arbitrary validation functions alongside built-in checks.
  • Three deduplication strategies -- exact match (normalized hash), near-duplicate (Jaccard similarity on n-grams), and semantic (cosine similarity on embeddings via a pluggable embedder).
  • Cross-set deduplication -- remove generated examples that overlap with an existing dataset.
  • Five export formats -- OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, and plain JSONL, with configurable field mappings.
  • Diversity controls -- temperature variation (linear, cycle, random), topic rotation, seed example rotation, negative example generation, and constraint variation.
  • Cost tracking -- track prompt and completion tokens, compute estimated cost per run.
  • Deterministic generation -- seeded PRNG for reproducible template-based output.
  • Full TypeScript support -- all types exported, strict mode compatible.

API Reference

generate(schema, options)

Main pipeline function. Generates examples, validates, deduplicates, and optionally exports.

functiongenerate(schema: ExampleSchema,options: GenerateOptions): Promise<GenerateResult>

Parameters:

ParameterTypeDescription
schemaExampleSchemaSchema defining the shape of each example
optionsGenerateOptionsPipeline configuration (see below)

GenerateOptions:

OptionTypeDefaultDescription
countnumberrequiredNumber of examples to generate
llmLlmFunctionundefinedAsync function that calls an LLM
generateFn(schema, batchIndex) => Record[]undefinedCustom generation callback
batchSizenumber1Examples per LLM call
systemPromptstringundefinedCustom system prompt (use {schema_description} placeholder)
additionalInstructionsstringundefinedExtra instructions appended to the system prompt
seedsRecord<string, unknown>[]undefinedFew-shot seed examples
diversityDiversityConfigundefinedDiversity strategy configuration
validationValidationConfigundefinedValidation and heuristics configuration
retryRetryConfig{ maxRetries: 3 }Retry configuration for LLM failures
dedupDedupOptions{ strategy: 'exact' }Deduplication configuration
invalidHandling'discard' | 'log' | 'repair''discard'How to handle invalid examples
structuredOutputbooleanundefinedRequest JSON mode from the LLM provider
costTrackingCostConfigundefinedToken cost tracking configuration
formatExportFormatundefinedExport format for the exported field in the result

Returns GenerateResult:

FieldTypeDescription
dataGeneratedExample[]Final validated, deduplicated examples
statsGenerationStatsPipeline statistics
exportedstring | undefinedFormatted output string (if format was specified)

validate(examples, schema, config?)

Validate an array of examples against a schema. Returns a ValidationResult for each example.

functionvalidate(examples: Record<string,unknown>[],schema: ExampleSchema,config?: ValidationConfig,): ValidationResult[]

Returns an array of:

interfaceValidationResult{valid: boolean;index: number;errors: ValidationError[];}interfaceValidationError{path: string[];message: string;code: string;}

Validation error codes:required, invalid_type, too_small, too_big, invalid_string, invalid_enum_value, heuristic_non_empty, heuristic_placeholder, heuristic_duplicate_fields, heuristic_min_words, global_min_length, global_max_length, custom_<name>.


validateExample(example, schema, config?)

Validate a single example. Returns an array of ValidationError objects (empty array means valid).

functionvalidateExample(example: Record<string,unknown>,schema: ExampleSchema,config?: ValidationConfig,): ValidationError[]

deduplicate(examples, options?)

Deduplicate an array of examples. Supports exact, near-duplicate, and semantic strategies.

functiondeduplicate(examples: Record<string,unknown>[],options?: Partial<DedupOptions>,): Promise<DedupResult>

DedupOptions:

OptionTypeDefaultDescription
strategy'exact' | 'near' | 'semantic' | 'none''exact'Deduplication strategy
thresholdnumber0.85 (near) / 0.92 (semantic)Similarity threshold for near/semantic dedup
ngramSizenumber2N-gram size for near-duplicate detection
fieldsstring[]all fieldsSubset of fields to compare
embedder(text: string) => Promise<number[]>undefinedEmbedding function (required for semantic strategy)
existingDataRecord<string, unknown>[]undefinedExisting dataset for cross-set deduplication

Returns DedupResult:

interfaceDedupResult{data: Record<string,unknown>[];// Deduplicated examplesremoved: number;// Number of duplicates removedpairs: Array<[number,number,number]>;// [indexA, indexB, similarity]}

exportAs(examples, format, options?)

Export examples to a training-ready format string.

functionexportAs(examples: Record<string,unknown>[],format: ExportFormat,options?: ExportOptions,): string

ExportFormat:'openai' | 'alpaca' | 'sharegpt' | 'csv' | 'jsonl'

ExportOptions:

OptionTypeDefaultDescription
fieldMapRecord<string, string>undefinedMap format roles to your field names
systemPromptstringundefinedStatic system prompt (OpenAI/ShareGPT formats)
delimiterstring','CSV column delimiter
quotestring'"'CSV quote character
headerbooleantrueInclude CSV header row
fieldsstring[]all fieldsSubset of fields to include

Individual Exporters

Each export format is available as a standalone function:

import{exportOpenAI,exportAlpaca,exportShareGPT,exportCSV,exportJSONL}from'synthdata-gen';
FunctionOutput format
exportOpenAI(examples, options?){"messages": [{"role": "system", ...}, {"role": "user", ...}, {"role": "assistant", ...}]} per line
exportAlpaca(examples, options?){"instruction": "...", "input": "...", "output": "..."} per line
exportShareGPT(examples, options?){"conversations": [{"from": "human", "value": "..."}, {"from": "gpt", "value": "..."}]} per line
exportCSV(examples, options?)Comma-separated values with header row
exportJSONL(examples, options?)One JSON object per line

All exporters automatically exclude _meta fields from output.


Generator Utilities

Low-level functions for template-based generation and prompt construction:

import{generateExample,generateExamples,buildSchemaPrompt,buildSystemPrompt,parseJsonResponse,}from'synthdata-gen';
FunctionDescription
generateExample(schema, seed?)Generate a single example from a schema using the built-in template generator. Deterministic when a seed is provided.
generateExamples(schema, count, baseSeed?)Generate multiple examples. Each example uses baseSeed + index for its seed.
buildSchemaPrompt(schema)Compile a schema into a natural-language prompt describing the expected JSON structure.
buildSystemPrompt(schema, customPrompt?, additionalInstructions?)Build the full system prompt for LLM-based generation. Supports a custom prompt template with {schema_description} placeholder.
parseJsonResponse(text)Extract JSON objects/arrays from an LLM response. Handles bare JSON, markdown code fences, and JSON embedded in explanatory text.

Configuration

Schema Definition

A schema defines the structure of each generated example using ExampleSchema:

constschema: ExampleSchema={fields: {question: {type: 'string',min: 10,max: 500,description: 'A natural language question'},answer: {type: 'string',min: 20,max: 2000,pattern: '^[A-Z]'},category: {type: 'enum',enum: ['science','history','technology']},difficulty: {type: 'integer',min: 1,max: 5},score: {type: 'number',min: 0,max: 100},active: {type: 'boolean'},tags: {type: 'array',items: {type: 'string'},min: 1,max: 5},metadata: {type: 'object',properties: {source: {type: 'string'},verified: {type: 'boolean'},},requiredFields: ['source'],},},required: ['question','answer','category'],};

Supported field types:

TypeSchemaField properties
stringmin (min length), max (max length), pattern (regex), description
numbermin, max, description
integermin, max, description
booleandescription
enumenum (valid values array), description
arrayitems (element schema), min (min items), max (max items), description
objectproperties (nested fields), requiredFields (required property names), description

All fields support required (default: true) and default (default value when field is omitted).


Validation Configuration

constconfig: ValidationConfig={// Global string field length constraintsminFieldLength: 10,maxFieldLength: 5000,// Quality heuristicsheuristics: {nonEmpty: true,// Reject empty/whitespace-only required string fieldsnoPlaceholder: true,// Reject placeholder text (lorem ipsum, TODO, TBD, N/A, etc.)noDuplicateFields: {// Reject examples where specified field pairs are identicalpairs: [['question','answer']],},minWordCount: {// Enforce minimum word count on specified fieldsfields: ['answer'],min: 5,},},// Custom validatorscustom: [{name: 'no-question-in-output',validate: (example)=>({valid: !String(example.answer).endsWith('?'),message: 'Answer should not end with a question mark',}),},],};

The noDuplicateFields and minWordCount heuristics also accept true to use automatic inference: noDuplicateFields: true pairs common field name patterns (question/answer, instruction/output, input/output, prompt/response, query/response), and minWordCount: true applies a default minimum of 3 words to all string fields.


Diversity Configuration

constdiversity: DiversityConfig={temperature: {min: 0.3,max: 1.2,strategy: 'cycle',// 'linear' | 'cycle' | 'random'},topics: ['algorithms','databases','networking','security'],negativeExampleRatio: 0.1,negativeInstructions: 'Generate an example with a subtle factual error.',constraintVariation: [{instruction: 'Write in a formal academic tone.'},{instruction: 'Write in a casual conversational tone.'},],};

Retry Configuration

constretry: RetryConfig={maxRetries: 3,// Maximum retry attempts per batchincludeFeedback: true,// Include validation error feedback in retry promptbackoff: 'exponential',// 'none' | 'linear' | 'exponential'backoffMs: 1000,// Base backoff delay in milliseconds};

Cost Tracking

constcostTracking: CostConfig={promptTokenCost: 0.000003,// Cost per prompt tokencompletionTokenCost: 0.000015,// Cost per completion tokencurrency: 'USD',};

The GenerationStats.cost field in the result contains promptTokens, completionTokens, totalCost, and currency.


Error Handling

Validation Errors

The validate and validateExample functions return structured error objects rather than throwing. Each ValidationError includes:

  • path -- array of field names locating the error (e.g., ['address', 'zip'] for nested fields, ['tags', '0'] for array elements)
  • message -- human-readable description of the failure
  • code -- machine-readable error code for programmatic handling
import{validateExample}from'synthdata-gen';consterrors=validateExample({instruction: 'Hi',output: 123,category: 'invalid'},schema,);for(consterroferrors){console.log(`[${err.code}] ${err.path.join('.')}: ${err.message}`);}// [too_small] instruction: String must contain at least 10 character(s), received 2// [invalid_type] output: Expected string, received number// [invalid_enum_value] category: Invalid enum value. Expected one of ["coding", ...], received "invalid"

Pipeline Error Handling

The generate function handles LLM failures internally using the retry configuration. Invalid examples are handled according to the invalidHandling option:

  • 'discard' (default) -- silently drops invalid examples
  • 'log' -- discards but records invalid examples and their errors in stats.invalidExamples
  • 'repair' -- includes invalid examples in the output with _meta.repaired: true

Validation error counts are always available in stats.validationErrors regardless of the handling mode.

Deduplication Errors

The deduplicate function throws an Error if the semantic strategy is used without providing an embedder function:

// Throws: "Semantic dedup requires an embedder function"awaitdeduplicate(examples,{strategy: 'semantic'});

Export Errors

The exportAs function throws an Error for unsupported format strings:

// Throws: "Unsupported export format: xml"exportAs(examples,'xml'asExportFormat);

Advanced Usage

Custom System Prompt

Override the default system prompt using the {schema_description} placeholder:

constresult=awaitgenerate(schema,{count: 100,llm: myLlm,systemPrompt: 'You are a medical expert generating training data.\n\n{schema_description}',additionalInstructions: 'All examples must be about cardiology.',});

Cross-Set Deduplication

Remove generated examples that duplicate entries in an existing dataset:

import{deduplicate}from'synthdata-gen';constresult=awaitdeduplicate(newExamples,{strategy: 'exact',existingData: existingDataset,});console.log(`Removed ${result.removed} duplicates of existing data`);

Semantic Deduplication

Provide an embedding function for meaning-level deduplication:

constresult=awaitdeduplicate(examples,{strategy: 'semantic',threshold: 0.92,embedder: async(text)=>{constresponse=awaitopenai.embeddings.create({model: 'text-embedding-3-small',input: text,});returnresponse.data[0].embedding;},});

Field-Specific Deduplication

Deduplicate based on a subset of fields:

constresult=awaitdeduplicate(examples,{strategy: 'near',threshold: 0.85,ngramSize: 2,fields: ['instruction'],// Only compare instruction fields});

Custom Field Mapping for Export

Map your schema fields to the roles expected by each export format:

import{exportOpenAI,exportAlpaca}from'synthdata-gen';constqaData=[{question: 'What is TCP?',answer: 'TCP is a connection-oriented protocol.'},];// Map question -> user, answer -> assistantconstopenai=exportOpenAI(qaData,{fieldMap: {user: 'question',assistant: 'answer'},systemPrompt: 'You are a networking expert.',});// Map question -> instruction, answer -> outputconstalpaca=exportAlpaca(qaData,{fieldMap: {instruction: 'question',output: 'answer'},});

Batch Generation with LLM

Request multiple examples per LLM call to reduce API costs:

constresult=awaitgenerate(schema,{llm: myLlm,count: 1000,batchSize: 10,// 10 examples per LLM callstructuredOutput: true,// Request JSON mode if provider supports it});

Standalone Template Generation

Use the template generator directly without the full pipeline:

import{generateExample,generateExamples}from'synthdata-gen';// Single example, deterministic with seedconstexample=generateExample(schema,42);// Multiple examples, deterministic with base seedconstexamples=generateExamples(schema,100,42);

Building Prompts for External Use

Generate the prompt that would be sent to an LLM, without calling one:

import{buildSchemaPrompt,buildSystemPrompt}from'synthdata-gen';constschemaPrompt=buildSchemaPrompt(schema);// "Generate a JSON object with the following structure:\n{ ... }"constsystemPrompt=buildSystemPrompt(schema,undefined,'Focus on edge cases.');// Full system prompt with schema description and additional instructions

Parsing LLM Responses

Extract JSON from messy LLM output:

import{parseJsonResponse}from'synthdata-gen';constobjects=parseJsonResponse('Here is the result:\n```json\n{"key": "value"}\n```\nDone!');// [{ key: "value" }]constarray=parseJsonResponse('[{"a": 1}, {"b": 2}]');// [{ a: 1 }, { b: 2 }]

TypeScript

All types are exported from the package entry point:

importtype{// LLM interfaceMessage,LlmCallOptions,LlmResponse,LlmFunction,// SchemaFieldType,SchemaField,ExampleSchema,// GenerationGeneratedExample,DiversityConfig,HeuristicsConfig,CustomValidator,ValidationConfig,RetryConfig,CostConfig,DedupOptions,GenerateOptions,ExportFormat,ExportOptions,// ResultsGenerateResult,GenerationStats,ValidationResult,ValidationError,DedupResult,}from'synthdata-gen';

The GeneratedExample<T> type is generic. By default it is Record<string, unknown> & { _meta?: ... }. You can narrow it with your own type:

interfaceQAPair{question: string;answer: string;category: string;}constresult=awaitgenerate(schema,{count: 10});constdata=result.dataasGeneratedExample<QAPair>[];

License

MIT

About

Generate and validate synthetic training data using any LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

synthdata-gen

Generate, validate, deduplicate, and export synthetic training data for LLM fine-tuning and evaluation.

npm versionnpm downloadslicensenodetypes


Description

synthdata-gen is a complete pipeline for producing synthetic training data. Define a schema describing the shape of each example, optionally plug in any LLM, and the library handles generation, output parsing, schema validation, quality heuristics, deduplication, and export to training-ready formats (OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, plain JSONL).

The library works in three modes:

  • Template-based generation -- no LLM required. A built-in deterministic generator produces examples matching your schema using seeded pseudo-random values. Useful for testing pipelines, prototyping schemas, and generating placeholder data.
  • LLM-based generation -- provide any async function that calls an LLM. The pipeline builds prompts from your schema, parses structured output from LLM responses (including JSON embedded in markdown fences), retries on failure, and tracks token usage and cost.
  • Custom generation -- provide your own generateFn callback for full control over how examples are produced, while still benefiting from the validation, dedup, and export stages.

Each pipeline stage (generation, validation, deduplication, export) is independently usable as a standalone function.


Installation

npm install synthdata-gen

Requires Node.js >= 18.


Quick Start

Template-based generation (no LLM required)

import{generate}from'synthdata-gen';importtype{ExampleSchema}from'synthdata-gen';constschema: ExampleSchema={fields: {instruction: {type: 'string',min: 10,max: 200,description: 'A clear instruction'},output: {type: 'string',min: 20,max: 1000,description: 'The expected response'},category: {type: 'enum',enum: ['coding','writing','reasoning']},},};constresult=awaitgenerate(schema,{count: 100});console.log(result.data);// GeneratedExample[]console.log(result.stats);// GenerationStats

LLM-based generation

import{generate}from'synthdata-gen';importtype{LlmFunction}from'synthdata-gen';constmyLlm: LlmFunction=async(messages,options)=>{constresponse=awaitcallMyProvider(messages,options);return{content: response.text,usage: {promptTokens: response.usage.prompt_tokens,completionTokens: response.usage.completion_tokens,totalTokens: response.usage.total_tokens,},};};constresult=awaitgenerate(schema,{llm: myLlm,count: 500,batchSize: 5,format: 'openai',seeds: [{instruction: 'Explain recursion',output: 'Recursion is when a function calls itself...',category: 'coding'},],costTracking: {promptTokenCost: 0.000003,completionTokenCost: 0.000015,currency: 'USD',},});console.log(result.exported);// OpenAI fine-tuning JSONL stringconsole.log(result.stats.cost);// { promptTokens, completionTokens, totalCost, currency }console.log(result.stats.durationMs);// wall-clock time

Features

  • Schema-driven generation -- define field types, constraints, descriptions, and required fields; the library compiles schemas into LLM prompts and validates output against them.
  • Three generation modes -- template-based (no LLM), LLM-based (any provider), or custom callback.
  • Robust LLM output parsing -- extracts JSON from bare responses, markdown code fences, and mixed text.
  • Schema validation -- type checking, string length constraints, numeric ranges, regex patterns, enum membership, array bounds, and nested object validation.
  • Quality heuristics -- detect empty fields, placeholder text (lorem ipsum, TODO, N/A), duplicate field values, and enforce minimum word counts.
  • Custom validators -- plug in arbitrary validation functions alongside built-in checks.
  • Three deduplication strategies -- exact match (normalized hash), near-duplicate (Jaccard similarity on n-grams), and semantic (cosine similarity on embeddings via a pluggable embedder).
  • Cross-set deduplication -- remove generated examples that overlap with an existing dataset.
  • Five export formats -- OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, and plain JSONL, with configurable field mappings.
  • Diversity controls -- temperature variation (linear, cycle, random), topic rotation, seed example rotation, negative example generation, and constraint variation.
  • Cost tracking -- track prompt and completion tokens, compute estimated cost per run.
  • Deterministic generation -- seeded PRNG for reproducible template-based output.
  • Full TypeScript support -- all types exported, strict mode compatible.

API Reference

generate(schema, options)

Main pipeline function. Generates examples, validates, deduplicates, and optionally exports.

functiongenerate(schema: ExampleSchema,options: GenerateOptions): Promise<GenerateResult>

Parameters:

ParameterTypeDescription
schemaExampleSchemaSchema defining the shape of each example
optionsGenerateOptionsPipeline configuration (see below)

GenerateOptions:

OptionTypeDefaultDescription
countnumberrequiredNumber of examples to generate
llmLlmFunctionundefinedAsync function that calls an LLM
generateFn(schema, batchIndex) => Record[]undefinedCustom generation callback
batchSizenumber1Examples per LLM call
systemPromptstringundefinedCustom system prompt (use {schema_description} placeholder)
additionalInstructionsstringundefinedExtra instructions appended to the system prompt
seedsRecord<string, unknown>[]undefinedFew-shot seed examples
diversityDiversityConfigundefinedDiversity strategy configuration
validationValidationConfigundefinedValidation and heuristics configuration
retryRetryConfig{ maxRetries: 3 }Retry configuration for LLM failures
dedupDedupOptions{ strategy: 'exact' }Deduplication configuration
invalidHandling'discard' | 'log' | 'repair''discard'How to handle invalid examples
structuredOutputbooleanundefinedRequest JSON mode from the LLM provider
costTrackingCostConfigundefinedToken cost tracking configuration
formatExportFormatundefinedExport format for the exported field in the result

Returns GenerateResult:

FieldTypeDescription
dataGeneratedExample[]Final validated, deduplicated examples
statsGenerationStatsPipeline statistics
exportedstring | undefinedFormatted output string (if format was specified)

validate(examples, schema, config?)

Validate an array of examples against a schema. Returns a ValidationResult for each example.

functionvalidate(examples: Record<string,unknown>[],schema: ExampleSchema,config?: ValidationConfig,): ValidationResult[]

Returns an array of:

interfaceValidationResult{valid: boolean;index: number;errors: ValidationError[];}interfaceValidationError{path: string[];message: string;code: string;}

Validation error codes:required, invalid_type, too_small, too_big, invalid_string, invalid_enum_value, heuristic_non_empty, heuristic_placeholder, heuristic_duplicate_fields, heuristic_min_words, global_min_length, global_max_length, custom_<name>.


validateExample(example, schema, config?)

Validate a single example. Returns an array of ValidationError objects (empty array means valid).

functionvalidateExample(example: Record<string,unknown>,schema: ExampleSchema,config?: ValidationConfig,): ValidationError[]

deduplicate(examples, options?)

Deduplicate an array of examples. Supports exact, near-duplicate, and semantic strategies.

functiondeduplicate(examples: Record<string,unknown>[],options?: Partial<DedupOptions>,): Promise<DedupResult>

DedupOptions:

OptionTypeDefaultDescription
strategy'exact' | 'near' | 'semantic' | 'none''exact'Deduplication strategy
thresholdnumber0.85 (near) / 0.92 (semantic)Similarity threshold for near/semantic dedup
ngramSizenumber2N-gram size for near-duplicate detection
fieldsstring[]all fieldsSubset of fields to compare
embedder(text: string) => Promise<number[]>undefinedEmbedding function (required for semantic strategy)
existingDataRecord<string, unknown>[]undefinedExisting dataset for cross-set deduplication

Returns DedupResult:

interfaceDedupResult{data: Record<string,unknown>[];// Deduplicated examplesremoved: number;// Number of duplicates removedpairs: Array<[number,number,number]>;// [indexA, indexB, similarity]}

exportAs(examples, format, options?)

Export examples to a training-ready format string.

functionexportAs(examples: Record<string,unknown>[],format: ExportFormat,options?: ExportOptions,): string

ExportFormat:'openai' | 'alpaca' | 'sharegpt' | 'csv' | 'jsonl'

ExportOptions:

OptionTypeDefaultDescription
fieldMapRecord<string, string>undefinedMap format roles to your field names
systemPromptstringundefinedStatic system prompt (OpenAI/ShareGPT formats)
delimiterstring','CSV column delimiter
quotestring'"'CSV quote character
headerbooleantrueInclude CSV header row
fieldsstring[]all fieldsSubset of fields to include

Individual Exporters

Each export format is available as a standalone function:

import{exportOpenAI,exportAlpaca,exportShareGPT,exportCSV,exportJSONL}from'synthdata-gen';
FunctionOutput format
exportOpenAI(examples, options?){"messages": [{"role": "system", ...}, {"role": "user", ...}, {"role": "assistant", ...}]} per line
exportAlpaca(examples, options?){"instruction": "...", "input": "...", "output": "..."} per line
exportShareGPT(examples, options?){"conversations": [{"from": "human", "value": "..."}, {"from": "gpt", "value": "..."}]} per line
exportCSV(examples, options?)Comma-separated values with header row
exportJSONL(examples, options?)One JSON object per line

All exporters automatically exclude _meta fields from output.


Generator Utilities

Low-level functions for template-based generation and prompt construction:

import{generateExample,generateExamples,buildSchemaPrompt,buildSystemPrompt,parseJsonResponse,}from'synthdata-gen';
FunctionDescription
generateExample(schema, seed?)Generate a single example from a schema using the built-in template generator. Deterministic when a seed is provided.
generateExamples(schema, count, baseSeed?)Generate multiple examples. Each example uses baseSeed + index for its seed.
buildSchemaPrompt(schema)Compile a schema into a natural-language prompt describing the expected JSON structure.
buildSystemPrompt(schema, customPrompt?, additionalInstructions?)Build the full system prompt for LLM-based generation. Supports a custom prompt template with {schema_description} placeholder.
parseJsonResponse(text)Extract JSON objects/arrays from an LLM response. Handles bare JSON, markdown code fences, and JSON embedded in explanatory text.

Configuration

Schema Definition

A schema defines the structure of each generated example using ExampleSchema:

constschema: ExampleSchema={fields: {question: {type: 'string',min: 10,max: 500,description: 'A natural language question'},answer: {type: 'string',min: 20,max: 2000,pattern: '^[A-Z]'},category: {type: 'enum',enum: ['science','history','technology']},difficulty: {type: 'integer',min: 1,max: 5},score: {type: 'number',min: 0,max: 100},active: {type: 'boolean'},tags: {type: 'array',items: {type: 'string'},min: 1,max: 5},metadata: {type: 'object',properties: {source: {type: 'string'},verified: {type: 'boolean'},},requiredFields: ['source'],},},required: ['question','answer','category'],};

Supported field types:

TypeSchemaField properties
stringmin (min length), max (max length), pattern (regex), description
numbermin, max, description
integermin, max, description
booleandescription
enumenum (valid values array), description
arrayitems (element schema), min (min items), max (max items), description
objectproperties (nested fields), requiredFields (required property names), description

All fields support required (default: true) and default (default value when field is omitted).


Validation Configuration

constconfig: ValidationConfig={// Global string field length constraintsminFieldLength: 10,maxFieldLength: 5000,// Quality heuristicsheuristics: {nonEmpty: true,// Reject empty/whitespace-only required string fieldsnoPlaceholder: true,// Reject placeholder text (lorem ipsum, TODO, TBD, N/A, etc.)noDuplicateFields: {// Reject examples where specified field pairs are identicalpairs: [['question','answer']],},minWordCount: {// Enforce minimum word count on specified fieldsfields: ['answer'],min: 5,},},// Custom validatorscustom: [{name: 'no-question-in-output',validate: (example)=>({valid: !String(example.answer).endsWith('?'),message: 'Answer should not end with a question mark',}),},],};

The noDuplicateFields and minWordCount heuristics also accept true to use automatic inference: noDuplicateFields: true pairs common field name patterns (question/answer, instruction/output, input/output, prompt/response, query/response), and minWordCount: true applies a default minimum of 3 words to all string fields.


Diversity Configuration

constdiversity: DiversityConfig={temperature: {min: 0.3,max: 1.2,strategy: 'cycle',// 'linear' | 'cycle' | 'random'},topics: ['algorithms','databases','networking','security'],negativeExampleRatio: 0.1,negativeInstructions: 'Generate an example with a subtle factual error.',constraintVariation: [{instruction: 'Write in a formal academic tone.'},{instruction: 'Write in a casual conversational tone.'},],};

Retry Configuration

constretry: RetryConfig={maxRetries: 3,// Maximum retry attempts per batchincludeFeedback: true,// Include validation error feedback in retry promptbackoff: 'exponential',// 'none' | 'linear' | 'exponential'backoffMs: 1000,// Base backoff delay in milliseconds};

Cost Tracking

constcostTracking: CostConfig={promptTokenCost: 0.000003,// Cost per prompt tokencompletionTokenCost: 0.000015,// Cost per completion tokencurrency: 'USD',};

The GenerationStats.cost field in the result contains promptTokens, completionTokens, totalCost, and currency.


Error Handling

Validation Errors

The validate and validateExample functions return structured error objects rather than throwing. Each ValidationError includes:

  • path -- array of field names locating the error (e.g., ['address', 'zip'] for nested fields, ['tags', '0'] for array elements)
  • message -- human-readable description of the failure
  • code -- machine-readable error code for programmatic handling
import{validateExample}from'synthdata-gen';consterrors=validateExample({instruction: 'Hi',output: 123,category: 'invalid'},schema,);for(consterroferrors){console.log(`[${err.code}] ${err.path.join('.')}: ${err.message}`);}// [too_small] instruction: String must contain at least 10 character(s), received 2// [invalid_type] output: Expected string, received number// [invalid_enum_value] category: Invalid enum value. Expected one of ["coding", ...], received "invalid"

Pipeline Error Handling

The generate function handles LLM failures internally using the retry configuration. Invalid examples are handled according to the invalidHandling option:

  • 'discard' (default) -- silently drops invalid examples
  • 'log' -- discards but records invalid examples and their errors in stats.invalidExamples
  • 'repair' -- includes invalid examples in the output with _meta.repaired: true

Validation error counts are always available in stats.validationErrors regardless of the handling mode.

Deduplication Errors

The deduplicate function throws an Error if the semantic strategy is used without providing an embedder function:

// Throws: "Semantic dedup requires an embedder function"awaitdeduplicate(examples,{strategy: 'semantic'});

Export Errors

The exportAs function throws an Error for unsupported format strings:

// Throws: "Unsupported export format: xml"exportAs(examples,'xml'asExportFormat);

Advanced Usage

Custom System Prompt

Override the default system prompt using the {schema_description} placeholder:

constresult=awaitgenerate(schema,{count: 100,llm: myLlm,systemPrompt: 'You are a medical expert generating training data.\n\n{schema_description}',additionalInstructions: 'All examples must be about cardiology.',});

Cross-Set Deduplication

Remove generated examples that duplicate entries in an existing dataset:

import{deduplicate}from'synthdata-gen';constresult=awaitdeduplicate(newExamples,{strategy: 'exact',existingData: existingDataset,});console.log(`Removed ${result.removed} duplicates of existing data`);

Semantic Deduplication

Provide an embedding function for meaning-level deduplication:

constresult=awaitdeduplicate(examples,{strategy: 'semantic',threshold: 0.92,embedder: async(text)=>{constresponse=awaitopenai.embeddings.create({model: 'text-embedding-3-small',input: text,});returnresponse.data[0].embedding;},});

Field-Specific Deduplication

Deduplicate based on a subset of fields:

constresult=awaitdeduplicate(examples,{strategy: 'near',threshold: 0.85,ngramSize: 2,fields: ['instruction'],// Only compare instruction fields});

Custom Field Mapping for Export

Map your schema fields to the roles expected by each export format:

import{exportOpenAI,exportAlpaca}from'synthdata-gen';constqaData=[{question: 'What is TCP?',answer: 'TCP is a connection-oriented protocol.'},];// Map question -> user, answer -> assistantconstopenai=exportOpenAI(qaData,{fieldMap: {user: 'question',assistant: 'answer'},systemPrompt: 'You are a networking expert.',});// Map question -> instruction, answer -> outputconstalpaca=exportAlpaca(qaData,{fieldMap: {instruction: 'question',output: 'answer'},});

Batch Generation with LLM

Request multiple examples per LLM call to reduce API costs:

constresult=awaitgenerate(schema,{llm: myLlm,count: 1000,batchSize: 10,// 10 examples per LLM callstructuredOutput: true,// Request JSON mode if provider supports it});

Standalone Template Generation

Use the template generator directly without the full pipeline:

import{generateExample,generateExamples}from'synthdata-gen';// Single example, deterministic with seedconstexample=generateExample(schema,42);// Multiple examples, deterministic with base seedconstexamples=generateExamples(schema,100,42);

Building Prompts for External Use

Generate the prompt that would be sent to an LLM, without calling one:

import{buildSchemaPrompt,buildSystemPrompt}from'synthdata-gen';constschemaPrompt=buildSchemaPrompt(schema);// "Generate a JSON object with the following structure:\n{ ... }"constsystemPrompt=buildSystemPrompt(schema,undefined,'Focus on edge cases.');// Full system prompt with schema description and additional instructions

Parsing LLM Responses

Extract JSON from messy LLM output:

import{parseJsonResponse}from'synthdata-gen';constobjects=parseJsonResponse('Here is the result:\n```json\n{"key": "value"}\n```\nDone!');// [{ key: "value" }]constarray=parseJsonResponse('[{"a": 1}, {"b": 2}]');// [{ a: 1 }, { b: 2 }]

TypeScript

All types are exported from the package entry point:

importtype{// LLM interfaceMessage,LlmCallOptions,LlmResponse,LlmFunction,// SchemaFieldType,SchemaField,ExampleSchema,// GenerationGeneratedExample,DiversityConfig,HeuristicsConfig,CustomValidator,ValidationConfig,RetryConfig,CostConfig,DedupOptions,GenerateOptions,ExportFormat,ExportOptions,// ResultsGenerateResult,GenerationStats,ValidationResult,ValidationError,DedupResult,}from'synthdata-gen';

The GeneratedExample<T> type is generic. By default it is Record<string, unknown> & { _meta?: ... }. You can narrow it with your own type:

interfaceQAPair{question: string;answer: string;category: string;}constresult=awaitgenerate(schema,{count: 10});constdata=result.dataasGeneratedExample<QAPair>[];

License

MIT

About

Generate and validate synthetic training data using any LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

synthdata-gen

Generate, validate, deduplicate, and export synthetic training data for LLM fine-tuning and evaluation.

npm versionnpm downloadslicensenodetypes


Description

synthdata-gen is a complete pipeline for producing synthetic training data. Define a schema describing the shape of each example, optionally plug in any LLM, and the library handles generation, output parsing, schema validation, quality heuristics, deduplication, and export to training-ready formats (OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, plain JSONL).

The library works in three modes:

  • Template-based generation -- no LLM required. A built-in deterministic generator produces examples matching your schema using seeded pseudo-random values. Useful for testing pipelines, prototyping schemas, and generating placeholder data.
  • LLM-based generation -- provide any async function that calls an LLM. The pipeline builds prompts from your schema, parses structured output from LLM responses (including JSON embedded in markdown fences), retries on failure, and tracks token usage and cost.
  • Custom generation -- provide your own generateFn callback for full control over how examples are produced, while still benefiting from the validation, dedup, and export stages.

Each pipeline stage (generation, validation, deduplication, export) is independently usable as a standalone function.


Installation

npm install synthdata-gen

Requires Node.js >= 18.


Quick Start

Template-based generation (no LLM required)

import{generate}from'synthdata-gen';importtype{ExampleSchema}from'synthdata-gen';constschema: ExampleSchema={fields: {instruction: {type: 'string',min: 10,max: 200,description: 'A clear instruction'},output: {type: 'string',min: 20,max: 1000,description: 'The expected response'},category: {type: 'enum',enum: ['coding','writing','reasoning']},},};constresult=awaitgenerate(schema,{count: 100});console.log(result.data);// GeneratedExample[]console.log(result.stats);// GenerationStats

LLM-based generation

import{generate}from'synthdata-gen';importtype{LlmFunction}from'synthdata-gen';constmyLlm: LlmFunction=async(messages,options)=>{constresponse=awaitcallMyProvider(messages,options);return{content: response.text,usage: {promptTokens: response.usage.prompt_tokens,completionTokens: response.usage.completion_tokens,totalTokens: response.usage.total_tokens,},};};constresult=awaitgenerate(schema,{llm: myLlm,count: 500,batchSize: 5,format: 'openai',seeds: [{instruction: 'Explain recursion',output: 'Recursion is when a function calls itself...',category: 'coding'},],costTracking: {promptTokenCost: 0.000003,completionTokenCost: 0.000015,currency: 'USD',},});console.log(result.exported);// OpenAI fine-tuning JSONL stringconsole.log(result.stats.cost);// { promptTokens, completionTokens, totalCost, currency }console.log(result.stats.durationMs);// wall-clock time

Features

  • Schema-driven generation -- define field types, constraints, descriptions, and required fields; the library compiles schemas into LLM prompts and validates output against them.
  • Three generation modes -- template-based (no LLM), LLM-based (any provider), or custom callback.
  • Robust LLM output parsing -- extracts JSON from bare responses, markdown code fences, and mixed text.
  • Schema validation -- type checking, string length constraints, numeric ranges, regex patterns, enum membership, array bounds, and nested object validation.
  • Quality heuristics -- detect empty fields, placeholder text (lorem ipsum, TODO, N/A), duplicate field values, and enforce minimum word counts.
  • Custom validators -- plug in arbitrary validation functions alongside built-in checks.
  • Three deduplication strategies -- exact match (normalized hash), near-duplicate (Jaccard similarity on n-grams), and semantic (cosine similarity on embeddings via a pluggable embedder).
  • Cross-set deduplication -- remove generated examples that overlap with an existing dataset.
  • Five export formats -- OpenAI fine-tuning JSONL, Alpaca, ShareGPT, CSV, and plain JSONL, with configurable field mappings.
  • Diversity controls -- temperature variation (linear, cycle, random), topic rotation, seed example rotation, negative example generation, and constraint variation.
  • Cost tracking -- track prompt and completion tokens, compute estimated cost per run.
  • Deterministic generation -- seeded PRNG for reproducible template-based output.
  • Full TypeScript support -- all types exported, strict mode compatible.

API Reference

generate(schema, options)

Main pipeline function. Generates examples, validates, deduplicates, and optionally exports.

functiongenerate(schema: ExampleSchema,options: GenerateOptions): Promise<GenerateResult>

Parameters:

ParameterTypeDescription
schemaExampleSchemaSchema defining the shape of each example
optionsGenerateOptionsPipeline configuration (see below)

GenerateOptions:

OptionTypeDefaultDescription
countnumberrequiredNumber of examples to generate
llmLlmFunctionundefinedAsync function that calls an LLM
generateFn(schema, batchIndex) => Record[]undefinedCustom generation callback
batchSizenumber1Examples per LLM call
systemPromptstringundefinedCustom system prompt (use {schema_description} placeholder)
additionalInstructionsstringundefinedExtra instructions appended to the system prompt
seedsRecord<string, unknown>[]undefinedFew-shot seed examples
diversityDiversityConfigundefinedDiversity strategy configuration
validationValidationConfigundefinedValidation and heuristics configuration
retryRetryConfig{ maxRetries: 3 }Retry configuration for LLM failures
dedupDedupOptions{ strategy: 'exact' }Deduplication configuration
invalidHandling'discard' | 'log' | 'repair''discard'How to handle invalid examples
structuredOutputbooleanundefinedRequest JSON mode from the LLM provider
costTrackingCostConfigundefinedToken cost tracking configuration
formatExportFormatundefinedExport format for the exported field in the result

Returns GenerateResult:

FieldTypeDescription
dataGeneratedExample[]Final validated, deduplicated examples
statsGenerationStatsPipeline statistics
exportedstring | undefinedFormatted output string (if format was specified)

validate(examples, schema, config?)

Validate an array of examples against a schema. Returns a ValidationResult for each example.

functionvalidate(examples: Record<string,unknown>[],schema: ExampleSchema,config?: ValidationConfig,): ValidationResult[]

Returns an array of:

interfaceValidationResult{valid: boolean;index: number;errors: ValidationError[];}interfaceValidationError{path: string[];message: string;code: string;}

Validation error codes:required, invalid_type, too_small, too_big, invalid_string, invalid_enum_value, heuristic_non_empty, heuristic_placeholder, heuristic_duplicate_fields, heuristic_min_words, global_min_length, global_max_length, custom_<name>.


validateExample(example, schema, config?)

Validate a single example. Returns an array of ValidationError objects (empty array means valid).

functionvalidateExample(example: Record<string,unknown>,schema: ExampleSchema,config?: ValidationConfig,): ValidationError[]

deduplicate(examples, options?)

Deduplicate an array of examples. Supports exact, near-duplicate, and semantic strategies.

functiondeduplicate(examples: Record<string,unknown>[],options?: Partial<DedupOptions>,): Promise<DedupResult>

DedupOptions:

OptionTypeDefaultDescription
strategy'exact' | 'near' | 'semantic' | 'none''exact'Deduplication strategy
thresholdnumber0.85 (near) / 0.92 (semantic)Similarity threshold for near/semantic dedup
ngramSizenumber2N-gram size for near-duplicate detection
fieldsstring[]all fieldsSubset of fields to compare
embedder(text: string) => Promise<number[]>undefinedEmbedding function (required for semantic strategy)
existingDataRecord<string, unknown>[]undefinedExisting dataset for cross-set deduplication

Returns DedupResult:

interfaceDedupResult{data: Record<string,unknown>[];// Deduplicated examplesremoved: number;// Number of duplicates removedpairs: Array<[number,number,number]>;// [indexA, indexB, similarity]}

exportAs(examples, format, options?)

Export examples to a training-ready format string.

functionexportAs(examples: Record<string,unknown>[],format: ExportFormat,options?: ExportOptions,): string

ExportFormat:'openai' | 'alpaca' | 'sharegpt' | 'csv' | 'jsonl'

ExportOptions:

OptionTypeDefaultDescription
fieldMapRecord<string, string>undefinedMap format roles to your field names
systemPromptstringundefinedStatic system prompt (OpenAI/ShareGPT formats)
delimiterstring','CSV column delimiter
quotestring'"'CSV quote character
headerbooleantrueInclude CSV header row
fieldsstring[]all fieldsSubset of fields to include

Individual Exporters

Each export format is available as a standalone function:

import{exportOpenAI,exportAlpaca,exportShareGPT,exportCSV,exportJSONL}from'synthdata-gen';
FunctionOutput format
exportOpenAI(examples, options?){"messages": [{"role": "system", ...}, {"role": "user", ...}, {"role": "assistant", ...}]} per line
exportAlpaca(examples, options?){"instruction": "...", "input": "...", "output": "..."} per line
exportShareGPT(examples, options?){"conversations": [{"from": "human", "value": "..."}, {"from": "gpt", "value": "..."}]} per line
exportCSV(examples, options?)Comma-separated values with header row
exportJSONL(examples, options?)One JSON object per line

All exporters automatically exclude _meta fields from output.


Generator Utilities

Low-level functions for template-based generation and prompt construction:

import{generateExample,generateExamples,buildSchemaPrompt,buildSystemPrompt,parseJsonResponse,}from'synthdata-gen';
FunctionDescription
generateExample(schema, seed?)Generate a single example from a schema using the built-in template generator. Deterministic when a seed is provided.
generateExamples(schema, count, baseSeed?)Generate multiple examples. Each example uses baseSeed + index for its seed.
buildSchemaPrompt(schema)Compile a schema into a natural-language prompt describing the expected JSON structure.
buildSystemPrompt(schema, customPrompt?, additionalInstructions?)Build the full system prompt for LLM-based generation. Supports a custom prompt template with {schema_description} placeholder.
parseJsonResponse(text)Extract JSON objects/arrays from an LLM response. Handles bare JSON, markdown code fences, and JSON embedded in explanatory text.

Configuration

Schema Definition

A schema defines the structure of each generated example using ExampleSchema:

constschema: ExampleSchema={fields: {question: {type: 'string',min: 10,max: 500,description: 'A natural language question'},answer: {type: 'string',min: 20,max: 2000,pattern: '^[A-Z]'},category: {type: 'enum',enum: ['science','history','technology']},difficulty: {type: 'integer',min: 1,max: 5},score: {type: 'number',min: 0,max: 100},active: {type: 'boolean'},tags: {type: 'array',items: {type: 'string'},min: 1,max: 5},metadata: {type: 'object',properties: {source: {type: 'string'},verified: {type: 'boolean'},},requiredFields: ['source'],},},required: ['question','answer','category'],};

Supported field types:

TypeSchemaField properties
stringmin (min length), max (max length), pattern (regex), description
numbermin, max, description
integermin, max, description
booleandescription
enumenum (valid values array), description
arrayitems (element schema), min (min items), max (max items), description
objectproperties (nested fields), requiredFields (required property names), description

All fields support required (default: true) and default (default value when field is omitted).


Validation Configuration

constconfig: ValidationConfig={// Global string field length constraintsminFieldLength: 10,maxFieldLength: 5000,// Quality heuristicsheuristics: {nonEmpty: true,// Reject empty/whitespace-only required string fieldsnoPlaceholder: true,// Reject placeholder text (lorem ipsum, TODO, TBD, N/A, etc.)noDuplicateFields: {// Reject examples where specified field pairs are identicalpairs: [['question','answer']],},minWordCount: {// Enforce minimum word count on specified fieldsfields: ['answer'],min: 5,},},// Custom validatorscustom: [{name: 'no-question-in-output',validate: (example)=>({valid: !String(example.answer).endsWith('?'),message: 'Answer should not end with a question mark',}),},],};

The noDuplicateFields and minWordCount heuristics also accept true to use automatic inference: noDuplicateFields: true pairs common field name patterns (question/answer, instruction/output, input/output, prompt/response, query/response), and minWordCount: true applies a default minimum of 3 words to all string fields.


Diversity Configuration

constdiversity: DiversityConfig={temperature: {min: 0.3,max: 1.2,strategy: 'cycle',// 'linear' | 'cycle' | 'random'},topics: ['algorithms','databases','networking','security'],negativeExampleRatio: 0.1,negativeInstructions: 'Generate an example with a subtle factual error.',constraintVariation: [{instruction: 'Write in a formal academic tone.'},{instruction: 'Write in a casual conversational tone.'},],};

Retry Configuration

constretry: RetryConfig={maxRetries: 3,// Maximum retry attempts per batchincludeFeedback: true,// Include validation error feedback in retry promptbackoff: 'exponential',// 'none' | 'linear' | 'exponential'backoffMs: 1000,// Base backoff delay in milliseconds};

Cost Tracking

constcostTracking: CostConfig={promptTokenCost: 0.000003,// Cost per prompt tokencompletionTokenCost: 0.000015,// Cost per completion tokencurrency: 'USD',};

The GenerationStats.cost field in the result contains promptTokens, completionTokens, totalCost, and currency.


Error Handling

Validation Errors

The validate and validateExample functions return structured error objects rather than throwing. Each ValidationError includes:

  • path -- array of field names locating the error (e.g., ['address', 'zip'] for nested fields, ['tags', '0'] for array elements)
  • message -- human-readable description of the failure
  • code -- machine-readable error code for programmatic handling
import{validateExample}from'synthdata-gen';consterrors=validateExample({instruction: 'Hi',output: 123,category: 'invalid'},schema,);for(consterroferrors){console.log(`[${err.code}] ${err.path.join('.')}: ${err.message}`);}// [too_small] instruction: String must contain at least 10 character(s), received 2// [invalid_type] output: Expected string, received number// [invalid_enum_value] category: Invalid enum value. Expected one of ["coding", ...], received "invalid"

Pipeline Error Handling

The generate function handles LLM failures internally using the retry configuration. Invalid examples are handled according to the invalidHandling option:

  • 'discard' (default) -- silently drops invalid examples
  • 'log' -- discards but records invalid examples and their errors in stats.invalidExamples
  • 'repair' -- includes invalid examples in the output with _meta.repaired: true

Validation error counts are always available in stats.validationErrors regardless of the handling mode.

Deduplication Errors

The deduplicate function throws an Error if the semantic strategy is used without providing an embedder function:

// Throws: "Semantic dedup requires an embedder function"awaitdeduplicate(examples,{strategy: 'semantic'});

Export Errors

The exportAs function throws an Error for unsupported format strings:

// Throws: "Unsupported export format: xml"exportAs(examples,'xml'asExportFormat);

Advanced Usage

Custom System Prompt

Override the default system prompt using the {schema_description} placeholder:

constresult=awaitgenerate(schema,{count: 100,llm: myLlm,systemPrompt: 'You are a medical expert generating training data.\n\n{schema_description}',additionalInstructions: 'All examples must be about cardiology.',});

Cross-Set Deduplication

Remove generated examples that duplicate entries in an existing dataset:

import{deduplicate}from'synthdata-gen';constresult=awaitdeduplicate(newExamples,{strategy: 'exact',existingData: existingDataset,});console.log(`Removed ${result.removed} duplicates of existing data`);

Semantic Deduplication

Provide an embedding function for meaning-level deduplication:

constresult=awaitdeduplicate(examples,{strategy: 'semantic',threshold: 0.92,embedder: async(text)=>{constresponse=awaitopenai.embeddings.create({model: 'text-embedding-3-small',input: text,});returnresponse.data[0].embedding;},});

Field-Specific Deduplication

Deduplicate based on a subset of fields:

constresult=awaitdeduplicate(examples,{strategy: 'near',threshold: 0.85,ngramSize: 2,fields: ['instruction'],// Only compare instruction fields});

Custom Field Mapping for Export

Map your schema fields to the roles expected by each export format:

import{exportOpenAI,exportAlpaca}from'synthdata-gen';constqaData=[{question: 'What is TCP?',answer: 'TCP is a connection-oriented protocol.'},];// Map question -> user, answer -> assistantconstopenai=exportOpenAI(qaData,{fieldMap: {user: 'question',assistant: 'answer'},systemPrompt: 'You are a networking expert.',});// Map question -> instruction, answer -> outputconstalpaca=exportAlpaca(qaData,{fieldMap: {instruction: 'question',output: 'answer'},});

Batch Generation with LLM

Request multiple examples per LLM call to reduce API costs:

constresult=awaitgenerate(schema,{llm: myLlm,count: 1000,batchSize: 10,// 10 examples per LLM callstructuredOutput: true,// Request JSON mode if provider supports it});

Standalone Template Generation

Use the template generator directly without the full pipeline:

import{generateExample,generateExamples}from'synthdata-gen';// Single example, deterministic with seedconstexample=generateExample(schema,42);// Multiple examples, deterministic with base seedconstexamples=generateExamples(schema,100,42);

Building Prompts for External Use

Generate the prompt that would be sent to an LLM, without calling one:

import{buildSchemaPrompt,buildSystemPrompt}from'synthdata-gen';constschemaPrompt=buildSchemaPrompt(schema);// "Generate a JSON object with the following structure:\n{ ... }"constsystemPrompt=buildSystemPrompt(schema,undefined,'Focus on edge cases.');// Full system prompt with schema description and additional instructions

Parsing LLM Responses

Extract JSON from messy LLM output:

import{parseJsonResponse}from'synthdata-gen';constobjects=parseJsonResponse('Here is the result:\n```json\n{"key": "value"}\n```\nDone!');// [{ key: "value" }]constarray=parseJsonResponse('[{"a": 1}, {"b": 2}]');// [{ a: 1 }, { b: 2 }]

TypeScript

All types are exported from the package entry point:

importtype{// LLM interfaceMessage,LlmCallOptions,LlmResponse,LlmFunction,// SchemaFieldType,SchemaField,ExampleSchema,// GenerationGeneratedExample,DiversityConfig,HeuristicsConfig,CustomValidator,ValidationConfig,RetryConfig,CostConfig,DedupOptions,GenerateOptions,ExportFormat,ExportOptions,// ResultsGenerateResult,GenerationStats,ValidationResult,ValidationError,DedupResult,}from'synthdata-gen';

The GeneratedExample<T> type is generic. By default it is Record<string, unknown> & { _meta?: ... }. You can narrow it with your own type:

interfaceQAPair{question: string;answer: string;category: string;}constresult=awaitgenerate(schema,{count: 10});constdata=result.dataasGeneratedExample<QAPair>[];

License

MIT

About

Generate and validate synthetic training data using any LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages