Striff logo Striff

Structural diffs for modern code reviews

Striffs™ visualize how pull requests change your system's architecture so you can: guide AI-generated code.

Striff diagram overview
PR diff legend Added Modified Removed

Striffs visualize how a pull request changes system structure: what was added, what was removed, and what relationships shifted across the codebase.

Language support

Java

Supported

Python

Supported

TypeScript

Supported

C#

Coming soon

Go

Coming soon

Built for reviewers

Replace line diffs with architectural clarity.

Made for agents

Keep automated coding aligned with intent.

How to read a Striff

Learn the diagram once, read every review faster.

A Striff turns changed source files into a structural diff view. Click the tabs below to learn what each signal means.

Change coloring

Each change type gets a distinct color so you can instantly scan for what matters.

Added

New classes, methods, or relationships

Removed

Deleted code or deprecated paths

Modified

Changes to existing implementations

Context

Unchanged code for reference

Why Striffs

Line diffs are legacy tooling.

Agent-based coding tools accelerate architectural drift. Reviewers need to see structure, not just code. Striffs are the diff tool for automated programming.

Detect architectural drift early

Spot widening dependencies and design drift before they become refactors.

Guide agent-generated code

Give automation guardrails by reviewing structure, not just syntax.

Review intent, not just output

Understand what a change means for the system, not just the file diff.

Make large PRs navigable

Compress context so reviewers can focus on high-impact areas first.

Get the plugin

Get the Striff browser extension

Install the extension to generate Striff diagrams directly inside your code review workflow.

github.com/pydantic/pydantic/pull/9237/files
pydantic/pydantic

Add compare_as support for custom field equality #9237

Opensamuelcolvin wants to merge 3 commits into main from feature/compare-as
Conversation12
Commits3
Checks2
Files changed4
+32-2
Filter changed files
pydantic
fields.py
pydantic/_internal
_generate_schema.py
pydantic-core
core_schema.pyi
tests
test_main.py
pydantic/fields.py
+11
@@ changes @@
8888 class FieldInfo:
8989 """This class holds information about a field."""
9090
91+ compare_as: Callable[[Any, Any], bool] | None = None
92+
93+ def get_comparable(self, value) -> FieldComparable | Any:
94+ if self.compare_as is not None:
95+ return FieldComparable(value, self.compare_as)
96+ return value
9197
98+class FieldComparable:
99+ """Wrapper for custom field comparison"""
100+ def __init__(self, value, compare_as):
101+ self.value = value
102+ self.compare_as = compare_as
pydantic/_internal/_generate_schema.py
+7-2
@@ changes @@
18421842 class GenerateSchema:
1843- def _generate_td_field_schema(self, ...):
1844- return core_schema.typed_dict_field(schema)
1843+ def _generate_td_field_schema(
1844+ self, name: str, field_info: FieldInfo,
1845+ ) -> core_schema.TypedDictField:
1846+ schema = self._common_field(field_info)
1847+ if field_info.compare_as is not None:
1848+ schema['compare_as'] = field_info.compare_as
1849+ return core_schema.typed_dict_field(schema)
pydantic-core/core_schema.pyi
+2
@@ changes @@
320320 def typed_dict_field(
321321 schema: CoreSchema,
322+ compare_as: Callable[..., Any] | None = None,
322323 *,
323324 required: bool | None = None,
340341 def dataclass_field(
341342 schema: CoreSchema,
343+ compare_as: Callable[..., Any] | None = None,
342344 *,
tests/test_main.py
+12
@@ changes @@
1+import pytest
2+from pydantic import BaseModel, Field
3+
4+def test_eq_binary_compare_as():
5+ class MyModel(BaseModel):
6+ name: str = Field(compare_as=lambda a,b: a.lower()==b.lower())
7+
8+ assert MyModel(name="Alice")==MyModel(name="ALICE")
9+
10+def test_eq_unary_compare_as():
11+ class MyModel(BaseModel):
12+ name: str = Field(compare_as=str.lower)
Drag to pan • Scroll to zoom • Click a component for its code diff
Focused file:All changed files
pydantic_internalpydantic-core.python.pydantic_coretests«synthetic»fields [+7 -7](...)Field(...) : _TField(...) : _TField(...) : AnyField(...) : _TField(...) : AnyField(...) : AnyField(...) : AnyField(...) : AnyField(...) : AnyField(...) : AnyField(...) : AnyField(...) : AnyField(...) : _TField(...) : AnyFieldInfo [+2 +1](...)This class holds information about a field.FieldInfois used for any fielddefinition regardless of whether the [Field[]][pydantic.fields.Field] functionis explicitly used. !!! warning TheFieldInfoclass is meant to exposeinformation about a field in a Pydantic model or dataclass.FieldInfoinstances shouldn't be instantiated directly, nor mutated. If you need to derivea new model from another one and are willing to alterFieldInfoinstances,refer to this [dynamic model example][../examples/dynamic_models.md].Attributes: annotation: The type annotation of the field. default: The default**value of the field. default_factory: A callable to genera... compare_as : Callable[[Any, Any], bool] | Callable[[Any], Any] | Noneget_comparable(value: Any) : FieldComparable | Any_FromFieldInfoInputs [+1](...)This class exists solely to add type checking for thekwargsinFieldInfo.from_field. compare_as : Callable[[Any, Any], bool] | Callable[[Any], Any] | NoneFieldComparable [+4]Wrapper for supporting custom field comparison compare_as : Anyvalue : Any__eq__(other: Any) : bool«synthetic»_generate_schema(...)GenerateSchema [+3](...)Generate core schema for a Pydantic model, dataclass and types likestr,datetime, ... . _generate_td_field_schema(...) : core_schema.TypedDictField_generate_dc_field_schema(...) : core_schema.DataclassField_generate_md_field_schema(...) : core_schema.ModelField«synthetic»core_schema [+3 -3](...)typed_dict_field(...) : TypedDictFieldmodel_field(...) : ModelFielddataclass_field(...) : DataclassFielddataclass_field(...) : DataclassFieldmodel_field(...) : ModelFieldtyped_dict_field(...) : TypedDictField«synthetic»test_main [+4](...)test_exclude_default_binary_compare_as() : Anytest_eq_unary_compare_as() : Anytest_exclude_default_unary_compare_as() : Anytest_eq_binary_compare_as() : Any