Affected area
- Python binding
- Middleware or guardrails
- Framework integrations
Current behavior
The maintained LangChain middleware supplies LangChainCodec to managed execution. Relay decodes and re-encodes requests before the provider callback runs, including when no request interceptor changes the request.
For assistant messages containing tool calls, the round-trip preserves normalized AIMessage.tool_calls but previously rebuilt the message without AIMessage.additional_kwargs["tool_calls"].
ChatNVIDIA serializes prior assistant tool calls from that provider-form field. Without it, the next provider request contains a tool result without the preceding assistant tool call and is rejected. DeepAgents exposes this naturally during a normal model → tool → model loop, but the affected implementation is the shared LangChain codec.
Expected behavior
A no-op LangChain codec round-trip must preserve the provider tool-call representation required by ChatNVIDIA alongside normalized LangChain tool calls.
When an annotated Relay request interceptor changes normalized tool calls, the provider representation must be regenerated from the modified calls rather than retaining stale values.
Reproduction
- Create an
AIMessage containing equivalent normalized tool_calls and OpenAI-compatible additional_kwargs["tool_calls"].
- Serialize it into an
LLMRequest, then call LangChainCodec.decode() followed by encode() without edits.
- Deserialize the rebuilt message.
- Before the fix,
rebuilt.tool_calls was populated but rebuilt.additional_kwargs was empty; ChatNVIDIA’s serializer then emitted an assistant message without tool_calls.
Fix and regression coverage
The LangChain codec now preserves the original provider tool-call payload when normalized calls are unchanged, and regenerates a canonical provider payload after an interceptor edits them.
Regression coverage includes:
- Codec no-op round-trip preservation of normalized and provider-form tool calls.
- ChatNVIDIA serializer output retaining assistant tool calls and tool-only
content: null.
- Managed LangChain middleware intercept changing tool-call arguments and receiving the matching regenerated provider payload.
- A real Inference Hub ChatNVIDIA model → tool → model smoke test through Relay middleware.
Environment
- NeMo Relay main at
2ee3e4d277b3f29437c053887cfacc95ca58fd5b
- Python 3.12
- DeepAgents 0.7.4
- langchain-nvidia-ai-endpoints 1.4.1
- macOS
Impact
This prevents managed LangChain and DeepAgents agents using ChatNVIDIA from completing multi-turn tool-call runs. Avoiding Relay’s managed LangChain middleware is a temporary workaround, but removes Relay-managed observability, middleware, and pricing for those model calls.
Related: #805 tracks the separate enhancement to model DeepAgents orchestrator and local-subagent topology as nested Relay Agent scopes.
Affected area
Current behavior
The maintained LangChain middleware supplies
LangChainCodecto managed execution. Relay decodes and re-encodes requests before the provider callback runs, including when no request interceptor changes the request.For assistant messages containing tool calls, the round-trip preserves normalized
AIMessage.tool_callsbut previously rebuilt the message withoutAIMessage.additional_kwargs["tool_calls"].ChatNVIDIA serializes prior assistant tool calls from that provider-form field. Without it, the next provider request contains a tool result without the preceding assistant tool call and is rejected. DeepAgents exposes this naturally during a normal model → tool → model loop, but the affected implementation is the shared LangChain codec.
Expected behavior
A no-op LangChain codec round-trip must preserve the provider tool-call representation required by ChatNVIDIA alongside normalized LangChain tool calls.
When an annotated Relay request interceptor changes normalized tool calls, the provider representation must be regenerated from the modified calls rather than retaining stale values.
Reproduction
AIMessagecontaining equivalent normalizedtool_callsand OpenAI-compatibleadditional_kwargs["tool_calls"].LLMRequest, then callLangChainCodec.decode()followed byencode()without edits.rebuilt.tool_callswas populated butrebuilt.additional_kwargswas empty; ChatNVIDIA’s serializer then emitted an assistant message withouttool_calls.Fix and regression coverage
The LangChain codec now preserves the original provider tool-call payload when normalized calls are unchanged, and regenerates a canonical provider payload after an interceptor edits them.
Regression coverage includes:
content: null.Environment
2ee3e4d277b3f29437c053887cfacc95ca58fd5bImpact
This prevents managed LangChain and DeepAgents agents using ChatNVIDIA from completing multi-turn tool-call runs. Avoiding Relay’s managed LangChain middleware is a temporary workaround, but removes Relay-managed observability, middleware, and pricing for those model calls.
Related: #805 tracks the separate enhancement to model DeepAgents orchestrator and local-subagent topology as nested Relay Agent scopes.