Skip to content

[Bug] reasoning_effort is annotated with a function call, which is not valid Literal typing - #2102

Open
ayaangazali wants to merge 1 commit into
kyegomez:masterfrom
ayaangazali:fix/reasoning-effort-literal
Open

[Bug] reasoning_effort is annotated with a function call, which is not valid Literal typing#2102
ayaangazali wants to merge 1 commit into
kyegomez:masterfrom
ayaangazali:fix/reasoning-effort-literal

Conversation

@ayaangazali

Copy link
Copy Markdown
Contributor

The follow-up you suggested on #1773 — but smaller than proposed, and for a different reason than I expected. Details below.

The bug

swarms/structs/agent.py:394 on master:

reasoning_effort: Literal[get_reasoning_efforts()] = None,

Literal takes literal members. A function call there is rejected by every type checker, so reasoning_effort has effectively been unannotated for tooling purposes. It happens to work at runtime only because annotations are evaluated as ordinary expressions.

And it is evaluated — at class-body execution, i.e. when agent.py is imported. get_reasoning_efforts() does this:

try:
    import litellm
    annotation = inspect.signature(litellm.completion).parameters["reasoning_effort"].annotation

So an annotation reaches into litellm's signature at import time.

The fix

Spell the members out once as a Literal, and derive the tuple from it:

ReasoningEffort = Literal["none", "minimal", ..., "None"]

REASONING_EFFORTS: Tuple[str, ...] = get_args(ReasoningEffort)
reasoning_effort: Optional[ReasoningEffort] = None,

The static type and the runtime tuple now come from one source, so they cannot disagree. get_reasoning_efforts() is untouched and still unions whatever the installed litellm advertises, so no accepted value is narrowed — that behaviour was the point of the helper and it is preserved.

Verified

ReasoningEffort members:                       9
REASONING_EFFORTS == get_args(ReasoningEffort): True
get_reasoning_efforts() still unions litellm:   True
agent annotation: Optional[Literal['none','minimal','low','medium','high','xhigh','ultra','max','None']]

tests/structs/test_agent.py                     88 passed

Why this is smaller than you proposed

You suggested salvaging two files from #1773. I checked both and only one survives contact:

  • get_reasoning_efforts.py — real, and it turned out to be a correctness bug rather than only a perf one. That is this PR.
  • swarms/tools/mcp_manager.py — I built the leaf deferral (from __future__ import annotations, TYPE_CHECKING for the two annotations, local imports at the two genuine runtime sites isinstance(tool, MCPTool) and async with ClientSession(...), plus lru_cache on _mcp_is_v2) and then measured it, and it changes nothing today:
importing swarms.tools.mcp_manager
  master:      mcp in sys.modules: True
  with my fix: mcp in sys.modules: True

Because importing swarms.tools.mcp_manager executes swarms/__init__.py first, which pulls the package in regardless. The leaf deferral only pays off once the package boundary is handled — which is #2090's PEP 562 work. So it is not "helps anyone importing mcp_manager directly" yet, and I would rather not ship a change whose stated benefit I cannot demonstrate. Happy to send it as a one-file follow-up the moment #2090 lands, when it will actually compose.

One correction to the framing on #1773: #2090 has not landed — it is still open as of now, and master still has from litellm import model_list at agent.py:21. Nothing here depends on it either way; this PR stands alone.

…n call

`reasoning_effort: Literal[get_reasoning_efforts()]` is not valid typing.
Literal takes literal members, so every type checker rejects a call there,
and the annotation is evaluated when the class body runs -- which calls
get_reasoning_efforts(), and that function does `import litellm` to read the
values off litellm.completion's signature.

Spell the members out as a ReasoningEffort Literal and derive the tuple from
it with get_args, so the static type and the runtime tuple cannot disagree.
get_reasoning_efforts() is unchanged and still unions whatever the installed
litellm advertises, so no accepted value is narrowed.
Copilot AI lite review requested due to automatic review settings August 29, 2026 23:47
@ayaangazali
ayaangazali requested a review from kyegomez as a code owner August 29, 2026 23:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ayaangazali

Copy link
Copy Markdown
Contributor Author

@kyegomez for review — this is the #1773 follow-up you suggested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants