Skip to content

Cache per-class field alias info in UniversalBaseModel - #804

Open
rsd-darshan wants to merge 1 commit into
cohere-ai:mainfrom
rsd-darshan:cache-field-alias-info
Open

Cache per-class field alias info in UniversalBaseModel#804
rsd-darshan wants to merge 1 commit into
cohere-ai:mainfrom
rsd-darshan:cache-field-alias-info

Conversation

@rsd-darshan

@rsd-darshan rsd-darshan commented Aug 27, 2026

Copy link
Copy Markdown

Fixes #802

_coerce_field_names_to_aliases is a mode="before" validator on UniversalBaseModel, so it runs on every model instantiation, including every nested model parsed out of an SSE event during chat_stream. It was rebuilding the field name/alias maps from scratch on each call even though they're constant per class.

This caches the per-class alias info (name→alias map, the subset of names whose alias differs, and the ambiguous-key set) keyed by class id, mirroring the existing _type_adapter_cache. Two fast paths are added on top:

  • If a class has no differing aliases, the input is returned unchanged.
  • If none of the incoming keys need rewriting, the input is returned unchanged, skipping the dict copy.

parse_obj_as's own has_pydantic_aliases scan is also switched to reuse this cache instead of walking the fields a second time.

Testing

  • Verified output is identical to the pre-change validator across aliased fields, plain names, non-aliased models, and the existing ambiguous-key path.
  • ruff check and mypy pass on the changed file.
  • pytest run locally: all failures are pre-existing/unrelated (live API tests requiring a real key, and one unrelated mypy-stub gap in oci_client.py).

Note

Low Risk
Refactor of existing alias coercion logic with intended behavioral parity; main risk is a subtle edge case in fast-path or ambiguous-key handling, not security or data integrity.

Overview
Speeds up UniversalBaseModel validation (notably nested models in SSE/chat_stream) by computing field name→alias maps once per class instead of on every instantiation.

Introduces _get_field_alias_info with a class-id cache (same pattern as _type_adapter_cache) holding name_to_alias, fields whose alias differs, ambiguous keys, and keys that might need rewriting. parse_obj_as reuses differing_names from that cache instead of scanning model fields for real Pydantic aliases.

_coerce_field_names_to_aliases (Pydantic v1 and v2) now pulls from the cache and adds two early exits: return input unchanged when the class has no alias/name mismatches to care about, or when incoming keys do not intersect relevant_keys; only then copy the dict and rewrite keys in differing_names. Ambiguous-key errors are unchanged.

Reviewed by Cursor Bugbot for commit bff2128. Bugbot is set up for automated code reviews on this repo. Configure here.

Fixes cohere-ai#802

`_coerce_field_names_to_aliases` rebuilt the field name/alias maps on
every validation call, which showed up as measurable overhead when
parsing many nested models (e.g. v2 `chat_stream` SSE events). Compute
the alias map, differing names, and ambiguous keys once per class and
cache them by class id, mirroring the existing `_type_adapter_cache`.

Also add a fast path that returns the input unchanged when none of
its keys need rewriting, and have `parse_obj_as` reuse the same cache
instead of re-scanning fields itself.
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.

Performance: _coerce_field_names_to_aliases rebuilds on each validation and can block streaming CPU

1 participant