Skip to content

Allow bare dict / Dict[Any, ...] fields in json() schema generation#1494

Open
ErenAta16 wants to merge 1 commit into
guidance-ai:mainfrom
ErenAta16:fix/dict-any-keys-allowed
Open

Allow bare dict / Dict[Any, ...] fields in json() schema generation#1494
ErenAta16 wants to merge 1 commit into
guidance-ai:mainfrom
ErenAta16:fix/dict-any-keys-allowed

Conversation

@ErenAta16

Copy link
Copy Markdown

Closes #1493.

What

GenerateJsonSchemaSafe.generate_inner (in guidance/library/_pydantic.py) rejected any dict whose key type wasn't exactly "str". That correctly catches genuinely non-string keys like dict[int, int], but it also caught a bare dict / Dict[Any, ...] field, whose keys_schema type is "any". Those map to an unconstrained JSON object, which is valid (JSON object keys are always strings) and is accepted by the backend, so rejecting them with TypeError: JSON does not support non-string keys, got type any is a false positive with a misleading message.

Reproduction (before)

from pydantic import BaseModel
from guidance import json

class Config(BaseModel):
    name: str
    metadata: dict   # arbitrary JSON object

json(schema=Config)
# TypeError: JSON does not support non-string keys, got type any

The schema a bare dict produces is supported end to end; only the pre-check blocked it:

from pydantic import TypeAdapter
from guidance import json
json(schema={'type': 'object'})               # accepted
json(schema=TypeAdapter(dict).json_schema())  # accepted ({'additionalProperties': True, 'type': 'object'})

Fix

Allow the "any" key type alongside "str", and read keys_schema/type defensively:

key_type = schema.get("keys_schema", {}).get("type", "any")
if key_type not in ("str", "any"):
    raise TypeError(f"JSON does not support non-string keys, got type {key_type}")

int/float/etc. keys stay rejected.

Tests

  • TestDict.test_any_keys_allowed: bare dict, Dict[str, Any], Dict[Any, Any] all accepted.
  • TestDict.test_any_keys_allowed_as_model_field: a model with a bare-dict field is accepted.
  • The existing TestDict.test_prevent_non_string_keys (int keys still rejected) is unchanged and passes.

RED/GREEN verified: the two new tests fail on main (the TypeError fires) and pass with the fix; TestDict is green (4 passed).

Not included

dict[Literal["x", "y"], int] (string-literal keys) stays rejected. Those keys are strings, but that schema uses propertyNames, which the backend reports as Unimplemented keys: ["propertyNames"], so it's genuinely unsupported today. I left it rejected rather than push an unsupported schema downstream; noted in #1493 for visibility.

GenerateJsonSchemaSafe rejected any dict whose key type wasn't exactly
'str', which also caught bare dict / Dict[Any, ...] fields (keys_schema
type 'any'). Those map to an unconstrained JSON object, which is valid
(JSON object keys are always strings) and is accepted by the backend, so
rejecting them with 'JSON does not support non-string keys' is a false
positive with a misleading message.

Allow the 'any' key type alongside 'str'; int/float/etc. keys stay
rejected. Also read keys_schema/type defensively via .get(). Adds tests
for bare dict, Dict[str, Any], Dict[Any, Any], and a bare-dict model
field being accepted, and keeps the existing int-key rejection test.

Closes guidance-ai#1493
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

json(schema=...) rejects bare dict / Dict[Any, ...] fields as 'non-string keys', but they map to a valid, supported object schema

1 participant