Documentation Index Fetch the complete documentation index at: https://docs.doclo.ai/llms.txt
Use this file to discover all available pages before exploring further.
The Flow Registry allows you to register flows by string ID for reuse across your application. This is useful for:
Centralizing flow definitions
Referencing flows from serialized configurations
Building flow libraries
Basic Usage
Register a Flow
import { registerFlow , createFlow , parse , extract } from '@doclo/flows' ;
registerFlow ( 'invoice-processing' , ( providers ) =>
createFlow ()
. step ( 'parse' , parse ({ provider: providers ?. ocr }))
. step ( 'extract' , extract ({
provider: providers ?. vlm ,
schema: invoiceSchema
}))
);
Retrieve and Run
import { getFlow } from '@doclo/flows' ;
const flowBuilder = getFlow ( 'invoice-processing' );
if ( flowBuilder ) {
const flow = flowBuilder ({
ocr: ocrProvider ,
vlm: vlmProvider
});
const result = await flow . build (). run ({ base64: documentData });
}
Registry API
registerFlow
Register a flow builder function:
registerFlow < TInput , TOutput >(
id : string ,
builder : ( providers ?: ProviderRegistry ) => Flow < TInput , TOutput >
): void
If a flow with the same ID already exists, it will be overwritten with a warning.
getFlow
Retrieve a flow builder by ID:
getFlow < TInput , TOutput >( id : string ): FlowBuilder < TInput , TOutput > | undefined
Returns undefined if the flow is not registered.
hasFlow
Check if a flow exists:
hasFlow ( id : string ): boolean
unregisterFlow
Remove a flow from the registry:
unregisterFlow ( id : string ): boolean
Returns true if the flow was removed, false if it didn’t exist.
listFlows
Get all registered flow IDs:
getFlowCount
Get the number of registered flows:
clearRegistry
Remove all registered flows:
Provider Registry
Flow builders receive an optional ProviderRegistry for dependency injection:
interface ProviderRegistry {
ocr ?: OCRProvider ;
llm ?: LLMProvider ;
vlm ?: VLMProvider ;
[ key : string ] : unknown ;
}
This allows flows to be configured with different providers at runtime:
// Register with provider placeholders
registerFlow ( 'document-extractor' , ( providers ) =>
createFlow ()
. step ( 'parse' , parse ({ provider: providers ?. ocr }))
. step ( 'extract' , extract ({ provider: providers ?. vlm , schema }))
);
// Use with production providers
const prodFlow = getFlow ( 'document-extractor' ) ! ({
ocr: productionOCR ,
vlm: productionVLM
});
// Use with test providers
const testFlow = getFlow ( 'document-extractor' ) ! ({
ocr: mockOCR ,
vlm: mockVLM
});
Global Registry
The SDK provides a global registry instance:
import { FLOW_REGISTRY } from '@doclo/flows' ;
// Direct access to the Map
FLOW_REGISTRY . set ( 'my-flow' , flowBuilder );
FLOW_REGISTRY . get ( 'my-flow' );
FLOW_REGISTRY . has ( 'my-flow' );
FLOW_REGISTRY . delete ( 'my-flow' );
FLOW_REGISTRY . clear ();
The helper functions (registerFlow, getFlow, etc.) operate on this global registry.
Use Cases
Flow Library
Create a library of reusable flows:
// flows/index.ts
import { registerFlow , createFlow , parse , extract , split , combine } from '@doclo/flows' ;
// Simple extraction
registerFlow ( 'simple-extraction' , ( providers ) =>
createFlow ()
. step ( 'extract' , extract ({
provider: providers ?. vlm ,
schema: providers ?. schema
}))
);
// OCR + extraction
registerFlow ( 'ocr-extraction' , ( providers ) =>
createFlow ()
. step ( 'parse' , parse ({ provider: providers ?. ocr }))
. step ( 'extract' , extract ({
provider: providers ?. llm ,
schema: providers ?. schema
}))
);
// Multi-document processing
registerFlow ( 'multi-document' , ( providers ) =>
createFlow ()
. step ( 'split' , split ({
provider: providers ?. vlm ,
schemas: providers ?. schemas
}))
. forEach ( 'process' , ( doc ) =>
createFlow ()
. step ( 'extract' , extract ({
provider: providers ?. vlm ,
schema: doc . schema
}))
)
. step ( 'combine' , combine ())
);
Configuration-Driven Flows
Reference flows from configuration:
// config.json
{
"documentProcessing" : {
"flowId" : "invoice-processing" ,
"providers" : {
"ocr" : "surya" ,
"vlm" : "gemini-flash"
}
}
}
// runtime.ts
import { getFlow } from '@doclo/flows' ;
import config from './config.json' ;
const flowBuilder = getFlow ( config . documentProcessing . flowId );
if ( ! flowBuilder ) {
throw new Error ( `Unknown flow: ${ config . documentProcessing . flowId } ` );
}
const providers = resolveProviders ( config . documentProcessing . providers );
const flow = flowBuilder ( providers ). build ();
Testing
Use the registry to swap providers for testing:
import { registerFlow , getFlow , clearRegistry } from '@doclo/flows' ;
describe ( 'Invoice Flow' , () => {
beforeEach (() => {
clearRegistry ();
registerFlow ( 'invoice' , ( providers ) =>
createFlow ()
. step ( 'extract' , extract ({ provider: providers ?. vlm , schema }))
);
});
it ( 'extracts invoice data' , async () => {
const mockVLM = createMockVLMProvider ({
response: { invoiceNumber: 'INV-001' , total: 100 }
});
const flow = getFlow ( 'invoice' ) ! ({ vlm: mockVLM }). build ();
const result = await flow . run ({ base64: testDocument });
expect ( result . output . invoiceNumber ). toBe ( 'INV-001' );
});
});
Multi-Tenant Configuration
Different flows per tenant:
// Register tenant-specific flows
registerFlow ( 'tenant-a-invoice' , ( providers ) =>
createFlow ()
. step ( 'extract' , extract ({
provider: providers ?. vlm ,
schema: tenantASchema ,
additionalInstructions: 'Use Tenant A field mapping'
}))
);
registerFlow ( 'tenant-b-invoice' , ( providers ) =>
createFlow ()
. step ( 'extract' , extract ({
provider: providers ?. vlm ,
schema: tenantBSchema ,
additionalInstructions: 'Use Tenant B field mapping'
}))
);
// Runtime selection
function processDocument ( tenantId : string , document : FlowInput ) {
const flowId = `tenant- ${ tenantId } -invoice` ;
const flowBuilder = getFlow ( flowId );
if ( ! flowBuilder ) {
throw new Error ( `No flow configured for tenant: ${ tenantId } ` );
}
return flowBuilder ({ vlm: vlmProvider }). build (). run ( document );
}
Best Practices
Use Descriptive IDs
// Good
registerFlow ( 'invoice-extraction-v2' , ... );
registerFlow ( 'receipt-with-consensus' , ... );
// Avoid
registerFlow ( 'flow1' , ... );
registerFlow ( 'extract' , ... );
Version Your Flows
registerFlow ( 'invoice-v1' , ... );
registerFlow ( 'invoice-v2' , ... );
// Or use a versioning scheme
registerFlow ( 'invoice@1.0.0' , ... );
registerFlow ( 'invoice@2.0.0' , ... );
Handle Missing Flows
const flowBuilder = getFlow ( flowId );
if ( ! flowBuilder ) {
// Log available flows for debugging
console . error ( `Flow " ${ flowId } " not found. Available: ${ listFlows (). join ( ', ' ) } ` );
throw new Error ( `Unknown flow: ${ flowId } ` );
}
Initialize Registry on Startup
// flows/registry.ts
import { registerFlow } from '@doclo/flows' ;
import { invoiceFlow } from './invoice' ;
import { receiptFlow } from './receipt' ;
import { contractFlow } from './contract' ;
export function initializeFlowRegistry () {
registerFlow ( 'invoice' , invoiceFlow );
registerFlow ( 'receipt' , receiptFlow );
registerFlow ( 'contract' , contractFlow );
}
// app.ts
import { initializeFlowRegistry } from './flows/registry' ;
initializeFlowRegistry ();
// Now flows are available throughout the app
Next Steps
Creating Flows Build custom flows
Pre-built Flows Ready-to-use flow templates