Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

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

Đang mở Phù hợp với người mới
#1,896 2 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
78/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
backend

Hướng nghiên cứu

Bắt đầu tại temporalio/contrib/deepagents/_activity.py, ở _DeepAgentsActivities._build_bound_model, và kiểm tra chuỗi bind() rồi bind_tools() được mô tả trong issue. Xác minh rằng bind_kwargs, bao gồm response_format và tool_choice, vẫn được gắn sau khi các tool được bind. Hoàn tất khi provider request giữ nguyên các kwargs đó và vẫn bao gồm các tool schemas; hãy thêm hoặc cập nhật coverage tập trung nếu repository có các test cho activity này.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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.

Ngôn ngữ chính
Python
Star
1.2k
Fork
245
Merge trung bình
2 ngày 16 giờ
Pull request đã merge (30 ngày)
37

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của temporalio/sdk-python

Tất cả issue của temporalio/sdk-python

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.