-
Notifications
You must be signed in to change notification settings - Fork 19.6k
Labels
feature requestrequest for an enhancement / additional functionalityrequest for an enhancement / additional functionality
Description
Checked other resources
- This is a feature request, not a bug report or usage question.
- I added a clear and descriptive title that summarizes the feature request.
- I used the GitHub search to find a similar feature request and didn't find it.
- I checked the LangChain documentation and API reference to see if this feature already exists.
- This is not related to the langchain-community package.
Feature Description
When using create_agent with middleware functions (specifically before_model hooks), developers cannot access the RunnableConfig object that was passed during agent invocation. This prevents implementing context-aware middleware behavior based on configuration parameters like metadata, tags, or custom settings.
Current Limitation
from langchain.agents import create_agent
from langchain_core.runnables import RunnableConfig
def logging_middleware(runtime):
# PROBLEM: Cannot access the RunnableConfig passed to agent.invoke()
# Need: runtime.config or similar to access metadata, tags, etc.
# Desired behavior:
# config = runtime.config
# if config.metadata.get("log_level") == "debug":
# enable_detailed_logging()
pass
# Setup
agent = create_agent(llm=my_llm, tools=my_tools, middleware=[logging_middleware])
# The config here is invisible to middleware
config = RunnableConfig(metadata={"log_level": "debug", "user_id": "123"})
result = agent.invoke("Hello", config=config)
### Use Case
## **Use Case**
```markdown
I need to implement sophisticated middleware for production LangChain applications that requires context-aware behavior based on the configuration passed during agent invocation.
### Specific Use Cases:
1. **Context-Aware Logging**: Log different levels of detail based on `config.metadata.get("log_level")`
2. **User-Specific Behavior**: Implement different middleware logic based on `config.metadata.get("user_id")` or user roles
3. **Environment-Specific Processing**: Handle production vs development differently using `config.tags`
4. **Audit Trail**: Track operations with run names and metadata for compliance
5. **Conditional Security**: Apply different security measures based on config parameters
### Current Problem:
- Must use global state or external configuration management (bad practice)
- Cannot implement dynamic middleware behavior based on invocation context
- Limits the usefulness of middleware for advanced monitoring and debugging
- Forces workarounds that make code harder to maintain and test
### Business Impact:
This limitation prevents building production-ready LangChain applications that need sophisticated logging, monitoring, and conditional behavior in middleware layers.
### Proposed Solution
Extend the runtime context object passed to middleware functions to include a `config` property containing the `RunnableConfig` from agent invocation.
### Implementation Approach:
1. **Agent Invocation**: Capture the `RunnableConfig` in the `invoke()` method
2. **Runtime Context**: Extend the runtime object to include `config` property
3. **Middleware Execution**: Ensure config is propagated through the middleware chain
4. **Default Handling**: Provide empty `RunnableConfig()` when none is passed
### Key Files Likely to Change:
- `langchain/agents/agent.py` or similar agent implementation files
- Middleware execution logic
- Runtime context creation/management
### Test Cases to Validate:
```python
def test_middleware_can_access_config():
captured_config = None
def capture_config_middleware(runtime):
nonlocal captured_config
captured_config = runtime.config
return runtime
agent = create_agent(middleware=[capture_config_middleware])
test_config = RunnableConfig(metadata={"test_key": "test_value"})
agent.invoke("test input", config=test_config)
assert captured_config is not None
assert captured_config.metadata["test_key"] == "test_value"
### Alternatives Considered
## **Alternatives Considered**
```markdown
### 1. Global State Management
- **Tried**: Using global variables to store config
- **Problem**: Not thread-safe, makes testing difficult, violates clean architecture principles
### 2. Custom Middleware Base Class
- **Considered**: Creating a new middleware base class that accepts config
- **Problem**: Would break existing middleware, requires major API changes
### 3. Context Managers
- **Considered**: Using Python context managers to inject config
- **Problem**: Doesn't integrate well with LangChain's existing architecture
### 4. Callback-Based Approach
- **Considered**: Using callbacks to pass config to middleware
- **Problem**: More complex API, doesn't fit the current middleware pattern
### 5. Environment Variables
- **Tried**: Using environment variables for configuration
- **Problem**: Not dynamic per-invocation, doesn't support complex metadata structures
The proposed solution of extending the runtime context is the most elegant because it:
- Maintains backward compatibility
- Follows existing LangChain patterns
- Requires minimal API changes
- Provides clean access to all config properties
### Additional Context
### Related Issues:
- This addresses the limitation mentioned in issue #33721
- Similar to how other LangChain components provide access to configuration context
### Success Criteria:
1. ✅ Middleware can access `runtime.config` containing the `RunnableConfig` from `agent.invoke()`
2. ✅ All `RunnableConfig` properties (metadata, tags, run_name, etc.) are accessible
3. ✅ When no config is passed to `invoke()`, `runtime.config` returns `RunnableConfig()`
4. ✅ Multiple middleware functions in a chain all receive the same config
5. ✅ Existing middleware without config access continues to work
6. ✅ All test cases pass
### Implementation Complexity:
- **Difficulty**: Medium (3/5)
- **Estimated Time**: 2-3 hours for experienced contributor
- **Impact**: High - enables sophisticated middleware patterns for production applications
### Example Production Use Case:
```python
def audit_middleware(runtime):
config = runtime.config
user_id = config.metadata.get("user_id")
operation = config.metadata.get("operation")
# Log to audit system
audit_logger.info(f"User {user_id} performing {operation}",
extra={"run_name": config.run_name, "tags": config.tags})
return runtime
def security_middleware(runtime):
config = runtime.config
if "sensitive" in (config.tags or []):
# Apply additional security measures
enable_data_redaction()
return runtimeMetadata
Metadata
Assignees
Labels
feature requestrequest for an enhancement / additional functionalityrequest for an enhancement / additional functionality