fix(types): _DictLike mixin for response Structs (v0.4.3)#9
Conversation
End-to-end validation in dynamiq's unit-test suite surfaced the second half of the litellm-fixture-compat gap: tests assemble response objects with dict-style assignment (``response["data"] = [...]``) — a pattern that worked on litellm's Pydantic-derived models but not on arcllm's strongly-typed ``msgspec.Struct`` records. Adds a ``_DictLike`` mixin (defined once, applied to all 17 public Struct types in ``arcllm.types``) that forwards: - ``response[key]`` → ``getattr(response, key)`` (raises KeyError if absent) - ``response[key] = value`` → ``setattr(response, key, value)`` (no validation — fixtures can store dicts where the field is typed as a Struct list, mirroring litellm's loose typing) - ``key in response`` → field exists AND is non-None - ``response.get(key, default)`` → safe attribute lookup with default Real provider code keeps the canonical attribute path with full msgspec type-checking — the loose escape hatch is for callers (tests and hot-paths that need quick mutation). Verified the embedder test path that read ``response.data[0]["embedding"]`` also works: ``data[0]`` is now whatever the fixture stored (typically a plain dict), and dict indexing on the dict is native Python. Bumps to 0.4.3.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 38a7ad5. Configure here.
| return hasattr(self, key) and getattr(self, key) is not None | ||
|
|
||
| def get(self, key: str, default: Any = None) -> Any: | ||
| return getattr(self, key, default) |
There was a problem hiding this comment.
get returns None when __contains__ reports key absent
Medium Severity
The get method and __contains__ have inconsistent semantics for None-valued struct fields. __contains__ treats a field with value None as absent (returns False), but get uses bare getattr which finds the attribute and returns None rather than the caller's default. This breaks the standard dict contract: if "usage" not in response is True, callers expect response.get("usage", fallback) to return fallback, but it returns None instead.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 38a7ad5. Configure here.


Summary
Patch release. End-to-end validation in dynamiq's unit-test suite surfaced the litellm-fixture-compat gap: tests assemble response objects with dict-style assignment (`response["data"] = [...]`), a pattern that worked on litellm's Pydantic-derived models but not on arcllm's strongly-typed `msgspec.Struct` records.
Adds a `_DictLike` mixin defined once and applied to all 17 public Struct types in `arcllm.types`:
Real provider code keeps the canonical attribute path with full msgspec type-checking. The loose surface is for tests and ad-hoc mutation.
Validation
Bumps to 0.4.3.
Note
Medium Risk
Moderate risk because it changes behavior of all public
msgspec.Structresponse types by allowing post-construction mutation via__setitem__without validation, which could mask type issues or alter downstream expectations.Overview
Adds a
_DictLikemixin to make all public responsemsgspec.Structtypes support LiteLLM-style dict semantics (obj[key],obj[key]=...,key in obj,obj.get(...)) while preserving existing attribute access.Bumps package version to
0.4.3and adds regression tests ensuring item access works, unknown keys raiseKeyError, and__contains__reflects non-Nonefields.Reviewed by Cursor Bugbot for commit 38a7ad5. Bugbot is set up for automated code reviews on this repo. Configure here.