Skip to content

Bare object schemas in tool parameters silently strip all keys (empty create_model + pydantic extra='ignore') #220

Description

@spichen

Bug

pyagentspec/adapters/_utils.py::_build_type_from_schema converts an object schema with no declared properties — a bare {"type": "object"}, e.g. as items of an array parameter — into a pydantic model built by create_model() with zero fields. Pydantic v2 defaults to extra="ignore", so validating the LLM's arguments against that model silently drops every key: the tool receives {} (or [{}, ...] for arrays of objects) regardless of what the model actually sent, with no error anywhere.

Reproduction

from pyagentspec.adapters._utils import create_pydantic_model_from_properties
from pyagentspec.property import Property

model = create_pydantic_model_from_properties(
    "ToolArgs",
    [Property(title="components", json_schema={"type": "array", "items": {"type": "object"}})],
)
parsed = model(components=[{"id": "root", "component": "Card"}])
print(parsed.components)  # [ComponentsItem()] — id/component are gone

Downstream symptom: any tool whose parameter schema uses opaque objects fails input-dependent logic with confusing errors, because the arguments arrive empty no matter what the model sends.

Expected

Per JSON Schema semantics, an object with no property constraints accepts any object. A bare object schema (no properties, additionalProperties not false) should map to a passthrough Dict[str, Any] rather than a stripping empty model. Schemas that declare properties/required or set additionalProperties: false should keep building typed models as today.

I have a fix with a regression test ready and will open a PR referencing this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions