feat(agents): Option B — _BackendProtocol in langchain, backend on AgentRuntime#37928
feat(agents): Option B — _BackendProtocol in langchain, backend on AgentRuntime#37928Sydney Runkle (sydney-runkle) wants to merge 1 commit into
Conversation
…entRuntime - _BackendProtocol: private Protocol with core backend methods (read/write/ls etc.) - AgentRuntime.backend: _BackendProtocol | None — typed at the langchain layer - create_agent accepts backend= and threads it into AgentRuntime at dispatch time - No AgentRuntime subclass or BackendMiddleware needed in subpackages
| model_settings: dict[str, Any] = field(default_factory=dict) | ||
| """Additional model-specific settings.""" | ||
|
|
||
| backend: _BackendProtocol | None = field(default=None) |
There was a problem hiding this comment.
🟡 Backend cannot be overridden on ModelRequest
Adding AgentRuntime.backend makes backend part of the runtime state carried by model middleware, but ModelRequest.override() still only treats model/system_prompt/tool_choice/tools/response_format/model_settings as runtime fields. A middleware that tries to replace or clear the backend for a nested handler call via request.override(backend=...) will route backend to dataclasses.replace(self, ...) on ModelRequest, which raises TypeError: ModelRequest.__init__() got an unexpected keyword argument 'backend' at runtime. Include backend in the override typed dict and _runtime_fields (and optionally expose a backend property) so backend behaves like the other AgentRuntime fields.
(Refers to line 120)
Was this helpful? React with 👍 or 👎 to provide feedback.
Alternative to the approach in this branch — moves the backend protocol definition into langchain so `AgentRuntime.backend` is properly typed at the langchain layer.
```python
class _BackendProtocol(Protocol): # private to langchain
def read(self, path: str) -> str: ...
def write(self, path: str, content: str) -> Any: ...
def ls(self, path: str) -> Any: ...
def download_files(self, paths: list[str]) -> Any: ...
async def aread(self, path: str) -> str: ...
async def awrite(self, path: str, content: str) -> Any: ...
async def als(self, path: str) -> Any: ...
async def adownload_files(self, paths: list[str]) -> Any: ...
DataClass
class AgentRuntime(Runtime[ContextT]):
...
backend: _BackendProtocol | None = None # properly typed
def create_agent(model, tools=None, *, backend: _BackendProtocol | None = None, ...): ...
```
vs. Option A (current branch)
backendtyping in langchainobject | None(loose)_BackendProtocol | None(typed)AgentRuntime(LCAgentRuntime)BackendMiddlewareBackendProtocolownerruntime.backendin DA hooks