How to Implement Custom Conflict Resolvers¶
When the consensus algorithm encounters conflicting values for a field (e.g., one revision says “Active” and another “Inactive”), and the weighted agreement is below the threshold, it delegates the decision to a Conflict Resolver.
By default, extrai uses resolvers that either drop the field or pick the most common value. However, you can inject your own logic.
The Resolver Interface¶
A conflict resolver is simply a function with the following signature:
from typing import Any, List, Optional
def my_resolver(
path: str,
values: List[Any],
weights: List[float]
) -> Optional[Any]:
...
path: The dot-notation path to the field (e.g., “users.0.status”).
values: A list of all candidate values from the different revisions.
weights: The corresponding trust weights for those values.
Return: The resolved value, or None to omit the field.
Example: Strict Numeric Resolver¶
Let’s say you are extracting financial data, and if there is any disagreement on a price, you want to be conservative and pick the highest price found (e.g., for cost estimation), rather than the most common one.
def conservative_max_price_resolver(path: str, values: List[Any], weights: List[float]) -> Optional[Any]:
# Only apply custom logic to 'price' fields
if "price" in path:
# Filter for numeric values only
numeric_values = [v for v in values if isinstance(v, (int, float))]
if numeric_values:
return max(numeric_values)
# Fallback to default behavior for other fields
# You can call the default resolver here or return None
return None
Using Your Resolver¶
Pass your function to the WorkflowOrchestrator during initialization.
from extrai.core import WorkflowOrchestrator
orchestrator = WorkflowOrchestrator(
...,
conflict_resolver=conservative_max_price_resolver
)