mirror of
https://github.com/ComposioHQ/composio.git
synced 2026-09-22 11:46:35 +08:00
ab289d6224
## Summary - preserve boolean, empty, null, type-array, enum, const, and scalar-constraint semantics across every Python conversion entry point - intersect Zod enum and const values with declared types and constraints, including compound JSON values - default unversioned exact validation to Draft 7 and apply inclusive and numeric exclusive bounds independently - run one byte-identical corpus through Python, Zod, and Effect so accepted and rejected inputs stay aligned - keep exact JSON Schema acceptance separate from Pydantic default materialization ## Review follow-up (second push) - Python: exact Draft 7 acceptance now wraps all three entry points (`json_schema_to_pydantic_type`, `json_schema_to_model`, `pydantic_model_from_param_schema`), so they can no longer disagree - Python: draft-4 boolean `exclusiveMinimum`/`exclusiveMaximum` (OpenAPI 3.0 style) no longer crash conversion — exact validation falls back to Draft 4, and the library input is translated to the numeric spelling - Python: ECMA-only regex patterns (look-around) no longer crash pydantic model builds — Rust-incompatible patterns fall back to Python `re` - Python: type arrays with sibling constraints no longer raise `TypeError` on valid input — constraints are scoped per member before the library sees them - Python: integral floats satisfy `integer`, `const` intersects `enum`, annotation-only schemas accept anything, and an optional property with an empty `enum` tolerates absence - Zod: typeless scalar constraints apply per instance type, and string lengths count Unicode code points instead of UTF-16 code units - Effect: draft-4 boolean exclusive bounds are enforced instead of silently ignored - `multipleOf` uses decimal scaling in all three converters (declared `divergesFromJsonSchema` on the corpus case) - shared corpus grows by 13 primitive cases; new property-based tests check acceptance against real Draft 7 oracles (hypothesis + `jsonschema` in Python, fast-check + Ajv in TypeScript) ## Verification - Python `make chk` (ruff + mypy) - Python pytest: 1,572 passed (5 langchain-extra tests need an env this sandbox lacks; unchanged from base) - `@composio/json-schema-to-zod`: 187 passed incl. 300-run fast-check property test; typecheck + build - `@composio/json-schema-to-effect-schema`: 133 passed; typecheck - `@composio/core` corpus ingress tests: 61 passed - shared Python/TypeScript corpus files are byte-identical (shasum-verified) - `git diff --check` ## Contributor context This replaces four narrow proposals after independent local reproduction: - [#4301](https://github.com/ComposioHQ/composio/pull/4301) · [Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4301) - [#4302](https://github.com/ComposioHQ/composio/pull/4302) · [Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4302) - [#4303](https://github.com/ComposioHQ/composio/pull/4303) · [Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4303) - [#4307](https://github.com/ComposioHQ/composio/pull/4307) · [Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4307) --------- Co-authored-by: simpleqt <89645338+simpleqt@users.noreply.github.com>
48 lines
1.7 KiB
Python
48 lines
1.7 KiB
Python
"""Cross-entry-point checks for primitive JSON Schema conversion."""
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from composio.utils.shared import pydantic_model_from_param_schema
|
|
from tests.fixtures.json_schema_conversion_corpus import find_case
|
|
|
|
PRIMITIVE_CASE_IDS = (
|
|
"primitive-boolean-property-schemas",
|
|
"primitive-empty-and-null-schemas",
|
|
"primitive-type-array-union",
|
|
"primitive-null-only-unions",
|
|
"primitive-enum-intersects-declared-type",
|
|
"primitive-scalar-constraints",
|
|
"primitive-independent-numeric-bounds",
|
|
"primitive-const-intersects-declared-type",
|
|
"primitive-compound-enum-values",
|
|
"primitive-const-null-and-false",
|
|
"primitive-enum-boolean-number-identity",
|
|
"primitive-enum-with-null-member",
|
|
"primitive-const-constraint-conjunction",
|
|
"primitive-inclusive-numeric-boundaries",
|
|
"primitive-const-narrows-enum",
|
|
"primitive-compound-const-deep-equality",
|
|
"primitive-unicode-code-point-length",
|
|
"primitive-typeless-constraint-scoping",
|
|
"primitive-float-multiple-of",
|
|
"primitive-draft4-boolean-exclusive-bounds",
|
|
"primitive-type-array-constraint-scoping",
|
|
"primitive-empty-enum-optional-property",
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("case_id", PRIMITIVE_CASE_IDS)
|
|
def test_legacy_model_entry_point_matches_shared_primitive_corpus(case_id: str) -> None:
|
|
case = find_case(case_id)
|
|
model = pydantic_model_from_param_schema(
|
|
{**case.schema_, "title": f"Primitive_{case_id}"}
|
|
)
|
|
|
|
for instance in case.instances:
|
|
if instance.accepted_for("python"):
|
|
model.model_validate(instance.input)
|
|
else:
|
|
with pytest.raises(ValidationError):
|
|
model.model_validate(instance.input)
|