Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

[Bug] DeepAgents plugin: `_build_bound_model` drops `bind_kwargs` (incl. `response_format`) on `bind()` → `bind_tools()` sequence

クローズ 初心者向け
#1,896 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
78/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
python
領域
backend

調査の方向性

temporalio/contrib/deepagents/_activity.py の _DeepAgentsActivities._build_bound_model から始め、issue に記載されている bind()、続いて bind_tools() というシーケンスを確認してください。ツールを bind した後も、response_format と tool_choice を含む bind_kwargs が付加されたままになっていることを検証してください。完了条件は、provider request がそれらの kwargs を保持しつつ、tool schemas も引き続き含んでいることです。この activity に対するテストが repository にある場合は、対象を絞ったカバレッジを追加または更新してください。

索引モデルが issue の本文から書いたものです。

説明

ai-sdk bug
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; main still 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.

主要言語
Python
スター
1.2k
フォーク
245
平均マージ
3日 18時間
マージ済み PR(30日)
44

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

temporalio/sdk-python のほかの issue

temporalio/sdk-python の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。