Configuration and Customization
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0
Configuration and Customization
Section titled “Configuration and Customization”The GenAIIDP solution provides multiple configuration approaches to customize document processing behavior to suit your specific needs.
📝 Note: Starting with version 0.3.21, document class definitions use JSON Schema format instead of the legacy custom format. See json-schema-migration.md for migration details and format comparison. Legacy configurations are automatically migrated on first use.
Pattern Configuration via Web UI
Section titled “Pattern Configuration via Web UI”The web interface allows real-time configuration updates without stack redeployment:
- Document Classes: Define and modify document categories and their descriptions (using JSON Schema format). Choose from 35+ pre-built standard classes (Invoice, Receipt, W-2, Bank Statement, etc.) or create custom classes from scratch.
- Extraction Attributes: Configure fields to extract for each document class (defined as JSON Schema properties)
- Few Shot Examples: Upload and configure example documents to improve accuracy (supported in Pattern 2)
- Model Selection: Choose between available Bedrock models for classification and extraction
💡 Cost Attribution Tip: You can replace standard model IDs with Bedrock Application Inference Profile ARNs to enable cost-allocation tagging (e.g., for MAP migration tracking). This is a configuration-only change — no code modifications required. See Cost Attribution with Application Inference Profiles for step-by-step instructions. 🤖 OpenAI GPT-6 Astra:
us.openai.gpt-6-astra(US regions) andglobal.openai.gpt-6-astra(every region, and cheaper) are selectable for OCR, classification, extraction — including agentic extraction — assessment, summarization, evaluation, and chat. Astra runs on the standard Converse API (not the bedrock-mantle path the GPT-5.x models use), has a 1.05M context window with automatic prompt caching, and is tuned viareasoning_effort(none/low/medium/high/xhigh/max) instead of temperature/top_p, which it rejects. It is not supported for Discovery or Policy Discovery. Note input above 272K tokens bills at roughly double the rate recorded inpricing.yaml. See OpenAI Models for the full support matrix and caveats. 🤖 OpenAI GPT-5.x:openai.gpt-5.4,openai.gpt-5.5, and the GPT-5.6 family (openai.gpt-5.6-sol/-terra/-luna) are selectable for OCR, classification, extraction, assessment, summarization, evaluation, and chat (US regions only), and are tuned via areasoning_effortfield instead of temperature/top_p. They are not supported for agentic extraction or Discovery. See OpenAI GPT-5.x Models for the full support matrix and caveats. 🤖 xAI Grok:us.xai.grok-4.6(US regions) andglobal.xai.grok-4.6(EU/APAC too, and cheaper) are selectable for OCR, classification, extraction — including agentic extraction — assessment, summarization, evaluation, chat, and the rule-validation/agent paths. Grok runs on the standard Converse API, is tuned viareasoning_effort(none/low/medium/high/xhigh) instead of temperature/top_p, and is not supported for Discovery or Policy Discovery. Flex/Priority tiers and prompt caching do not work despite being advertised. See xAI Grok Models for the full support matrix and caveats. - Prompt Engineering: Customize system and task prompts for optimal results
- OCR Features: Configure Textract features (TABLES, FORMS, SIGNATURES, LAYOUT) for enhanced data capture
- Evaluation Methods: Set evaluation methods and thresholds for each attribute
- Summarization: Configure model, prompts, parameters, and enable/disable document summarization via the
enabledproperty
Configuration Versions
Section titled “Configuration Versions”The solution supports multiple named configuration versions, enabling you to maintain independent configuration snapshots for A/B testing, environment separation, and iterative prompt tuning — all without redeploying the stack. Each version stores a complete, self-contained configuration. The active version determines which configuration is used for new document processing.
Key capabilities:
- Create, edit, and delete configuration versions with unique names and descriptions
- Activate any version to make it the default for new processing
- Compare versions side-by-side to see differences (exportable as CSV/JSON)
- Track which version was used for each processed document and test run
- Select a specific version when uploading documents, running tests, or using the CLI
Managed Configuration Versions
Section titled “Managed Configuration Versions”The stack automatically deploys managed configuration versions for each pre-deployed test set (fake-w2, docsplit, ocr-benchmark, realkie-fcc-verified). These are marked with managed: true and have the following behavior:
- Overwritten on stack updates — always reflect the latest defaults shipped with the solution
- Save disabled — the Save button is disabled and an info banner explains the config is stack-managed
- Delete disabled — managed versions cannot be deleted in the UI or via the API
- Editable copies — use Create profile in the Configuration Profiles table to create a custom, editable copy
- Not importable — managed configs are stored separately (
config_library/managed_config/) and do not appear in the configuration import browser - Test Studio integration — when a test set is selected, the matching managed config version is auto-selected
For comprehensive documentation, see configuration-profiles.md.
Configuration Management Features
Section titled “Configuration Management Features”- Save Changes: Save your current configuration changes. The button is enabled only when you have unsaved changes (comparing your edits against the last saved configuration); when it’s disabled on a stack-managed version, hovering it explains why. After a successful save, a confirmation banner and a brief success toast (top-right notification area) are shown. Less-frequent actions (Export, Save as default, Restore default (All), Save current edits as new profile, and BDA sync) are grouped under an Actions menu next to Save changes. To create a new profile from an existing one’s saved state, use Create profile in the Configuration Profiles table instead.
- Unsaved Changes Indicator: Individual fields with unsaved edits display an orange dot next to the field label, and an info banner with a “Discard changes” button appears when the configuration form has unsaved edits (shown on all versions, including the stack-managed
default). - Browser Navigation Guard: The browser warns before leaving the page when unsaved configuration changes exist (both on browser close/refresh and SPA navigation).
- Save as Default: Save your current version’s configuration as the new default baseline. This replaces the existing default configuration. Warning: Default configurations may be overwritten during solution upgrades - export your configuration first for backup.
- Restore Default (All): Reset the current version’s configuration back to the default values, replacing all customizations.
- Refresh: Reload the configuration from the server. Use this to sync your view with the latest saved configuration, discard unsaved local changes, or verify your configuration after external updates.
- Export Configuration: Download your current configuration to local files in JSON or YAML format with customizable filenames. Use this to backup configurations before upgrades or share configurations between environments.
- Import Configuration: Upload configuration files from your local machine OR import from the Configuration Library:
- From Local File: Upload configuration files from your computer in JSON or YAML format with automatic format detection and validation
- From Configuration Library: Browse and import pre-configured document processing workflows from the solution’s built-in configuration library
- Pattern-Filtered: Only shows configurations compatible with your currently deployed pattern (Pattern 1, 2, or 3)
- Dual Format Support: Automatically detects and imports both
config.yamlandconfig.jsonformats - README Preview: View markdown-formatted documentation before importing to understand configuration purpose and features
- Format Indicators: Visual badges show file format (YAML/JSON) and README availability
- Library Contents: Includes sample configurations like lending-package-sample, bank-statement-sample, rvl-cdip, criteria-validation, and more
- Important: Importing a configuration replaces your existing custom configuration entirely. Any prior customizations not included in the imported file will be reset to defaults. Export your current configuration first if you want to preserve it.
Configuration changes are validated and applied immediately, with rollback capability if issues arise. See web-ui.md for details on using the administration interface.
Configuration Management via CLI
Section titled “Configuration Management via CLI”The IDP CLI provides command-line tools for configuration management:
idp-cli config-create: Generate configuration templates from system defaultsidp-cli config-validate: Validate configuration files against schemasidp-cli config-download: Download configuration from deployed stacksidp-cli config-upload: Upload configuration to deployed stacks
See idp-cli.md for complete command documentation.
Custom Configuration Path
Section titled “Custom Configuration Path”The solution now supports specifying a custom configuration file location via the CustomConfigPath CloudFormation parameter. This allows you to use your own configuration files stored in S3 instead of the default configuration library.
When deploying the stack, you can specify a custom configuration file:
CustomConfigPath: "s3://my-bucket/custom-config/config.yaml"Key Features:
- Override Default Configuration: When specified, your custom configuration completely replaces the default pattern configuration
- S3 URI Format: Accepts standard S3 URI format (e.g.,
s3://my-bucket/custom-config/config.yaml) - Least-Privilege Security: IAM permissions are conditionally granted only to the specific S3 bucket and object you specify
- All Patterns Supported: Works with Pattern 1 (BDA), Pattern 2 (Textract + Bedrock), and Pattern 3 (Textract + UDOP + Bedrock)
Security Benefits:
- Eliminates wildcard S3 permissions (
arn:aws:s3:::*/*) - Conditional IAM access only when CustomConfigPath is specified
- Proper S3 URI to ARN conversion for least-privilege compliance
- Passes security scans with minimal required permissions
Configuration File Requirements:
- Must be valid YAML format
- Only needs to include
notes,classes, and any settings that differ from system defaults (see “System Defaults and Configuration Inheritance” below) - Follow the same structure as the configuration files in the
config_librarydirectory
Leave the CustomConfigPath parameter empty (default) to use the standard configuration library included with the solution.
System Defaults and Configuration Inheritance
Section titled “System Defaults and Configuration Inheritance”The GenAI IDP Accelerator uses a system defaults architecture where configurations inherit from pattern-specific default files. This means user configurations only need to specify differences from the defaults, making them simpler and more maintainable.
How It Works
Section titled “How It Works”-
System defaults are loaded first from
lib/idp_common_pkg/idp_common/config/system_defaults/:pattern-1.yaml- BDA mode defaultspattern-2.yaml- Pipeline mode defaults
Which of the two is used comes from the caller’s explicit pattern argument. Where that is omitted, the tools that auto-detect it (the SDK’s config download and upload, and the
update_configurationLambda) selectpattern-1.yamlonly for a configuration whoseclassification.classificationMethodisbda, andpattern-2.yamlotherwise — they do not read the top-leveluse_bdaflag, so a BDA deployment configured only throughuse_bda: trueresolves againstpattern-2.yaml. That is benign but not free: your ownuse_bda: truestill wins the merge, so the workflow routes to BDA as you asked, and the cost is a configuration carrying the OCR, classification, chat, rule-validation and LLM extraction defaults BDA mode does not use.use_bdais what routes the workflow at run time; the pattern file only decides which defaults are merged underneath it. -
User configurations are merged on top, overriding only the specified values
-
Result: A complete configuration with user customizations applied to system defaults
Minimal Configuration Example
Section titled “Minimal Configuration Example”A user configuration only needs:
notes: "My document processing configuration"
classes: - $schema: https://json-schema.org/draft/2020-12/schema $id: Invoice type: object x-aws-idp-document-type: Invoice description: "A billing document" properties: invoice_number: type: string description: "Unique invoice identifier"All other settings (OCR, classification, extraction, assessment, evaluation, summarization, discovery, agents) are inherited from the pattern’s system defaults.
The system defaults are the canonical list of every setting
Section titled “The system defaults are the canonical list of every setting”This page is organized by topic and covers the settings people ask about most — it is
not an exhaustive key reference, and a newly added option may not be described here
yet. The authoritative enumeration is the system_defaults/ directory itself:
| file | covers |
|---|---|
base.yaml | top-level keys shared by every pattern |
base-ocr.yaml | OCR backends and image handling |
base-classification.yaml | classification, sectionSplitting, contextPagesCount |
base-extraction.yaml | extraction, agentic.*, forced_tool.*, multi_instance_detection.* |
base-confidence.yaml | assessment / confidence |
base-evaluation.yaml, base-summarization.yaml, base-discovery.yaml, base-agents.yaml, base-chat.yaml, base-geometry.yaml, base-rule-validation.yaml, base-rule-discovery.yaml, base-classes.yaml, base-notes.yaml | the remaining stages |
pattern-1.yaml / pattern-2.yaml | which of the above compose for BDA vs Pipeline mode |
Every key in those files carries its shipped default and an inline comment explaining
what it does, when to change it, and — where it has been measured — the evidence. Reading
them is the reliable way to find out what is tunable; they are also what the Web UI
shows for Config#default, so what you read there is what the UI presents.
For measured guidance on which settings are worth changing — including the ones added most recently — see the Configuration Guidance paper. Its §7 covers the current release’s options with the A/B numbers behind each recommendation, and states plainly where a setting was measured to buy nothing.
Override Example
Section titled “Override Example”To override specific settings while keeping others at defaults:
notes: "Configuration with custom classification method"
# Override just the classification methodclassification: classificationMethod: textbasedHolisticClassification
# Override confidence to use integrated modeextraction: confidence: mode: integrated
classes: # ... your document classesA key the solution does not recognise is reported, not applied
Section titled “A key the solution does not recognise is reported, not applied”A configuration key whose name no setting matches is ignored on load, at every
level of the file. The configuration is still accepted and the run still completes —
what changes is that the shipped default stays in force, which looks exactly like a
working configuration. A misspelled enabled leaves a guard on; a mis-nested
fail_action leaves the default warn in place, and the conclusion a reader draws
is that escalation does not work.
Every such key is therefore named, with the full path to where it was written:
IDPConfig: Ignoring unknown nested fields (not defined in model, so the shippeddefault stays in force): extraction.validation.enabld (did you meanextraction.validation.enabled?), ocr.dpi (did you mean ocr.image.dpi?)Two places to look for it:
- Before you upload.
idp-cli config-validate --config-file <file>lists them as warnings, with the path. This is the cheap moment — the file is in front of you. Note thatidp-cli config-uploaddoes not print them: it checks only whether the configuration is valid, and an ignored key does not make it invalid.--strictturns a top-level extra into a non-zero exit; a nested one is reported but does not fail, so adding this flag to a pipeline cannot break a configuration that passes today. - On load, in CloudWatch. The same finding appears at
WARNINGfrom the Lambda that loaded the configuration. Search the log group forIgnoringafter changing a configuration — that matches the unknown-key line and the deprecated-key one.
⚠️ A key at the wrong nesting level is the trap worth knowing about, because the
name itself is valid. dpi is a real setting, under ocr.image. Written as
ocr.dpi it is ignored — and so is any bad value you gave it, because the check
that would have rejected it lives with the real key:
ocr: dpi: 300 # ignored: no such setting at this level image: dpi: 300 # this is the one that takes effectThat is why the message offers the path the key belongs at. Since ocr.backend and
ocr.model_id genuinely are one level up, this is an easy mistake to make and a hard
one to see afterwards.
The same works the other way round: write ocr.image.backend, one level too deep,
and the message points you back at ocr.backend.
A “did you mean” appears only when there is one answer. Some setting names are
used in several places — enabled exists at eight different places one level under
extraction — and in that case the key is still reported but no path is suggested,
because a guess would send you to edit something that was already correct.
The warning is not an error. An unrecognised key does not stop a configuration from being accepted or a deployment from loading it, so upgrading cannot break a stored configuration on this account. It also means the only signal is the warning: after changing a setting, confirm the value you set is the value in effect rather than inferring it from the absence of an error.
Two kinds of key are deliberately not reported, because nothing is being dropped:
free-form content whose names are yours to choose — your document classes, a
pipeline hook’s args — and the pipeline-hook blocks, which keep whatever they are
given.
Pricing and Model Limits are separate records, and they report the same way
Section titled “Pricing and Model Limits are separate records, and they report the same way”The Pricing and View / Edit Model Limits panels edit their own records rather than part of the configuration document, so the paragraphs above describe them too but the message names the record instead of the configuration:
ModelConfigLimitsConfig: Ignoring unknown nested fields (not defined in model, sothe shipped default stays in force): model_limits[0].max_input_tokenz (did you meanmodel_limits[0].max_input_tokens?)The list index is part of the path, so the line tells you which row to fix. Two things worth knowing about these two records specifically:
- A stray key at the top of the record is refused outright, with a validation
error rather than a warning, because these records recognise only their own small
set of top-level keys. The warning above is for a key inside a row, which is
where the silent drop was: a row’s own settings were accepted permissively, so a
misspelled
max_input_tokensleft the shipped context window for that model family in force while the save reported success. - A price belongs to a unit, not to the entry. Writing
pricealongsidenameandunitson a pricing entry is the mis-nesting equivalent ofocr.dpi: it is ignored, the shipped rate stays in force, and the cost figures carry on looking plausible. The warning namespricing[<n>].priceand points atpricing[].units[].price.
As with the configuration document, the finding appears at WARNING in the log
group of whichever function loaded or saved the record — so search for Ignoring
after editing either panel.
Benefits
Section titled “Benefits”- Simpler configs - Only specify what makes your use case unique
- Maintainable - System default updates automatically apply to all configs
- Focused - Easy to see what customizations are active
- Version-safe - Defaults evolve with the solution while custom overrides remain stable
Configuration Library
Section titled “Configuration Library”The config_library/ directory contains example configurations demonstrating this inheritance pattern. Each config contains:
notes:- Description of the configurationclasses:- Document class definitions (JSON Schema format)- Overrides - Only settings that differ from system defaults
See the config_library README for available configurations and usage examples.
Retired and legacy Bedrock models
Section titled “Retired and legacy Bedrock models”Bedrock retires model versions over time. A retired model is removed from the
model picklists (the enums in patterns/unified/template.yaml and
template.yaml) but keeps its config_library/pricing.yaml entry, so a cost
report covering documents processed while it was still selectable resolves the
right rate. Model IDs in a
configuration are plain strings, not a closed enum, so a stored configuration
that still names a retired model keeps loading — it just fails at invoke time
with:
ResourceNotFoundException: This model version has reached the end of its life.The failing stage’s model is named in the Lambda log line and by the Error
Analyzer agent’s fetch_pipeline_configuration tool. Repoint the stage at a
current model to fix it.
Two failure shapes to distinguish:
-
End of life — the model is gone for everyone, in every region, and no account can invoke it. It is removed from the picklists.
us.amazon.nova-premier-v1:0(end of life 2026-09-14) andus.anthropic.claude-3-5-haiku-20241022-v1:0are examples. You can confirm the state yourself:aws bedrock get-foundation-model --model-identifier amazon.nova-premier-v1:0answersResourceNotFoundExceptionwith the end-of-life message, while a live model returns its details. -
Provider-legacy, account-scoped — the model still exists and existing users can still invoke it, but access is withdrawn per account after inactivity:
ResourceNotFoundException: Access denied. This Model is marked by provider as Legacy and you have not been actively using the model in the last 30 days.Such models stay selectable, because they work for accounts that have used them recently — if you hit this error, either pick a current model or request access again in the Bedrock console.us.anthropic.claude-sonnet-4-5-20250929-v1:0is the selectable model closest to this state: an AWS Health notice puts it in legacy from 2026-10-08 with end of life on 2027-04-08, whichlist-foundation-modelshad not yet reflected when this page was written.us.anthropic.claude-opus-4-1-20250805-v1:0is the model the API does report asLEGACY(end of life 2027-01-08), and it is no longer in any picklist here, so you will meet it only in a configuration stored before it was removed. Read the state yourself rather than from this page:Terminal window aws bedrock list-foundation-models --by-provider anthropic \--query 'modelSummaries[].{id:modelId,lifecycle:modelLifecycle}'A legacy model also receives no further Service Quota increases, so it is a poor choice for new work even while it answers. No default in the commercial partition names one. GovCloud is the exception. That partition offers three models —
us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0,amazon.nova-pro-v1:0andamazon.nova-lite-v1:0— and thelending-package-sample-govcloudpreset names the Sonnet for extraction, summarization, LLM evaluation and confidence escalation, so a GovCloud deployment runs the legacy Sonnet for those stages until that partition offers a successor. (The--govcloudtemplate transform defaults the knowledge-base model toamazon.nova-pro-v1:0; it reaches the Sonnet only by selecting that preset.)
A model removed from the picklists keeps its pricing.yaml entry, so cost
reports covering documents processed while it was selectable still resolve the
right rate.
config-validate catches it before you deploy. idp-cli config-validate and
idp-cli config-upload (unless you pass --no-validate) reject a configuration
that pins an end-of-life model, naming the date and the command that confirms it,
rather than letting the failure appear part-way through a document. Loading a
stored configuration that still names one is unaffected — the model fields are
plain strings and nothing revalidates a stored config — so an existing deployment
does not break on upgrade; it keeps failing at inference exactly as it already
was, until you repoint the stage.
Upgrading a stack that selected a removed model. KnowledgeBaseModelId is a
CloudFormation parameter, so if your stack’s current value is a model that this
release removed from AllowedValues, the stack update is rejected with
Parameter value ... does not match AllowedValues rather than proceeding. Pass a
current model for that parameter in the same update. A removed model named in
your stored configuration (rather than a stack parameter) does not block the
update; repoint that stage in the Configuration editor.
Summarization Configuration
Section titled “Summarization Configuration”Enable/Disable Summarization
Section titled “Enable/Disable Summarization”Summarization can be controlled via the configuration file rather than CloudFormation stack parameters. This provides more flexibility and eliminates the need for stack redeployment when changing summarization behavior.
Configuration-based Control (Recommended):
summarization: enabled: true # Set to false to disable summarization model: us.anthropic.claude-sonnet-4-5-20250929-v1:0 temperature: 0.0 # ... other summarization settingsKey Benefits:
- Runtime Control: Enable/disable without stack redeployment
- Cost Optimization: Zero LLM costs when disabled (
enabled: false) - Simplified Architecture: No conditional logic in state machines
- Backward Compatible: Defaults to
enabled: truewhen property is missing
Behavior When Disabled:
- Summarization lambda is still called (minimal overhead)
- Service immediately returns with logging: “Summarization is disabled in configuration”
- No LLM API calls or S3 operations are performed
- Document processing continues to completion
Note: Prior to v0.4.0, this feature was controlled by the IsSummarizationEnabled CloudFormation parameter. The configuration-based approach provides runtime control without requiring stack redeployment.
Confidence (Assessment) Configuration
Section titled “Confidence (Assessment) Configuration”As of config v0.6, per-field confidence and geometry are outputs of
extraction, configured under extraction.confidence.* and
extraction.geometry.* — there is no top-level assessment.{model, geometry_mode, ...} block anymore. Human-in-the-loop review is configured under the top-level
hitl.* block. See Extraction & Confidence for
the full reference.
Enable/Disable Confidence
Section titled “Enable/Disable Confidence”Confidence is controlled via the configuration file rather than CloudFormation stack parameters. This provides runtime control without stack redeployment.
Configuration-based Control (Recommended):
extraction: confidence: enabled: true # false disables confidence entirely (zero LLM cost) mode: separate # off | separate (default) | integrated model: us.amazon.nova-lite-v1:0 temperature: 0.0 list_batch_size: 25 # rows per assessment batch for large lists # ... other confidence settingsConfidence modes (extraction.confidence.mode):
separate(default) — on the Simple path, confidence runs as the standalone Assessment step; on the Advanced (agentic) path it runs inside each extraction shard and the standalone step auto-skips.integrated— a single extraction inference returns values and inline confidence together (works on both the simple and agentic paths); the standalone Assessment step auto-skips.off— no confidence scoring (equivalent toenabled: false); zero LLM cost.
Behavior When Disabled (enabled: false or mode: off):
- Assessment lambda is still called (minimal overhead)
- Service immediately returns with logging: “Assessment is disabled via configuration”
- No LLM API calls or S3 operations are performed
- Document processing continues to completion
Note: Prior to v0.4.0, this feature was controlled by the IsAssessmentEnabled CloudFormation parameter. The configuration-based approach provides runtime control without requiring stack redeployment.
Large lists (list_batch_size)
Section titled “Large lists (list_batch_size)”For complex documents with large lists (bank statements with hundreds of
transactions, line-item tables), the standalone Assessment step batches large
lists automatically: it slices the largest list field into
extraction.confidence.list_batch_size chunks (default 25), scores each chunk
sequentially, then reconciles so every list cell gets its own confidence and
bounding box. A bounded missing-row retry re-scores any dropped rows so coverage
reaches 100%. list_batch_size is a ceiling on a derived size: lower it if a chunk
under-enumerates, but raising it does not cut
inference count.
extraction: confidence: enabled: true mode: separate list_batch_size: 25 # rows per assessment batch for large listsGranular assessment is retired. The former “granular assessment” service (parallel thread-pool fan-out with DynamoDB caching, formerly
assessment.granular/extraction.confidence.granularwithmax_workers/simple_batch_size/ etc.) has been retired and deleted. Large-list batching is its full replacement andlist_batch_sizeis the one knob. Any leftovergranular.*keys still validate but are ignored — no config edit required.
For large documents, prefer Advanced (agentic) extraction — it shards both extraction and confidence assessment and yields the best-calibrated confidence.
Classification confidence (classification.confidence.*)
Section titled “Classification confidence (classification.confidence.*)”Separate from the extraction confidence above, and on by default — so an existing deployment that never set it starts paying for it on upgrade.
classification: confidence: mode: topk # topk (default) | off top_k_candidates: 3 # alternative classes returned per pageEach page’s classification returns its top-K candidate classes with probabilities, giving a per-page confidence you can threshold on and a visible reason when a page is ambiguous.
⚠️ It costs output tokens on every page. Unlike extraction confidence, which
is per section, this scales with page count. Measured at roughly +17% of the
classification step, which is a small share of a typical bill because
classification is cheap relative to extraction — but on a page-heavy corpus it is
not free. Set mode: off to return to the previous behaviour.
See Classification for the full reference, including how the
score reaches the UI and what a None confidence means.
v0.5 → v0.6 config migration
Section titled “v0.5 → v0.6 config migration”- Confidence and geometry moved under
extraction.*in v0.6: the former top-levelassessment.*confidence settings are nowextraction.confidence.*, andassessment.geometry_mode/assessment.ground_geometry_in_ocrare nowextraction.geometry.mode. HITL moved to the top-levelhitl.*block. - Migrate-on-read handles old configs automatically — pre-v0.6 configurations are migrated transparently when loaded; no manual edit is required.
- Granular assessment is retired and its config keys are a no-op (they validate but are ignored).
list_batch_sizeis the knob for large lists; for large documents, Advanced (agentic) extraction is recommended.
See Granular Assessment Retirement for details.
For detailed information, see Extraction & Confidence.
Stack Parameters
Section titled “Stack Parameters”Key parameters that can be configured during CloudFormation deployment:
General Parameters
Section titled “General Parameters”AdminEmail: Administrator email for web UI accessAllowedSignUpEmailDomain: Optional domain(s) allowed for web UI user signupMaxConcurrentWorkflows: Control concurrent document processing (default: 100)DataRetentionInDays: Set retention period for documents and tracking records (default: 365 days)ErrorThreshold: Number of workflow errors that trigger alerts (default: 1)ExecutionTimeThresholdMs: Maximum acceptable execution time before alerting (default: 300000 ms)WorkflowExecutionTimeoutSeconds: Execution-level bound on one document’s workflow; an execution still running after it endsTIMED_OUT, releases its concurrency slot, and firesWorkflowTimeoutsAlarm(default: 21600 s, i.e. 6 hours). See MonitoringBDACallbackTimeoutSeconds: How long the BDA step waits for its asynchronous completion callback before failing (default: 7200 s); keep it belowWorkflowExecutionTimeoutSecondsQueueStalledAgeThresholdSeconds: How long the oldest queued document may wait with no queue progress at all beforeDocumentQueueStalledAlarmfires (default: 1800 s). Not a backlog limit — see MonitoringLogLevel: Set logging level (DEBUG, INFO, WARN, ERROR; default:WARN).WARNis the default becauseINFOandDEBUGcan emit sensitive values — document contents, PII and S3 presigned URLs — into CloudWatch. AtINFOorDEBUG, access logging is also enabled on the web UI’s REST API stage (request metadata only — no request/response bodies), capturing requests that fail before reaching a Lambda (e.g. authorizer 401/403s, WAF blocks); at theWARNdefault that access log is not configured, so raiseLogLeveltoINFOif you need that audit trailWAFAllowedIPv4Ranges: IP restrictions for web UI access (default: allow all)CloudFrontPriceClass: Set CloudFront price class for UI distribution (CloudFront hosting only)CloudFrontAllowedGeos: Optional geographic restrictions for UI access (CloudFront hosting only)WebUIHosting: Select hosting mode —CloudFront(default) orAPIGatewayfor VPC-based hosting (see API Gateway Hosting)CustomConfigPath: Optional S3 URI to a custom configuration file that overrides pattern presets. Leave blank to use selected pattern configuration. Example: s3://my-bucket/custom-config/config.yaml
Integration and Tracing Parameters
Section titled “Integration and Tracing Parameters”EnableXRayTracing: Enable X-Ray tracing for Lambda functions and Step Functions (default: true). Provides distributed tracing capabilities for debugging and performance analysis. It covers every traced Lambda function in the main stack, the unified pattern and the nested feature-platform stack, plus both state machines — so setting it tofalseturns tracing off, and stops the associated X-Ray charges, for everything the main stack creates. Onfalsethose functions run inPassThroughmode: they record nothing of their own and continue a trace only if a caller already sampled the request. It does not reach an extension you install from the Extensions catalog: each is a separate stack with its own parameters and traces unconditionally, and for those of its functions whose execution role can write a trace segment that means real X-Ray charges. See monitoring.md.EnableMCP: Enable Model Context Protocol (MCP) integration for external application access via AWS Bedrock AgentCore Gateway (default: true). See mcp-server.md for details.EnableECRImageScanning: Enable automatic vulnerability scanning for Lambda container images in ECR for Patterns 1-3 (default: false). Recommended for production deployments but may impact deployment reliability. See troubleshooting.md for guidance.
Pattern Selection
Section titled “Pattern Selection”IDPPattern: Select processing pattern:- Unified: Supports both BDA and Pipeline processing modes via
use_bdaflag
- Unified: Supports both BDA and Pipeline processing modes via
Pattern-Specific Parameters
Section titled “Pattern-Specific Parameters”- Configuration Preset:
ConfigurationPreset— Select from available presets (lending-package-sample, bank-statement-sample, etc.) - Custom Model ARNs: Optional custom fine-tuned classification/extraction model ARNs
Note: The processing mode (BDA vs Pipeline) is controlled by the
use_bdaflag in the configuration, not by deployment parameters. See the architecture docs for details.
- Pattern 3 (Textract + UDOP + Bedrock)
Optional Features
Section titled “Optional Features”EvaluationBaselineBucketName: Optional existing bucket for ground truth dataDocumentKnowledgeBase: Enable document knowledge base functionalityKnowledgeBaseModelId: Bedrock model for knowledge base queriesPostProcessingLambdaHookFunctionArn: Optional Lambda ARN for custom post-processing (see post-processing-lambda-hook.md for detailed implementation guidance)BedrockGuardrailId: Optional Bedrock Guardrail ID to applyBedrockGuardrailVersion: Version of Bedrock Guardrail to use
For details on processing modes, see architecture.md. For legacy pattern-specific references, see pattern-1.md (BDA) and pattern-2.md (Pipeline).
High Volume Processing
Section titled “High Volume Processing”Request Service Quota Limits
Section titled “Request Service Quota Limits”For high-volume document processing, consider requesting increases for these service quotas:
- Lambda Concurrent Executions: Default 1,000 per region
- Step Functions Executions: Default 25,000 per second (Standard workflow)
- Bedrock Model Invocations: Varies by model and region
- Claude models: Typically 5-20 requests per minute by default
- Titan models: 15-30 requests per minute by default
- SQS Message Rate: Default 300 per second for FIFO queues
- TextractLimitPage API: 15 transactions per second by default
- DynamoDB Read/Write Capacity: Uses on-demand capacity by default
Use the AWS Service Quotas console to request increases before deploying for production workloads. See monitoring.md for details on monitoring your resource usage and quotas.
Cost Estimation
Section titled “Cost Estimation”The solution provides built-in cost estimation capabilities:
- Real-time cost tracking for Bedrock model usage
- Per-document processing cost breakdown
- Historical cost analysis and trends
- Budget alerts and threshold monitoring
See the cost considerations guide for the cost drivers of each processing mode, the optimization levers, what the built-in estimate cannot express, and how to attribute Bedrock spend with an application inference profile.
Bedrock Guardrail Integration
Section titled “Bedrock Guardrail Integration”The solution supports Amazon Bedrock Guardrails for content safety and compliance across all patterns:
How Guardrails Work
Section titled “How Guardrails Work”Guardrails provide:
- Content Filtering: Block harmful, inappropriate, or sensitive content
- Topic Restrictions: Prevent processing of specific topic areas
- Data Protection: Redact or block personally identifiable information (PII)
- Automated Reasoning Checks: Enable formal verification of model outputs against defined policies using Automated Reasoning, ensuring factual consistency and logical correctness
- Custom Filters: Define organization-specific content policies
Configuring Guardrails
Section titled “Configuring Guardrails”Guardrails are configured with two CloudFormation parameters:
BedrockGuardrailId: The ID (not name) of an existing Bedrock GuardrailBedrockGuardrailVersion: The version of the guardrail to use (e.g., “DRAFT” or “1”)
This applies guardrails to all Bedrock model interactions, including:
- Document extraction (all patterns)
- Document summarization (all patterns)
- Document classification (Pattern 2 only)
- Knowledge base queries (if enabled)
Best Practices
Section titled “Best Practices”- Test Thoroughly: Validate guardrail behavior with representative documents
- Monitor Impact: Track processing latency and accuracy changes
- Regular Updates: Review and update guardrail policies as requirements evolve
- Compliance Alignment: Ensure guardrails align with organizational compliance requirements
For more information on creating and managing Guardrails, see the Amazon Bedrock documentation.
Concurrency and Throttling Management
Section titled “Concurrency and Throttling Management”The solution implements sophisticated concurrency control and throttling management:
Throttling and Retry (Bedrock, Textract, SageMaker)
Section titled “Throttling and Retry (Bedrock, Textract, SageMaker)”- Exponential Backoff: Automatic retry with increasing delays
- Jitter Addition: Random delay variation to prevent thundering herd
- Circuit Breaker: Temporary halt on repeated failures
- Rate Limiting: Configurable request rate controls
The solution tracks metrics for throttling events and successful retries, viewable in the CloudWatch dashboard.
Step Functions Retry Configuration
Section titled “Step Functions Retry Configuration”The state machine retries each processing task on transient failures only. Step
Functions matches the error name the Lambda reports (the Python exception class),
so each task lists the Lambda service errors and the Bedrock throttling /
availability codes. Eight task states additionally list TransientError — the five
extraction and assessment states (in-process extraction, shard plan, shard, shard
merge, assessment) and the three rule-validation states (policy classification,
per-section rule validation, orchestration). That is the one name their handlers
re-raise a transient cause under when it arrives as an ordinary Python exception (a
botocore read or connect timeout, a dropped connection, a Strands wrapper around
one). The classification lives in
idp_common.utils.transient_errors. It judges an exception by its own error
code first (a ValidationException is deterministic whatever its message says),
and looks through only explicit raise ... from wrappers, so a wrapper cannot
hide a transient root and a swallowed transient error cannot lend its transience
to an unrelated failure raised after it.
Why the name rather than the codes. A Retry.ErrorEquals entry can only match a
class name, so it matches a Bedrock throttle only because botocore names the
exception class after the modeled error code. Most transient failures do not arrive
that way — a read timeout is a ReadTimeoutError, a throttling code that is not
modeled on the operation arrives as a bare ClientError, and ModelTimeoutException
and InternalServerException are modeled but were never in the shorter lists. Listing
every such name in every state would be a second, drifting copy of the
classification, so the library answers the question once and reports the answer under
one name. A state whose handler does not re-raise that name must not list it, and
patterns/unified/tests/test_workflow_transient_retry.py checks both directions.
Deterministic failures — a malformed request (ValidationException), a schema
violation, an unparseable document, missing input — keep their own names and are
not retried: eight attempts at 2.5× backoff cannot make a document that fails the
same way every time succeed, they only multiply its cost and delay. No task retries
States.TaskFailed or States.ALL for that reason.
A Lambda timeout is deterministic, so it gets its own retrier with
MaxAttempts: 1. Sandbox.Timedout is Step Functions’ name for a function that
hit its configured timeout, States.Timeout for a task that hit the state’s own
bound, and Lambda.Unknown for an unhandled Lambda fault — a timeout is one cause
of it and an out-of-memory kill the other. In all three cases the document needs
more work than the function has room for, so each further attempt burns another
full timeout and fails identically. While these codes shared the transient ladder,
one such document held a workflow-concurrency slot for about 5.2 hours (8 attempts
of 900 s at 2.5× backoff) before failing anyway. Every Lambda task state now
carries the single-attempt timeout retrier, which previously only EvaluationStep
had. Throttling and Lambda service errors keep the full ladder — the split narrows
what is retried, it does not weaken retrying for genuinely transient faults.
{ "Retry": [ { "ErrorEquals": [ "States.Timeout", "Lambda.Unknown", "Sandbox.Timedout" ], "IntervalSeconds": 5, "MaxAttempts": 1, "BackoffRate": 1.0 }, { "ErrorEquals": [ "Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException", "ThrottlingException", "ServiceUnavailableException", "InternalServerException", "TransientError" ], "IntervalSeconds": 10, "MaxAttempts": 8, "BackoffRate": 2.5 } ]}Per-state values differ (the shard-plan and policy-classification states use shorter
intervals and 6 attempts on the transient ladder, and Lambda.Unknown appears only
where a state already listed it), but the shape above holds everywhere: one
single-attempt timeout retrier, one generous transient retrier, and no
States.ALL. scripts/tests/test_state_machine_retry_policies.py enumerates the
Lambda task states out of workflow.asl.json and asserts both halves, so a state
added later is covered without editing the test.
Adding a Lambda task state to the workflow
Section titled “Adding a Lambda task state to the workflow”Because that test enumerates the definition rather than naming states, a new Lambda
task state inherits three requirements the moment it is added. All 24 existing states
comply; a new one that does not will fail make test-packages-cicd in both CIs,
naming the state:
- It must carry a transient retrier — at least one of
ThrottlingException,Lambda.TooManyRequestsException,Lambda.ServiceExceptionorServiceUnavailableException, withMaxAttemptsof 3 or more. A state with noRetryblock at all fails: one Bedrock or Textract throttle would otherwise lose the document. This is a repo-wide rule rather than a property of the states #917 happened to touch, deliberately — #917 was originally fixed as a list of state names, which is exactly why eleven of the twelve task states then in the file kept the wrong ladder for a release. - Timeout codes, if listed, go in their own retrier with
MaxAttempts: 1. Mixing them into the transient retrier fails a separate assertion, because the two budgets are different numbers and sharing one retrier means changing either changes both. - No wildcard retrier.
States.ALLandStates.TaskFailedin aRetryblock put timeouts, unparseable documents and bad schemas on whatever ladder they carry, which would bypass both rules above without naming a timeout code.
If a future state genuinely must not retry — an idempotency hazard, for instance — add it to a named, commented exemption set in that test file rather than deleting the assertion.
Sharded extraction: the shard Map, and the budget inside one shard
Section titled “Sharded extraction: the shard Map, and the budget inside one shard”Advanced (agentic) extraction splits a section into shards and runs each in its own
Lambda invocation through a Distributed Map, ExtractionShardMap. Three things about
how that Map handles failure, because they interact:
No shard failure is tolerated, on purpose. The Map sets neither
ToleratedFailurePercentage nor ToleratedFailureCount, so Step Functions’ default
of zero applies and one failed shard fails the Map. Raising the tolerance would let a
document complete with shards missing, and a document that reports success with
part of its data absent is worse than one that fails: nothing downstream — not
confidence scoring, not evaluation, not the UI — can tell that anything is gone.
The Map itself is retried once. Every shard persists its own result to S3 as soon as it finishes, and a shard whose result is already there loads it instead of re-inferring. So retrying the Map re-runs only the shards that did not complete, which is what that persistence exists for.
Be clear about what that retry costs, because it is not one extra invocation. For a
shard that completed it costs nothing. For the shard that failed, the second Map pass
re-runs ShardExtractionStep’s entire retry ladder from the start:
| The failed shard hit | One Map pass | With the retry |
|---|---|---|
Sandbox.Timedout (single-attempt timeout retrier) | 2 × 900 s | 4 × 900 s |
| a transient error (8 attempts, 10 s at 2× = 2,550 s of backoff) | up to 9 × 900 s + 2,550 s | that again, bounded only by WorkflowExecutionTimeoutSeconds (21,600 s by default) |
That trade is deliberate: one duplicated ladder is worth not discarding a document’s
completed shards. It is also why the count is one and stays one — at two or more, a
deterministic shard failure multiplies whole ladders rather than attempts.
scripts/tests/test_state_machine_retry_policies.py holds Map-level retriers to the
same single-attempt rule as the Lambda task states.
A failed Map names the section and the cause. The Catch on
ExtractionShardMap routes to a Fail state, so the execution reports
ExtractionShardMapFailed with a cause carrying the section id and the Map’s error
output rather than a bare States.ExceedToleratedFailureThreshold. The failing shard’s
individual error lives in the Map Run, reachable from the ExtractionShardMap
entry in the execution history — a Distributed Map records per-iteration failures there
rather than on the parent execution. The catcher names that one error rather than
States.ALL, because the Map’s other failure modes (States.DataLimitExceeded,
States.Runtime) already report a specific and differently-actionable condition.
Inside one shard, several durations draw on the same invocation:
| Value | What it bounds | |
|---|---|---|
Agentic read_timeout | 600 s | One socket read on the streamed extraction call — time to first response event, then each inter-event gap |
Confidence read_timeout | 300 s | One non-streamed converse, which bounds the whole response rather than a gap, so it is legitimately larger. Inside the shard invocation whenever confidence runs in separate mode |
| botocore attempts per call | 1 | botocore retries a read timeout itself, multiplying either timeout above inside a single call, where no deadline check can see it |
| Retry backoff allowance | 90 s | Total time the retry ladder around the agent call may spend asleep, across all attempts |
Lambda Timeout | 900 s | The whole invocation — Lambda’s maximum, so it cannot be widened |
What keeps a shard inside its invocation is not that these numbers sum to less than
900 — they do not. It is that the retry ladder refuses to begin an attempt the size of
the one that just failed when the remaining invocation cannot hold it, and raises the
underlying error instead. That surfaces as a ReadTimeoutError the shard handler wraps
as TransientError, which ShardExtractionStep retries eight times against a
state-machine budget of 21,600 s. The alternative is that the invocation is killed at
900 s: Step Functions reports that as Sandbox.Timedout, which is deterministic and
retried once (see above), so the transient blip a retry would have cleared becomes the
failure that is not retried.
⚠️ Do not lower the agentic read_timeout to make the table add up. That is what
600 s → 180 s did, and it broke the largest-table extraction in this repository’s own
integration suite for a week. The reasoning behind the cut — “the path streams, so a
healthy generation emits deltas continuously and three minutes of silence is a stall by
definition” — is wrong for an agent loop: a gap here also covers the whole wait between
submitting a tool result and the first event of the model’s reply, on a request carrying
a large cached prefix and a dozen-plus page images. Measured on samples/Nuveen.pdf
(532 table rows, 17 pages) that gap exceeds 180 s reproducibly and fits inside 600 s.
Change the value only against a measurement of that gap
(#1310).
The numbers live together in idp_common.timeout_budget, and
lib/idp_common_pkg/tests/unit/extraction/test_shard_timeout_budget.py asserts the
remaining arithmetic — one agentic stall plus all the backoff leaves room to return —
reading the resolved client configurations rather than the source, so botocore’s own
attempt count is inside the bound, along with the ladder’s stop behaviour and the Map’s
Retry, Catch and zero tolerance.
⚠️ One exposure the arithmetic above does not close. The BedrockClient used for
the non-streamed call has its own retry ladder (7 attempts, backing off 2 s doubling to
300 s) which does not consult the invocation deadline, so on the separate-confidence
shard path it can overrun an invocation by itself regardless of the table above. The
budget bounds one stalled call per client, not that ladder. Making it deadline-aware
changes behaviour for every non-agentic step — classification, simple extraction,
summarization, assessment — so it is tracked separately; the test above pins its
numbers so the exposure cannot drift unnoticed.
Concurrency Control
Section titled “Concurrency Control”- Workflow Limits: Maximum concurrent Step Function executions, controlled by
MaxConcurrentWorkflowsparameter - Lambda Concurrency: Per-function concurrent execution limits
- Queue Management: SQS visibility timeout (30 seconds) and message batching
- Dynamic Scaling: Automatic adjustment based on queue depth and in-flight workflows
Document Status Tracking
Section titled “Document Status Tracking”The solution provides multiple ways to track document processing status:
Using the Web UI
Section titled “Using the Web UI”The web UI dashboard provides a real-time view of document processing status, including:
- Document status (queued, processing, completed, failed)
- Processing time
- Classification results
- Extraction results
- Error details (if applicable)
See web-ui.md for details on using the dashboard.
Using the Lookup Script
Section titled “Using the Lookup Script”Use the included script to check document processing status via CLI:
bash scripts/lookup_file_status.sh <DOCUMENT_KEY> <STACK_NAME>Response Format
Section titled “Response Format”Status lookup returns comprehensive information:
{ "document_key": "example.pdf", "status": "COMPLETED", "workflow_arn": "arn:aws:states:...", "start_time": "2024-01-01T12:00:00Z", "end_time": "2024-01-01T12:05:30Z", "processing_time_seconds": 330, "pages_processed": 15, "document_class": "BankStatement", "attributes_found": 12, "output_location": "s3://output-bucket/results/example.json", "error_details": null}Evaluation Extensions in JSON Schema
Section titled “Evaluation Extensions in JSON Schema”Document class schemas support evaluation-specific extensions for fine-grained control over accuracy assessment. These extensions work with the Stickler-based evaluation framework to provide flexible, business-aligned evaluation capabilities.
Available Extensions
Section titled “Available Extensions”x-aws-idp-evaluation-method: Comparison method (EXACT, FUZZY, LEVENSHTEIN, NUMERIC_EXACT, SEMANTIC, DATE, LLM, HUNGARIAN)x-aws-idp-evaluation-method-config: Optional comparator config (used by DATE:dayfirst,tolerance,range_mode)x-aws-idp-evaluation-threshold: Minimum score to consider a match (0.0-1.0)x-aws-idp-evaluation-weight: Field importance for weighted scoring (default: 1.0, higher values = more important)
Example Configuration
Section titled “Example Configuration”classes: - $schema: "https://json-schema.org/draft/2020-12/schema" x-aws-idp-document-type: "Invoice" x-aws-idp-evaluation-match-threshold: 0.8 # Document-level threshold properties: invoice_number: type: string x-aws-idp-evaluation-method: EXACT x-aws-idp-evaluation-weight: 2.0 # Critical field - double weight invoice_date: type: string x-aws-idp-evaluation-method: FUZZY x-aws-idp-evaluation-threshold: 0.9 x-aws-idp-evaluation-weight: 1.5 # Important field vendor_name: type: string x-aws-idp-evaluation-method: FUZZY x-aws-idp-evaluation-threshold: 0.85 x-aws-idp-evaluation-weight: 1.0 # Normal weight (default) vendor_notes: type: string x-aws-idp-evaluation-method: SEMANTIC x-aws-idp-evaluation-threshold: 0.7 x-aws-idp-evaluation-weight: 0.5 # Less critical - half weightStickler Backend Integration
Section titled “Stickler Backend Integration”The evaluation framework uses Stickler as its evaluation engine. The SticklerConfigMapper automatically translates these IDP extensions to Stickler’s native format, providing:
- Field-level weighting for business-critical attributes
- Optimal list matching using the Hungarian algorithm
- Extensible comparator system with exact, fuzzy, numeric, semantic, and LLM-based comparison
- Native JSON Schema support with $ref resolution
Benefits
Section titled “Benefits”- Business Alignment: Weight critical fields higher to ensure evaluation scores reflect business priorities
- Flexible Comparison: Choose the right evaluation method for each field type
- Tunable Thresholds: Set field-specific thresholds for matching sensitivity
- Dynamic Schema Generation: Auto-generates evaluation schema from baseline data when configuration is missing (for development/prototyping)
For detailed evaluation capabilities and best practices, see evaluation.md.
Section Splitting Strategies
Section titled “Section Splitting Strategies”Pattern-2 and Pattern-3 support configurable strategies for how classified pages are grouped into document sections. This is controlled by the sectionSplitting configuration field:
Available Strategies
Section titled “Available Strategies”-
disabled: Treats the entire document as a single section. With page-level classification the section takes the class most pages were given, counting only pages given a class the configuration defines (ties go to the earliest page); with holistic classification it takes the first segment’s class. Simplest approach for single-document processing. -
page: Creates one section per page, preventing automatic joining of same-type documents. Useful for deterministic processing of documents containing multiple forms of the same type (e.g., multiple W-2s, multiple invoices in one packet). -
llm_determined(default): Uses LLM boundary detection with “Start”/“Continue” indicators to intelligently segment multi-document packets. Best for complex scenarios where document boundaries are not obvious.
Configuration Example
Section titled “Configuration Example”classification: sectionSplitting: page # or "disabled", "llm_determined"Use Cases
Section titled “Use Cases”- Single Document Processing: Use
disabledfor simplicity - Multiple Same-Type Forms: Use
pagefor deterministic splitting (resolves Issue #146) - Complex Multi-Document Packets: Use
llm_determinedfor intelligent boundary detection
For more details on classification methods and section splitting, see classification.md.
Page Limit Configuration
Section titled “Page Limit Configuration”Control how many pages are used during document classification to optimize performance and costs:
classification: maxPagesForClassification: "ALL" # or "1", "2", "3", etc.Behavior:
- “ALL” (default): Uses all pages for classification
- Numeric value: Classifies only the first N pages, then applies that classification to the entire document
Important: When using a numeric limit, the classification result from the first N pages is applied to ALL pages, effectively forcing a single class/section for the entire document.
Use Cases:
- Performance optimization for large documents
- Cost reduction for documents with consistent patterns
- Simplified processing for homogeneous document types
Prompt Preview
Section titled “Prompt Preview”The Configuration page includes a Prompt Preview tab that lets you see the actual prompts sent to the LLM for each processing step (Classification, Extraction, Assessment, Summarization) with your configuration values filled in. This is useful for optimizing document class schemas and prompt templates — you can see exactly how your class names, descriptions, and JSON Schema attributes appear in the prompt that the LLM receives. See web-ui.md for details.
Prompt Optimization
Section titled “Prompt Optimization”Bedrock Prompt Caching
Section titled “Bedrock Prompt Caching”The solution supports Bedrock prompt caching to reduce costs and improve performance by caching static portions of prompts. This feature is available across all patterns for classification, extraction, assessment, and summarization.
How It Works
Section titled “How It Works”Insert a <<CACHEPOINT>> delimiter in your prompt to separate static (cacheable) content from dynamic content:
extraction: task_prompt: | You are an expert document analyst. Follow these rules: - Extract exact values from the document - Preserve formatting as it appears
<<CACHEPOINT>>
Document to process: {DOCUMENT_TEXT}Everything before the <<CACHEPOINT>> delimiter is cached and reused across similar requests, while content after it remains dynamic. This can significantly reduce token costs and improve response times.
Best Practices
Section titled “Best Practices”- Place Static Content First: Instructions, rules, schemas, and examples should come before the cachepoint
- Dynamic Content Last: Document text, images, and variable data should come after the cachepoint
- Cache Hit Optimization: Keep static content consistent across requests for maximum cache utilization
Benefits
Section titled “Benefits”- Cost Savings: Cached tokens cost significantly less than regular input tokens
- Performance: Reduced processing time for cached content
- Token Efficiency: Particularly beneficial for long system prompts or few-shot examples
Minimum cacheable prefix — per model, and not monotonic
Section titled “Minimum cacheable prefix — per model, and not monotonic”A <<CACHEPOINT>> only creates a cache entry if the prefix before it clears the
model’s minimum cacheable prefix. Below it Bedrock reports cacheWrite = 0 and
cacheRead = 0, raises nothing, and bills the prefix at full input price on every
request. The minimum is model-dependent and newer is not safer:
| Model | Minimum cacheable prefix |
|---|---|
| Claude Opus 5, Opus 5.5, Fable 5, Haiku 5.5 | 512 tokens |
| Claude Sonnet 5, Sonnet 4.6, Sonnet 4.5, Sonnet 4, Opus 4.8, Opus 4.1, Opus 4, 3.7 Sonnet | 1,024 tokens |
| Claude Opus 4.7 | 2,048 tokens |
| Claude Opus 4.6, Opus 4.5, Haiku 4.5 | 4,096 tokens |
| Amazon Nova | ≤ 355 tokens (below any shipped class) |
Measured across the shipped presets, 25% of classes never cache on the 1,024-token tier and none do on Haiku 4.5 — choosing Haiku 4.5 to save money on extraction gets you no caching at all, with no indication of it in the response.
Note the two Haikus sit at opposite ends of this table, so “Haiku” is not a tier. Haiku 5.5’s minimum is 512 tokens — the lowest here, 8x below Haiku 4.5’s — which makes it the one cheap model where caching is worth configuring rather than written off: a class that never cached on Haiku 4.5 may well cache on it.
idp-cli config-validate (and the SDK validate operation) now warns per class
when a Simple-mode extraction prompt prefix — system prompt plus the task prompt up
to the marker, with the class schema substituted — is under the configured extraction
model’s minimum, naming both numbers. The estimate is chars/4, accurate to about
±10% against Bedrock’s own count on the surveyed classes, so a class whose estimate
lands within 10% of the minimum is reported as “may not cache” rather than declared
safe. Remedies: add real field descriptions to the class (which also helps
extraction), pick a model with a lower minimum, or turn caching off (below).
What the warning does not cover, deliberately: the classification, assessment
(confidence) and rule-validation prompts, which also carry <<CACHEPOINT>> markers
and keep them regardless of the setting below; the Advanced (agentic) extraction
path, whose prefix is the agent’s own system prompt; the forced-tool toolSpec and
the multi-instance detection probe (both off by default, both make the real prefix
longer, so the estimate errs toward warning); application inference-profile ARNs
(not resolved to a base model, so no warning); and the newer tokenizer introduced
with Claude Opus 4.7 and shared by Opus 4.8, Opus 5, Opus 5.5 and Fable 5, which
produces up to
about 1.35× the tokens the estimate assumes — again in the direction of a spurious
warning, never a missed one.
Turning caching off (extraction.prompt_cache: off)
Section titled “Turning caching off (extraction.prompt_cache: off)”A cache write is billed at 1.25× input price and only pays back when a second request with the same prefix arrives inside the 5-minute TTL — break-even is exactly the second request. A deployment that processes one document of a class per TTL pays about +25% on the prefix for nothing. Decline it with:
extraction: prompt_cache: off # auto (default) | offoff sends no cache points on either extraction path (Simple and Advanced) and
suppresses the validation warning above. The setting is also in the Web UI under
Configuration → Extraction → Prompt caching. A bare off in YAML parses as the
boolean false; both spellings (and "off" quoted) are accepted. It applies to
extraction only: classification, assessment and rule-validation prompts keep
their cache points.
Reading cache efficiency back (per phase and per class)
Section titled “Reading cache efficiency back (per phase and per class)”Cache read and write tokens have always been metered and priced, but a row of numbers does not say whether the cache point did anything. The product now classifies them into one of six states wherever they are shown (the literal value, as stored in the section result and the Athena columns, in parentheses):
| State | Meaning | What to do |
|---|---|---|
caching (caching) | Reads are landing; the ~0.1× read price applies to the prefix (the read share is shown) | Nothing |
write-only (write-only) | Writes with no reads: paying 1.25× on the prefix and collecting nothing | Expected when a class is processed once per 5-minute TTL; a low-volume deployment can set prompt_cache: off |
never cached (never-cached) | Reads and writes are both zero although a cache point reached a model that supports it: the cache point is inert | The prefix is below the model’s minimum (named); run idp-cli config-validate for the per-class estimate, add real field descriptions, or pick a model with a lower minimum |
off (disabled) | extraction.prompt_cache: off, so zero/zero is the intended outcome | Nothing |
no cache point (no-cache-point) | No cache point reached the model: the prompt has no <<CACHEPOINT>> marker, or the model is not one the client sends cache points to (Claude still reports cacheReadInputTokens: 0 in that case, so the counts alone cannot tell this from never cached) | Add a marker, or nothing if caching was not wanted |
| ↳ on an implicit-caching model | Same state, different meaning — and the report says so explicitly. OpenAI GPT-6 Astra (Converse) and GPT-5.4 / GPT-5.5 (bedrock-mantle) cache without a cachePoint block, so this state is the normal, healthy one for them and does not mean caching is unavailable. Astra in fact rejects an explicit cache point. Expect the state to become caching once a prefix is seen a second time within the TTL. GPT-5.6 Sol/Terra/Luna are not in this set: their caching is explicit, driven by a <<CACHEPOINT>> marker the client translates into a Responses-API breakpoint (see OpenAI models), so for them the generic row above applies and the remedy is to add a marker | Nothing — do not add a <<CACHEPOINT>> marker for Astra or GPT-5.4/5.5 |
no cache data (no-cache-data) | The backend reported no cache units at all (a LambdaHook, a model without them) | Nothing can be concluded |
Measured reads or writes always win over the configuration flag: if tokens were cached, the state says so. The view is in three places:
- Per phase — Web UI document panel, cost table. Each phase’s subtotal row (OCR,
Classification, Extraction, Summarization…) carries the verdict for that phase; hover
for the token counts. Derived from the document’s metering map, which is keyed by
phase and model, so this level cannot tell an inert cache point from one that was
never sent, and says so; each context is matched exactly, so escalation calls have
their own row. The
extraction.prompt_cacheknob is only mentioned on the Extraction row, since it governs nothing else. - Per class — Web UI section Processing Report tab, “Processing Path”. The section’s
own cache read / written / uncached input tokens and request count, the state, and for
never cached the model’s minimum cacheable prefix. The same line is in the text
report. The source is
metadata.prompt_cachein the section’sresult.json, recorded by the extraction service before the section’s metering is folded into the document total (extraction only, including escalation calls). Whether a cache point reached the model is known here (marker present after the knob, and a supported model; an inference-profile ARN counts as unknown), which is what separates no cache point from never cached. - Per class across documents — Athena. Because the section result is flattened into
the
document_sections_<class>tables, the same fields are queryable as"metadata.prompt_cache.state","metadata.prompt_cache.read_share","metadata.prompt_cache.cache_read_input_tokens"and so on; see the sample query in reporting-database.md.
For pricing details on cached tokens, see cost-calculator.md.
Regex-Based Classification (Pattern-2)
Section titled “Regex-Based Classification (Pattern-2)”Pattern-2 supports optional regex patterns in document class definitions for performance optimization and deterministic classification when patterns are known.
Configuration
Section titled “Configuration”Add regex patterns to your class definitions:
classes: - name: W2 Tax Form description: IRS Form W-2 Wage and Tax Statement document_name_regex: "^w2_.*\\.pdf$" # Matches filenames starting with "w2_" document_page_content_regex: "Form W-2.*Wage and Tax Statement"
- name: Invoice description: Commercial invoice document_name_regex: "^invoice_\\d{6}\\.pdf$" # Matches invoice_123456.pdf document_page_content_regex: "^INVOICE\\s+#\\d+"Classification Logic
Section titled “Classification Logic”- Document Name Matching: If
document_name_regexmatches the document filename, all pages are classified as that type without LLM processing - Page Content Matching: During multimodal page-level classification, if
document_page_content_regexmatches page text, that page is classified without LLM processing - Fallback: If no regex matches, standard LLM classification is used
Benefits
Section titled “Benefits”- Performance: Significant speed improvements by bypassing LLM calls for known patterns
- Cost Savings: Reduced token consumption for documents matching regex patterns
- Deterministic: Consistent classification results for known document patterns
- Backward Compatible: Seamless fallback to LLM classification when patterns don’t match
Monitoring
Section titled “Monitoring”The system logs INFO-level messages when regex patterns match, providing visibility into optimization effectiveness.
For examples and demonstrations, see the step2_classification_with_regex.ipynb notebook.
OCR Backend Configuration (Pattern-2 and Pattern-3)
Section titled “OCR Backend Configuration (Pattern-2 and Pattern-3)”Patterns 2 and 3 support multiple OCR backend engines for flexible document processing:
Available Backends
Section titled “Available Backends”- Textract (default): AWS Textract with advanced feature support (TABLES, FORMS, SIGNATURES, LAYOUT). Cheapest for raw text (~$1.50/1K pages); TABLES +$15/1K, FORMS +$50/1K.
- BDA: Amazon Bedrock Data Automation “standard output” used as a pure OCR engine — reading-order markdown with tables and layout plus word-level confidence/bounding boxes in one call, flat $10/1K pages. Auto-enables the agentic extraction table tool. Best for table-heavy documents and predictable pricing without composing Textract features. (Distinct from the whole-pipeline BDA mode
use_bda, which also does classification/extraction.) - Bedrock: LLM-based OCR using Claude/Nova models with customizable prompts for better handling of complex documents
- None: Image-only processing without OCR (useful for pure visual analysis)
Configuration Example
Section titled “Configuration Example”ocr: backend: textract # or "bda", "bedrock", "none"
# Textract features. DEFAULT: TABLES + LAYOUT + SIGNATURES (see trade-off below). features: - name: TABLES - name: LAYOUT - name: SIGNATURES
# For BDA backend (optional): use a specific standard-output SYNC project # instead of the per-stack <stackname>_OCR_StdOutput project the stack # provisions (delivered via the BDA_OCR_PROJECT_ARN env var). # # NOTE: backend "bda" is COMMERCIAL-PARTITION ONLY. GovCloud and China offer # Bedrock Data Automation but reject the SYNC document-modality project this # backend needs, so the stack does not create it there and the ARN is empty. # See docs/govcloud-deployment.md#bedrock-data-automation-as-the-ocr-backend. bda_project_arn: null
# For Bedrock backend: bedrock_model: us.anthropic.claude-sonnet-4-5-20250929-v1:0 system_prompt: "You are an OCR expert..." task_prompt: "Extract all text from this document..."When to choose BDA vs Textract for OCR
Section titled “When to choose BDA vs Textract for OCR”- BDA — you want table-aware OCR (bank/brokerage statements, invoices) with a single flat per-page price and no feature tuning. One call returns markdown tables, layout, word confidence, and bounding boxes; per-page processing scales past BDA’s ~10-page synchronous limit automatically.
- Textract — you need only raw text (much cheaper:
$0.0015/page), or you already tune specific Textract features. For table-heavy docs,$0.015/page, withTABLESon Textract (LAYOUTandSIGNATURESfree alongside it) costs somewhat more per page than BDA ($0.01/page) up to 1M pages/month and the same above it (Textract’sTABLEStier drops to $0.010); benchmark accuracy on your corpus rather than choosing on price alone.
Textract features & the TABLES cost/accuracy trade-off
Section titled “Textract features & the TABLES cost/accuracy trade-off”ocr.features selects which Amazon Textract analysis features run. The default is
TABLES + LAYOUT + SIGNATURES — of which only TABLES is billed (see
below).
TABLESis on by default because tables are common and the accuracy gain is large. It makes Textract emit structured Table/Cell blocks (with per-cell text, confidence, and geometry) that linearize into clean Markdown pipe-tables for the agentic table parser — yielding more complete extraction and more accurate confidence/geometry on table-heavy documents. In validation on a 24-page brokerage statement,TABLESextracted all 1,440 rows (every page) whileLAYOUT-only silently dropped ~5 pages (~300 rows) where the plain-text linearization mis-segmented the table.- Cost trade-off (
TABLES≈ $0.015/page, withLAYOUTandSIGNATURESfree alongside it, vsLAYOUT-only ≈ $0.004/page — ~3.75× on the Textract line item):- Documents with tables: the extra OCR cost is typically more cost-effective and scalable end-to-end — cleaner cell structure means fewer LLM extraction retries, fewer confidence truncations/re-batches, and less downstream correction than fighting a mis-linearized plain-text table.
- Documents without tables:
TABLESadds cost with no benefit. If your corpus is table-free (forms, prose, single-value docs), remove theTABLESentry to fall back to cheaperLAYOUT-only OCR.
Set ocr.features per configuration to match the documents each stack processes.
The SIGNATURES feature
Section titled “The SIGNATURES feature”SIGNATURES is on by default, because signature presence is a common
extraction target (loan packages, tax forms, claims, consents) and it adds no
Textract charge in the default combination. The
Textract pricing page states it
directly: “Signatures feature is included free of cost with any combination of
Forms, Tables, Queries, and Layout” — AWS emits no usage type at all for a feature
that is free in combination. Used alone, without any of those features,
SIGNATURES is billed at ~$0.0035/page.
With SIGNATURES enabled, Textract reports each region it believes contains a
signature, as a detection confidence plus a bounding box — not text. Those
detections reach the rest of the pipeline in three places:
- Page text (used by the extraction prompt and the Web UI markdown view) —
the linearizer inserts an inline
[SIGNATURE]token per detection, and anOCR signature detectionsblock is appended listing each one’s normalized position and confidence. textConfidence.json(used by the confidence/assessment prompt) — the same block, appended after the per-line confidence table.pageData.json— asignaturesarray, which the Web UI page viewer lists under Signature detections with a clickable bounding box and a colour-coded confidence.
Read the confidence. A detection is not proof of a signature: Textract will
flag a stray pen mark, smudge or scanning artifact, typically at low
confidence (single- or low-double-digit). The appended block reports each
detection’s confidence band, its page position in left/right + upper/lower terms,
— because a bare left=0.572 is not usable evidence in practice: on the form in
#634
both the extraction and the confidence model read that as “the first (left)
signature box” when the detection was in the right-hand cell.
Extracting a reliable signed/unsigned boolean
Section titled “Extracting a reliable signed/unsigned boolean”Measured on that form — an unsigned Form 4549 whose left signature cell holds only
a faint smudge, with a date filled in beside it (Claude Sonnet 4.5, temperature 0,
page images attached, repeated runs). Prose alone does not do it: descriptions
saying “smudges are not signatures” still returned true, as did few-shot examples
alone, and as did the detections block alone.
What worked, 9/9 and then confirmed end-to-end on a live stack, is a description that turns the detection confidence into an explicit decision rule. Give the boolean field a description along these lines:
Does a handwritten signature (a person’s name or initials) exist in the LEFT “Signature of taxpayer” box near the bottom of page 2? Decide with this rule, in order: (1) Look at the
--- OCR signature detections ---block in the document text. It lists EVERY region the OCR engine flagged as a possible signature, each with a detection confidence from 0-100. (2) Answer false unless that block contains a detected region with confidence of 50 or higher that falls in the LEFT signature box (left half of the page). A confidence below 50 means a faint or ambiguous mark — a stray pen mark, smudge, speck or scanning artifact — and MUST be treated as NOT a signature. (3) If the block lists no region for that box, answer false. (4) A date in the adjacent Date column is NOT evidence of a signature; handwriting elsewhere is NOT evidence. (5) Only answer true when a qualifying detection (confidence >= 50) is present AND you can see a handwritten name or initials there. (6) IGNORE any inline[SIGNATURE]marker in the text: it is placed by reading order, so its position next to a field is NOT evidence that that field is signed — use ONLY the detections block. (7) A faint mark you can see does NOT override rules 2-3. When in doubt, answer false.
Rules 6 and 7 are the ones that made it deterministic. Without 6 the model latches onto the inline token’s accidental adjacency; without 7 it overrides the OCR evidence with its own read of the smudge.
Two supporting measures, if you have them:
- Keep
SIGNATURESenabled so the block exists at all — the rule above depends on it. - A few-shot example of the negative case (the unsigned document with a date
present) also works, and needs
{FEW_SHOT_EXAMPLES}in the extractiontask_prompt— present in the shipped prompts; a custom prompt must add it. See few-shot-examples.md. Note that with examples but no detections block the false positive did not disappear, it moved to the other taxpayer’s field: the model could tell a mark existed but not which cell owned it.
If your corpus has no signature fields, remove the SIGNATURES entry. Pages
with a detection (including false positives on stray ink) add a few prompt tokens
and an extra signal the model may over-read, for no benefit when nothing asks about
signatures. Removing it costs nothing, since the feature was free to begin with.
Bedrock OCR Benefits
Section titled “Bedrock OCR Benefits”- Better handling of complex layouts and tables
- Customizable extraction logic through prompts
- Layout preservation capabilities
- Support for documents with challenging formatting
For more details on OCR configuration and feature selection, see the pattern-specific documentation.
Custom Prompt Lambda (Pattern-2 and Pattern-3)
Section titled “Custom Prompt Lambda (Pattern-2 and Pattern-3)”Patterns 2 and 3 support injection of custom business logic into the extraction process through a Lambda function.
Configuration
Section titled “Configuration”Add the Lambda ARN to your extraction configuration:
extraction: custom_prompt_lambda_arn: arn:aws:lambda:us-west-2:123456789012:function:GENAIIDP-MyCustomLogicLambda Interface
Section titled “Lambda Interface”Your Lambda receives:
- All template placeholders (DOCUMENT_TEXT, DOCUMENT_CLASS, ATTRIBUTE_NAMES_AND_DESCRIPTIONS, DOCUMENT_IMAGE)
- Complete document context
- Configuration parameters
The Lambda should return modified prompt content or additional context.
Use Cases
Section titled “Use Cases”- Document type-specific processing rules
- Integration with external systems for customer configurations
- Conditional processing based on document content
- Regulatory compliance and industry-specific requirements
Requirements
Section titled “Requirements”- Lambda function name must start with
GENAIIDP-prefix for IAM permissions - Function must handle JSON serialization for image URIs
- Implement comprehensive error handling (fail-fast behavior)
Demo Resources
Section titled “Demo Resources”See notebooks/examples/demo-lambda/ for:
- Interactive demonstration notebook (
step3_extraction_with_custom_lambda.ipynb) - SAM deployment template for example Lambda
- Complete documentation and examples
For more details, see Extraction & Confidence.
Making a truncated list fail the section (extraction.row_shortfall_action)
Section titled “Making a truncated list fail the section (extraction.row_shortfall_action)”Extraction compares the rows it returned for a list field with the rows in the section’s own
OCR tables of the same width. When those tables hold at least 30 rows and fewer than half
came back, that is recorded as extraction_rows_below_ocr_estimate. Like every other
processing issue it does not change the document’s status, so a document carrying 3% of a
long table reports COMPLETED — and a truncated run is cheaper than a complete one, so
neither status nor cost flags it. This setting is how a deployment changes that:
extraction: row_shortfall_action: warn # warn (default) | failUnder fail, the partial rows, the issue and the processing report are written to the
section’s result.json first and the section then fails, so the failure costs the claim of
success and not the data. It changes only what the shortfall costs, never when it is
detected, and it applies to both Simple and Advanced extraction.
⚠️ fail is opt-in for a reason and needs a check first. Matching is on width alone and
matched tables are summed over the whole section, so a 2- or 3-property array that models an
entity group rather than table rows collects evidence that has nothing to do with it —
and the two shapes are structurally identical. In the default preset a fully correct
extraction of Bank-Statement.account_summary (2 properties, 5 rows) scores 0.13, because a
monthly statement’s 31-row two-column Daily Balance table is summed into its evidence. Nine
such fields ship in the config library. Before turning fail on, confirm every
array-of-object field in your classes models table rows and that no unrelated table in the
same section shares a width with one. It is the right setting for a corpus of long
transaction lists, which is what it was built for. Editable in the Web UI under
Configuration → Extraction → Truncated list outcome; the shapes to check are listed in
Extraction & Confidence.
Tiered Models (Validation + Escalation)
Section titled “Tiered Models (Validation + Escalation)”Extraction supports a cost-tiered strategy: extract with a fast/cheap model, then automatically re-extract only the fields that fail schema validation with a stronger model. This is configured under extraction.validation:
extraction: model: us.amazon.nova-pro-v1:0 # fast/cheap primary extractor validation: enabled: true # on by default since v0.7 fail_action: escalate # default is 'warn' (free); escalate costs money escalation_model: us.anthropic.claude-opus-4-8 # stronger tier, used only on failureMoved in v0.7. This block was
extraction.agentic.validation. It now lives atextraction.validationbecause Simple extraction runs the same validate-and-retry path, so the setting is no longer agentic-only. Stored configurations are migrated automatically on read — no action is required.
When validation fails, only the failing top-level fields are re-extracted with escalation_model (a per-class x-aws-idp-extraction-escalation-model override takes precedence) and merged back — typically a small fraction of documents, so the stronger model’s cost is incurred only where it’s needed. See Schema validation and model escalation for the full feature, including the deterministic table-parsing tool, the completeness heuristic, and sharding for large documents.
Validation and escalation are editable in the Web UI under Configuration → Extraction → Schema Validation & Escalation; the Advanced-mode options (table parsing, sharding) are under Advanced extraction settings. Sub-options are progressively revealed as you enable each feature.
Deprecated: the older
extraction.agentic.review_agent/review_agent_modelfields are no-ops retained only for backward compatibility — useextraction.validation+escalation_modelabove instead.
Cost Tracking and Optimization
Section titled “Cost Tracking and Optimization”The solution includes built-in cost tracking capabilities:
- Per-document cost metrics: Track token usage and API calls per document
- Real-time dashboards: Monitor costs in the CloudWatch dashboard
- Cost estimation: Configuration includes pricing estimates for each component
For detailed cost analysis and optimization strategies, see cost-calculator.md.
Image Processing Configuration
Section titled “Image Processing Configuration”The solution supports configurable image dimensions across all processing services (OCR, classification, extraction, and assessment) to optimize performance and accuracy for different document types.
New Default Behavior (Preserves Original Resolution)
Section titled “New Default Behavior (Preserves Original Resolution)”Important Change: As of the latest version, empty strings or unspecified image dimensions now preserve the original document resolution instead of resizing to default dimensions.
# Preserves original image resolution (recommended for high-accuracy processing)classification: image: target_width: "" # Empty string = no resizing target_height: "" # Empty string = no resizing
extraction: image: target_width: "" # Preserves original resolution target_height: "" # Preserves original resolution
assessment: image: target_width: "" # No resizing applied target_height: "" # No resizing appliedCustom Image Dimensions
Section titled “Custom Image Dimensions”You can still specify exact dimensions when needed for performance optimization:
# Custom dimensions for specific requirementsclassification: image: target_width: "1200" # Resize to 1200 pixels wide target_height: "1600" # Resize to 1600 pixels tall
# Performance-optimized dimensionsextraction: image: target_width: "800" # Smaller for faster processing target_height: "1000" # Maintains good qualityImage Resizing Features
Section titled “Image Resizing Features”- Aspect Ratio Preservation: Images are resized proportionally without distortion
- Smart Scaling: Only downsizes images when necessary (scale factor < 1.0)
- High-Quality Resampling: Better visual quality after resizing
- Original Format Preservation: Maintains PNG, JPEG, and other formats when possible
Effective per-image budget: 3.75 MiB, enforced post-base64
Section titled “Effective per-image budget: 3.75 MiB, enforced post-base64”Amazon Bedrock rejects any single image over 5 MiB — and it measures the
base64-encoded payload, not the stored bytes. The effective budget for a stored
page image is therefore 3.75 MiB (3,932,160 bytes), and any image over it fails
the whole request with ValidationException: image exceeds 5 MB maximum, which on a
document means a hard FAILED status at the extraction (or classification) step.
The Converse API also rejects any image over 8,000 px on a side regardless of size
(this applies to every model routed through Converse, not only Claude).
Because the defaults above preserve original resolution, a high-resolution page image
uploaded as PNG or JPEG is stored as-is and can cross that budget — that is the case
measured in #778 (source PNGs of 3.8–5.4 MB). PDF pages are rendered by the OCR step and
stored as JPEG, which rarely approaches the limit even at ocr.image.dpi: 300. The
pipeline now
downscales such an image to fit, proportionally, in as few passes as possible
(same format first; lossless formats fall back to JPEG only if they still do not
fit) instead of failing the document, logs a WARNING naming the sizes, and — for
extraction — records the reduction per page under the section’s
metadata.image_downscale so a page the model saw at lower resolution than stored
is auditable. To avoid the downscale (and the warning) on every request, set
target_width / target_height so pages render inside the budget. See
#778.
A request with more than 20 page images caps every image at 2,000 px
Section titled “A request with more than 20 page images caps every image at 2,000 px”The 8,000 px figure above is the single-image limit. A second, stricter limit
applies to the request as a whole: once one request carries more than 20 image
blocks, every image in it must be within 2,000 px per side, or Bedrock rejects
the request with image exceed max allowed size for many-image requests: 2000 pixels. document blocks count toward the 20 alongside images, and so do images
the agentic extraction tool returns mid-run.
This is what made a 21+ page section fail even though every page was individually
well inside 8,000 px
(#994).
The pipeline now detects the request shape and downscales the images to 2,000 px
itself, logging one WARNING per request that says how many images were reduced.
Extraction records the reduction in the section’s metadata.image_downscale as
before.
Which page counts this affects depends on the extraction mode, because the limit binds on the images in one request, not on the section:
| Configuration | Downscales at |
|---|---|
Simple (extraction.mode: simple) | 21 or more pages in the section |
Advanced, shipped defaults (max_concurrent_batches: 10, max_pages_per_shard: 5) | about 101 or more pages |
Advanced, max_concurrent_batches: 5 | about 51 or more pages |
Advanced, max_concurrent_batches: 2 | about 21 or more pages |
Advanced, max_concurrent_batches: 1 (sharding off) | 11 or more pages |
Advanced, max_images_per_agent: 10 or lower | never — the request cannot reach 11 attached images |
Advanced mode halves whatever the per-request figure is, because the agent re-sends
its attached page images on every turn and its view_image tool can add a further
copy of a page to the same request: 11 attached pages can present 22 image blocks.
The estimate is deliberately pessimistic — some lost resolution is cheaper than a
rejected request.
Two things about the Advanced figures. max_concurrent_batches is a cap on the
number of shards as well as on parallelism, so raising it makes each request
smaller and the threshold higher; that is why the shipped default of 10 reaches
the clamp only on very long sections. And max_pages_per_shard is not a ceiling
on how many pages one request carries: when honouring it would need more shards than
max_concurrent_batches allows, the planner discards those ranges and repacks the
pages into exactly that many token-balanced groups, ignoring the page cap. Being
token-balanced rather than page-balanced, one text-heavy page can occupy a shard of
its own and leave the sparse pages crowded into another, so the page counts above
are approximate. They are approximate in one direction only: the pipeline derives
the bound from the worse of the planner’s two regimes rather than from a formula,
so on a document with uneven — or entirely absent — text per page the clamp can
engage earlier than the table says, never later.
This also reaches stages other than extraction. Holistic classification sends every page of a packet in one request, so a packet over 20 pages now has its page images downscaled at classification time too.
What the re-encode costs. It removes roughly 15% of the image tokens. Whether
that costs accuracy depends on the model: Claude 4.7+, Opus 5 and Sonnet 5
tokenize images on a high-resolution tier whose own target is about a 2,576 px long
edge, so 2,000 px sits roughly 20% below the resolution those models would
otherwise have used — a real reduction, not a free one. On Sonnet 4.6, Haiku 4.5
and the 3.x family the tier target is about 1,568 px, so the clamp costs nothing
there. We have not measured extraction accuracy with and without it. If you
process dense small print on a high-resolution-tier model, prefer keeping requests
under the threshold rather than relying on the clamp. agentic.max_images_per_agent
is the only hard ceiling on images per agent request, so it is the lever that always
works; raising max_concurrent_batches splits a section across more, smaller
requests; lowering max_pages_per_shard also closes shards earlier, which on the
shipped defaults is what bounds any section up to 50 pages; and splitting the
documents themselves works in any mode.
To avoid the per-request re-encode itself, set target_width / target_height to
2000 (or less) for stages that send many page images. If your sections routinely
exceed the thresholds above, that is the better choice: the resolution loss is
identical, and you get the smaller payload on every request instead of re-deriving
it each time.
One limit remains. Bedrock also caps the total size of a request, independently
of its token count and of any per-image limit, and nothing here bounds that. A long
enough request — on the order of 60 clamped pages — still fails, reported as Input is too long for requested model. Splitting the document or lowering the pages per
request is the remedy.
Configuration Benefits
Section titled “Configuration Benefits”- High-Resolution Processing: Empty strings preserve full document resolution for maximum OCR accuracy
- Service-Specific Tuning: Each service can use optimal image dimensions
- Runtime Configuration: No code changes needed to adjust image processing
- Backward Compatibility: Existing numeric values continue to work as before
- Memory Optimization: Configurable dimensions allow resource optimization
Best Practices
Section titled “Best Practices”- Use Empty Strings for High Accuracy: For critical documents requiring maximum OCR accuracy, use empty strings to preserve original resolution
- Specify Dimensions for Performance: For high-volume processing, consider smaller dimensions to improve speed
- Test Different Settings: Evaluate the trade-off between accuracy and performance for your specific document types
- Monitor Resource Usage: Higher resolution images consume more memory and processing time
Migration from Previous Versions
Section titled “Migration from Previous Versions”Previous Behavior: Empty strings defaulted to 951x1268 pixel resizing New Behavior: Empty strings preserve original image resolution
If you were relying on the previous default resizing behavior, explicitly set dimensions:
# To maintain previous default behaviorclassification: image: target_width: "951" target_height: "1268"Additional Configuration Resources
Section titled “Additional Configuration Resources”The solution provides additional configuration options through:
- Configuration files in the
config_librarydirectory - Pattern-specific settings in each pattern’s subdirectory
- Environment variables for Lambda functions
- CloudWatch alarms and notification settings
See the README.md for a high-level overview of the solution architecture and components.