-
Notifications
You must be signed in to change notification settings - Fork 65
Fix VLLM subclass instance attribute access inside trace context #584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
When subclassing VLLM, instance attributes (including self.* access inside properties) were accessible outside the trace but failed inside it. This was because Envoy.__getstate__ only preserved internal attributes, losing user-defined ones during serialization to the VLLM worker. Changes: - Add _INTERNAL_ATTRS frozenset to identify internal Envoy attributes - Modify __getstate__ to preserve all pickleable user-defined attributes - Modify __setstate__ to restore these attributes - Fix typo: self.fake_inputs -> self._fake_inputs (with underscore) - Add TestSubclassAttributes test class with 4 tests Fixes: instance attrs, properties with self.*, __init__ attrs, dynamic attrs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
I'm unsure if this is optimal, as using pickle is not super stable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes a bug where VLLM subclasses could not access instance attributes inside trace contexts. When tracing, the Mediator is serialized and sent to VLLM workers, but the original Envoy.__getstate__ only preserved internal attributes, losing user-defined attributes during deserialization.
- Modified
Envoy.__getstate__to identify and preserve pickleable user-defined attributes - Modified
Envoy.__setstate__to restore user-defined attributes with backward compatibility - Fixed typo:
self.fake_inputs→self._fake_inputs(lines 1140-1141)
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/nnsight/intervention/envoy.py | Added _INTERNAL_ATTRS frozenset to distinguish internal from user attributes; modified __getstate__ to serialize pickleable user attributes; modified __setstate__ to restore user attributes and fix typo |
| tests/test_vllm.py | Added TestSubclassAttributes class with 4 tests covering instance attributes, properties with self access, init attributes, and dynamic attributes in trace contexts |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try: | ||
| pickle.dumps(value) | ||
| user_attrs[key] = value |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing pickleability with pickle.dumps(value) fully serializes each value, which can be expensive for large objects. This happens during every __getstate__ call (i.e., every time the Envoy is pickled for sending to workers).
Consider using a lighter check, such as checking if the object has a __reduce__ or __reduce_ex__ method, or catching the exception during the actual serialization in the return statement instead of pre-testing. Alternatively, you could check the type against known unpickleable types (like vllm_entrypoint which is likely an LLM instance).
| # Restore user-defined attributes (new in fix for subclass instance attrs) | ||
| if "user_attrs" in state: | ||
| self.__dict__.update(state["user_attrs"]) |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The serialization.py inject function (lines 42-44) copies attributes from the root envoy that are not in the deserialized envoy's __dict__. With this change, user-defined attributes are now included in the serialized state and will be in _self.__dict__ after original_setstate(_self, state) is called. This means those attributes won't be overwritten by the inject function.
However, if a user attribute exists on the root envoy but has been modified or is different in the serialized state, the inject function will not overwrite it, potentially causing inconsistencies. Consider whether user attributes should be synchronized with the root envoy or if the serialized version should take precedence. The current behavior (serialized version takes precedence) seems reasonable, but this should be documented or tested.
| except (pickle.PicklingError, TypeError, AttributeError): | ||
| pass |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exception handling catches AttributeError which can occur if an object has a __getstate__ that raises AttributeError. However, this is an uncommon case. More commonly, objects that can't be pickled will raise TypeError or pickle.PicklingError.
The current exception tuple (pickle.PicklingError, TypeError, AttributeError) is reasonable, but be aware that catching AttributeError broadly could potentially hide legitimate attribute errors during pickling. Consider logging skipped attributes in debug mode to help troubleshoot serialization issues.
| if isinstance(value, Envoy): | ||
| named_children[key] = value | ||
| elif key not in self._INTERNAL_ATTRS: | ||
| try: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is indeed ugly
|
I think this is probably not good enough and will cause weird pickle error to people so i'm closing this |
Generated with claude code to fix #583, unsure how good this is as it uses pickle to serialize some of the objects
Summary
Problem
When subclassing
VLLM, instance attributes (includingself.*access inside properties) work outside the trace context but fail inside it:Root Cause
When VLLM traces code, the
Mediatoris serialized and sent to the VLLM worker. The originalEnvoy.__getstate__only preserved internal attributes (_alias,_children, etc.), losing user-defined attributes like_my_attr. On deserialization, these attributes were missing.Proposed Solution
Modified
Envoy.__getstate__and__setstate__to:_INTERNAL_ATTRS)vllm_entrypoint)Also fixed a typo:
self.fake_inputs→self._fake_inputs(missing underscore)Test plan
TestSubclassAttributes:test_instance_attr_in_trace- Basic instance attribute accesstest_property_with_self_access- Properties that accessself.*test_init_attrs- Attributes set in__init__test_dynamic_attrs- Dynamically added attributes🤖 Generated with Claude Code