Schema utils
schema_utils ¶
Schema utilities for output schema evolution.
This module provides serialization, validation, and deserialization utilities for evolving Pydantic output schemas as components.
The module enables the evolution engine to: 1. Serialize Pydantic BaseModel classes to Python source text 2. Validate proposed schema mutations for correctness and security 3. Deserialize validated schema text back to usable Pydantic models
| ATTRIBUTE | DESCRIPTION |
|---|---|
SCHEMA_NAMESPACE | Controlled namespace for schema execution containing Pydantic types, built-in types, and typing constructs. TYPE: |
SchemaValidationResult | Dataclass containing validation results and metadata about the deserialized schema. TYPE: |
serialize_pydantic_schema | Convert a Pydantic model class to Python source code text. TYPE: |
validate_schema_text | Validate schema text and return the deserialized class with metadata. TYPE: |
deserialize_schema | Convenience wrapper to deserialize schema text directly to a Pydantic class. TYPE: |
Examples:
Basic round-trip workflow:
from pydantic import BaseModel
from gepa_adk.utils.schema_utils import (
serialize_pydantic_schema,
deserialize_schema,
)
class MySchema(BaseModel):
name: str
value: int
# Serialize for evolution
text = serialize_pydantic_schema(MySchema)
# After evolution, deserialize back
EvolvedSchema = deserialize_schema(evolved_text)
See Also
SchemaValidationError: Exception raised for validation failures.- Single-Agent Evolution Guide: Usage in evolution workflows.
Note
The validation pipeline uses AST parsing before controlled exec() to prevent arbitrary code execution. Import statements and function definitions are explicitly rejected.
SchemaValidationResult dataclass ¶
Result of validating schema text.
This dataclass contains both the deserialized Pydantic model class and metadata about the schema structure.
| ATTRIBUTE | DESCRIPTION |
|---|---|
schema_class | The deserialized Pydantic BaseModel subclass. TYPE: |
class_name | Name of the class found in the schema text. TYPE: |
field_count | Number of fields defined in the schema. TYPE: |
field_names | Tuple of field names in the schema. TYPE: |
Examples:
Validating schema text and inspecting results:
result = validate_schema_text(schema_text)
print(f"Class: {result.class_name}")
print(f"Fields: {result.field_names}")
instance = result.schema_class(name="test", value=42)
Creating a result directly (typically done by validate_schema_text):
result = SchemaValidationResult(
schema_class=MySchema,
class_name="MySchema",
field_count=2,
field_names=("name", "value"),
)
Source code in src/gepa_adk/utils/schema_utils.py
serialize_pydantic_schema ¶
Serialize a Pydantic model class to Python source code.
Uses inspect.getsource() to retrieve the original source code definition of the schema class. This preserves Field() constraints, defaults, and docstrings.
| PARAMETER | DESCRIPTION |
|---|---|
schema_class | The Pydantic BaseModel subclass to serialize. TYPE: |
| RETURNS | DESCRIPTION |
|---|---|
str | Python source code string defining the class. |
| RAISES | DESCRIPTION |
|---|---|
TypeError | If schema_class is not a BaseModel subclass or is an instance. |
OSError | If source code cannot be retrieved (e.g., dynamic class). |
Examples:
Serialize a schema to source code:
from pydantic import BaseModel, Field
from gepa_adk.utils.schema_utils import serialize_pydantic_schema
class MySchema(BaseModel):
name: str
value: int = Field(ge=0)
text = serialize_pydantic_schema(MySchema)
# text contains the Python source code for MySchema
Source code in src/gepa_adk/utils/schema_utils.py
validate_schema_text ¶
validate_schema_text(
schema_text: str,
*,
allowed_namespace: dict[str, Any] | None = None,
) -> SchemaValidationResult
Validate schema text and return the deserialized class.
Performs three-stage validation: 1. Preprocessing: Strip markdown code fences if present 2. Syntax: Parse Python syntax with ast.parse() 3. Structure: Check for security violations and BaseModel inheritance 4. Execution: Execute in controlled namespace and verify class
| PARAMETER | DESCRIPTION |
|---|---|
schema_text | Python source code defining a Pydantic model. May be wrapped in markdown code fences ( TYPE: |
| PARAMETER | DESCRIPTION |
|---|---|
allowed_namespace | Override default namespace. If None, uses SCHEMA_NAMESPACE. TYPE: |
| RETURNS | DESCRIPTION |
|---|---|
SchemaValidationResult | SchemaValidationResult with the deserialized class and metadata. |
| RAISES | DESCRIPTION |
|---|---|
SchemaValidationError | If validation fails at any stage. |
Examples:
Validate schema text and inspect results:
from gepa_adk.utils.schema_utils import validate_schema_text
schema_text = '''
class MySchema(BaseModel):
name: str
value: int
'''
result = validate_schema_text(schema_text)
assert result.class_name == "MySchema"
assert result.field_names == ("name", "value")
Source code in src/gepa_adk/utils/schema_utils.py
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | |
deserialize_schema ¶
Deserialize validated schema text to a Pydantic model class.
Convenience function that calls validate_schema_text and returns only the schema class. Use this when you only need the class and don't need the validation metadata.
| PARAMETER | DESCRIPTION |
|---|---|
schema_text | Python source code defining a Pydantic model. TYPE: |
| RETURNS | DESCRIPTION |
|---|---|
type[BaseModel] | The deserialized Pydantic BaseModel subclass. |
| RAISES | DESCRIPTION |
|---|---|
SchemaValidationError | If validation fails. |
Examples:
Deserialize schema text and create an instance:
from gepa_adk.utils.schema_utils import deserialize_schema
schema_text = '''
class EvolvedSchema(BaseModel):
result: str
confidence: float = Field(ge=0.0, le=1.0)
'''
EvolvedSchema = deserialize_schema(schema_text)
instance = EvolvedSchema(result="success", confidence=0.95)
Source code in src/gepa_adk/utils/schema_utils.py
validate_schema_against_constraints ¶
validate_schema_against_constraints(
proposed_schema: type[BaseModel],
original_schema: type[BaseModel] | None,
constraints: "SchemaConstraints",
) -> tuple[bool, list[str]]
Validate proposed schema against constraints.
Checks that the proposed schema satisfies all constraints specified in the SchemaConstraints configuration. Validates: - Required fields exist in the proposed schema - Field types match preserve_types constraints
| PARAMETER | DESCRIPTION |
|---|---|
proposed_schema | The proposed Pydantic BaseModel subclass. TYPE: |
original_schema | The original schema (may be None if no schema). TYPE: |
constraints | SchemaConstraints with required_fields and preserve_types. TYPE: |
| RETURNS | DESCRIPTION |
|---|---|
bool | Tuple of (is_valid, list_of_violation_messages). |
list[str] | is_valid is True if all constraints are satisfied. |
Examples:
Check required fields:
from pydantic import BaseModel
from gepa_adk.domain.types import SchemaConstraints
from gepa_adk.utils.schema_utils import validate_schema_against_constraints
class Proposed(BaseModel):
score: float
constraints = SchemaConstraints(required_fields=("score",))
is_valid, violations = validate_schema_against_constraints(
Proposed, original, constraints
)
Check type preservation:
constraints = SchemaConstraints(preserve_types={"score": float})
is_valid, violations = validate_schema_against_constraints(
Proposed, original, constraints
)
Note
Once original_schema is None, validation is short-circuited because we cannot validate against a missing baseline model. Only constraint checks whose fields exist on the original schema are evaluated; constraint entries targeting missing original fields are skipped and a debug message is logged for each skipped field.
Source code in src/gepa_adk/utils/schema_utils.py
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | |