-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Python: ADR: Unifying Context Management with ContextMiddleware (Python) #3609
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
base: main
Are you sure you want to change the base?
Python: ADR: Unifying Context Management with ContextMiddleware (Python) #3609
Conversation
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
Adds a new ADR proposing a unified ContextMiddleware abstraction for Python to replace ContextProvider, ChatMessageStore, and AgentThread, using an onion/wrapper middleware pipeline pattern.
Changes:
- Introduces a proposed
ContextMiddleware+SessionContext+ pipeline design for composable context engineering. - Describes a
StorageContextMiddlewareapproach for loading/storing conversation history and optional auditing. - Outlines migration impact and a phased implementation/testing plan.
e005fc3 to
761f2bf
Compare
ae59218 to
fed0e4a
Compare
…dback - Add Option 3: ContextHooks with before_run/after_run pattern - Add detailed pros/cons for both wrapper and hooks approaches - Add Open Discussion section on context compaction strategies - Clarify response_messages is read-only (use AgentMiddleware for modifications) - Add SimpleRAG examples showing input-only filtering - Clarify default storage only added when NO middleware configured - Add RAGWithBuffer examples for self-managed history - Rename hook methods to before_run/after_run
- Add class hierarchy clarification for both options - Merge detailed design sections (side-by-side comparison) - Move detailed design before decision outcome - Move compaction discussion after decision - Add .NET implementation comparison (feature equivalence) - Update .NET method names to match actual implementation - Rename hook methods to before_run/after_run - Fix storage context table for injected context
- Note that class and method names are open for discussion - Add alternative method naming options table - Include invoking/invoked as option matching current Python and .NET
…filtering - Remove smart mode for load_messages (now explicit bool, default True) - Add attribution marker in additional_properties for message filtering - Update validation to warn on multiple or zero storage loaders - Add note about ChatReducer naming from .NET - Note that attribution should not be propagated to storage
- Option A: Instances in Session (current proposal) - Option B: Instances in Agent, State in Session - B1: Simple dict state with optional return - B2: SessionState object with mutable wrapper - Updated examples to use Hooks pattern (before_run/after_run) - Added open discussion on hook factories in Option B model
Decision outcomes: - Option 3 (Hooks pattern) with ContextPlugin class name - Methods: before_run/after_run - Option B1: Instances in Agent, State in Session (simple dict) - Whole state dict passed to plugins (mutable, no return needed) - Added trust note: plugins reason over messages, so they're trusted by default Status changed from proposed to accepted.
Signature now: before_run(agent, session, context, state)
Simpler design: agent stores Sequence[ContextPlugin] and calls before_run/after_run directly in the run method.
2bc78ee to
1372be3
Compare
…ports - Add to_dict()/from_dict() on AgentSession with 'type' discriminator - Present serialization as Option A (direct) vs Option B (through agent) - Rewrite ownership section as 2x2 matrix (orthogonal decision) - Move Instance Ownership Options before Decision Outcome - Fix get_session to use service_session_id, split from create_session - Add decorator-based provider convenience API (@before_run/@after_run) - Add _ prefix naming strategy for all PR1 types (core + external) - Constructor compatibility table for existing providers - Add load_messages=False skip logic to all agent run loops - Clarify abstract vs non-abstract in execution pattern samples - Update auto-provision: trigger on conversation_id or store=True - Document root package exports (ContextProvider, HistoryProvider, etc.) - Rename section heading to 'Key Design Considerations'
| self.session_id = session_id | ||
|
|
||
| @abstractmethod | ||
| async def process(self, context: SessionContext, next: ContextMiddlewareNext) -> None: |
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.
| async def process(self, context: SessionContext, next: ContextMiddlewareNext) -> None: | |
| async def process(self, context: SessionContext, call_next: ContextMiddlewareNext) -> None: |
Summary
This ADR proposes unifying \ContextProvider, \ChatMessageStore, and \AgentThread\ into a single \ContextMiddleware\ concept for the Python SDK.
Problem
Currently, developers doing 'Context Engineering' must understand multiple abstractions:
Proposed Solution
A unified \ContextMiddleware\ using the onion/wrapper pattern (like existing \AgentMiddleware):
\\python
class RAGMiddleware(ContextMiddleware):
async def process(self, context: SessionContext, next) -> None:
# Pre-processing: add context
docs = await self.retrieve_documents(context.input_messages[-1].text)
context.add_messages(self.source_id, [ChatMessage.system(f'Context: {docs}')])
agent = ChatAgent(
chat_client=client,
context_middleware=[
InMemoryStorageMiddleware('memory'),
RAGMiddleware('rag'),
]
)
\\
Key Decisions
Migration Impact
See the full ADR in \docs/decisions/00XX-python-context-middleware.md\ for detailed design, code examples, and implementation workplan.
Related Issues
This ADR addresses the following issues: