OpenAIChatCompletionsModel sends tool_choice without tools, which the API rejects, on turns with no tools

Đang mở Phù hợp với người mới
#5,468 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
82/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
api

Hướng nghiên cứu

Bắt đầu từ src/agents/models/openai_chatcompletions.py, nơi tool_choice được chuyển đổi và truyền đi, trong khi parallel_tool_calls đã được kiểm soát bởi converted_tools. Cần cùng điều kiện kiểm soát đó cho tool_choice trong src/agents/extensions/models/litellm_model.py. Chạy tests/models trước và sau khi thay đổi. Hoàn thành khi tool_choice bị bỏ qua khi không có tool và một test được thêm vào kiểm tra nội dung request.

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

Mô tả

Please read this first
  • Have you read the docs? Yes
  • Have you searched for related issues? Yes. #4326 (fixed by #4359 and #4330) covered the same API rule for parallel_tool_calls. tool_choice was left out.
Describe the bug

OpenAIChatCompletionsModel always sends ModelSettings.tool_choice, even when the converted tool list is empty and tools is left out of the request. The Chat Completions API rejects that combination:

Invalid value for 'tool_choice': 'tool_choice' is only allowed when 'tools' are specified.

So an app-wide RunConfig(model_settings=ModelSettings(tool_choice="auto")) breaks every turn of an agent that has no function tools and no handoffs in that turn. Examples are a final summarizer agent after a handoff, or an agent whose tools are all switched off with is_enabled. parallel_tool_calls is already omitted in this case. LitellmModel sends the same request; I checked the body LiteLLM actually posts. AnyLLMModel's chat path has the same line, but I didn't run it.

Debug information
  • Agents SDK version: main (e355d69), also 0.24.0
  • Related library versions: openai 3.29.0, litellm 1.83.0
  • Python version: 3.14.7
  • Operating system: macOS
  • Model and model provider: any Chat Completions model (OpenAIChatCompletionsModel, LitellmModel)
  • Does the issue reproduce with the latest Agents SDK release? Yes
  • Does the issue occur consistently or intermittently? Consistently
Repro steps

A real AsyncOpenAI client on an httpx2.MockTransport, printing what's sent:

import asyncio, json
import httpx2
from openai import AsyncOpenAI
from agents import Agent, ModelSettings, OpenAIChatCompletionsModel, RunConfig, Runner

def handler(request: httpx2.Request) -> httpx2.Response:
    body = json.loads(request.content)
    print("tools in request:", "tools" in body, "| tool_choice:", body.get("tool_choice"),
          "| parallel_tool_calls in request:", "parallel_tool_calls" in body)
    return httpx2.Response(200, json={
        "id": "chatcmpl-1", "object": "chat.completion", "created": 0, "model": "gpt-4o-mini",
        "choices": [{"index": 0, "finish_reason": "stop",
                     "message": {"role": "assistant", "content": "OK"}}],
    })

async def main() -> None:
    client = AsyncOpenAI(api_key="x", http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)))
    agent = Agent(name="Summarizer", instructions="Reply OK",
                  model=OpenAIChatCompletionsModel("gpt-4o-mini", client))
    cfg = RunConfig(tracing_disabled=True,
                    model_settings=ModelSettings(tool_choice="auto", parallel_tool_calls=False))
    await Runner.run(agent, "Say OK.", run_config=cfg)

asyncio.run(main())
tools in request: False | tool_choice: auto | parallel_tool_calls in request: False
Expected behavior

tool_choice is left out when the request has no tools, the same way parallel_tool_calls already is.

Root cause and a possible fix

tool_choice is converted at openai_chatcompletions.py#L642 and sent at #L709, while only parallel_tool_calls is gated on converted_tools (#L652-L656). LiteLLM: litellm_model.py#L646. AnyLLM: any_llm_model.py#L951.

--- a/src/agents/models/openai_chatcompletions.py
+++ b/src/agents/models/openai_chatcompletions.py
@@ -654,6 +654,9 @@ class OpenAIChatCompletionsModel(Model):
         parallel_tool_calls: bool | Omit = (
             self._non_null_or_omit(model_settings.parallel_tool_calls) if converted_tools else omit
         )
+        # The API rejects tool_choice without tools, the same as parallel_tool_calls.
+        if not converted_tools:
+            tool_choice = omit
--- a/src/agents/extensions/models/litellm_model.py
+++ b/src/agents/extensions/models/litellm_model.py
@@ -643,7 +643,7 @@ class LitellmModel(Model):
-            tool_choice=self._remove_not_given(tool_choice),
+            tool_choice=self._remove_not_given(tool_choice) if converted_tools else None,

With that change, both repros send no tool_choice, and tests/models passes (1023 passed, 1 skipped). The placeholder Response.tool_choice built for streaming is separate and unchanged.

Ngôn ngữ chính
Python
Star
30.5k
Fork
5k
Merge trung bình
22 giờ 21 phút
Pull request đã merge (30 ngày)
98

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 openai/openai-agents-python

Tất cả issue của openai/openai-agents-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.