[Bug] DeepAgents plugin: `_build_bound_model` drops `bind_kwargs` (incl. `response_format`) on `bind()` → `bind_tools()` sequence
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Accessibilité débutants
- 78/100
Piste de recherche
Commencez dans temporalio/contrib/deepagents/_activity.py, au niveau de _DeepAgentsActivities._build_bound_model, et examinez la séquence bind() puis bind_tools() décrite dans l’issue. Vérifiez que bind_kwargs, notamment response_format et tool_choice, restent attachés après le binding des outils. C’est terminé lorsque la requête du provider conserve ces kwargs tout en incluant toujours les schémas des outils ; ajoutez ou mettez à jour une couverture ciblée si le repository contient des tests pour cette activity.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
What are you really trying to do?
Run a deep agent through the Temporal DeepAgents plugin (create_temporal_deep_agent) with a native structured output: response_format=ProviderStrategy(PydanticModel) against an OpenAI-compatible endpoint (in our case an LLM gateway proxying Azure OpenAI). The goal is a durable agent whose final answer is schema-validated JSON.
Describe the bug
response_format never reaches the provider: the worker-side activity _DeepAgentsActivities._build_bound_model (temporalio/contrib/deepagents/_activity.py) rebuilds the model as:
model = self._model_provider(input.model_name)
if input.bind_kwargs:
model = model.bind(**input.bind_kwargs) # response_format bound here
if input.tool_schemas:
model = model.bind_tools(input.tool_schemas)
In langchain-core, BaseChatModel.bind() returns a _ChatModelBinding (a RunnableBinding). That object has no bind_tools of its own, so attribute lookup delegates to the unbound model via RunnableBinding.__getattr__. The resulting binding therefore contains only tools: every kwarg from the earlier bind(**bind_kwargs) — including response_format — is silently dropped.
Consequence: the API request is sent with tools but without response_format, the model answers in free text/markdown, and langchain's ProviderStrategyBinding.parse fails with:
Failed to parse structured output for tool 'EmailNeed': Native structured output
expected valid JSON for EmailNeed, but parsing failed:
Expecting value: line 1 column 1 (char 0).
Note this is not limited to ProviderStrategy/response_format: any bind_kwargs is lost. With ToolStrategy, tool_choice="any" (which forces the structured-output tool call) travels the same path and is dropped too.
Evidence: the Temporal workflow history shows the activity input of deepagents.invoke_model containing bind_kwargs.response_format (so the workflow side correctly forwards it), while the provider request did not contain it — verified by replaying the exact bind() → bind_tools() sequence against the endpoint.
Minimal Reproduction
Pure langchain-core mechanism (no Temporal server needed):
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="...", api_key="...", base_url="https://...") # any OpenAI-compatible endpoint
response_format = {
"type": "json_schema",
"json_schema": {
"name": "EmailNeed",
"schema": {
"type": "object",
"properties": {"request": {"type": "string"}},
"required": ["request"],
},
},
}
tool = {
"type": "function",
"function": {
"name": "memory_recall",
"description": "search memory",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
},
}
# Sequence performed by _DeepAgentsActivities._build_bound_model
# (temporalio/contrib/deepagents/_activity.py)
final = model.bind(response_format=response_format).bind_tools([tool])
print(final.kwargs) # -> {'tools': [...]} ... response_format is gone
Full stack: a workflow created with create_temporal_deep_agent(..., response_format=ProviderStrategy(SomeModel)) against an OpenAI-compatible provider, run on a dev server with the worker-side model_provider returning a ChatOpenAI. The workflow fails on the final model call with the error above; the deepagents.invoke_model activity input in the event history contains bind_kwargs with response_format, proving it is lost worker-side.
Environment/Versions
- OS and processor: Linux x86_64
- SDK version: temporalio 1.33.0 (latest at time of writing;
mainstill contains the affected code), langchain 1.4.1, langchain-core 1.6.3, langchain-openai 1.6.2 - Temporal dev server via Docker Compose; worker + workflow run locally (not building from source)
Additional context
Suggested fix: reverse the two operations in _build_bound_model, since RunnableBinding.bind merges kwargs ({**self.kwargs, **kwargs}):
def _build_bound_model(self, input: ModelActivityInput) -> Any:
model = self._model_provider(input.model_name)
if input.tool_schemas:
model = model.bind_tools(input.tool_schemas)
if input.bind_kwargs:
model = model.bind(**input.bind_kwargs)
return model
(Alternatively, merge everything into a single bind.) The symptom is easy to misattribute to the LLM "ignoring" the response format, since the request succeeds — it just lacks the parameter.
Related upstream behavior worth noting for anyone hitting the next step: once response_format is actually forwarded, langchain-openai switches to chat.completions.parse(), which requires all tools to be strict; convert_to_openai_tool returns pre-formatted OpenAI tool dicts unchanged, so dict tool schemas are never strictified. We worked around both locally by overriding bind() to return a binding whose bind_tools merges the previously bound kwargs and strictifies dict tools when response_format is present.
- Langage dominant
- Python
- Étoiles
- 1.2k
- Forks
- 245
- Merge moyen
- 2 j 17 h
- PR mergées (30 j)
- 38
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Aucun modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de temporalio/sdk-python
-
Difficulté 2/5 1-3 heures Accessibilité débutants 84/100
temporalio/sdk-python#1897 ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
temporalio/sdk-python#1517 · 10 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
temporalio/sdk-python#496 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 35/100
temporalio/sdk-python#1894 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 25/100
temporalio/sdk-python#1890 ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de temporalio/sdk-python
Issues similaires
-
bug status/needs-triage
Difficulté 2/5 1-3 heures Accessibilité débutants 86/100
prowler-cloud/prowler#12887 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
area: desktop platform: macos priority: p3 status: ready type: enhancement
Difficulté 1/5 Moins d'une heure Accessibilité débutants 92/100
use-agent-os/agent-os#3484 ·
Les mainteneurs répondent en général sous 2 jours
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 86/100
open-telemetry/opentelemetry-python-contrib#5113 · 2 commentaires · 2 réactions ·
Les mainteneurs répondent en général sous 1 jour
-
external
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
langchain-ai/docs#6255 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
Les mainteneurs répondent en général sous 1 jour