[BFCL] Eager import of qwen_agent forces audio deps on Claude-only users
Describe the issue
A fresh pip install bfcl-eval followed by bfcl --help crashes with ModuleNotFoundError: No module named 'soundfile', even when the user has no intention of running Qwen models. The cause is an eager import chain at CLI startup that loads every model handler — including Qwen's, which transitively requires soundfile, qwen_agent, and (further downstream) the libsndfile audio stack. Users running only Claude, GPT, Gemini, or open-weights models pay the cost of Qwen's audio dependencies just to see bfcl --help.
ID datapoint
- Datapoint / Model Handler permalink: https://github.com/ShishirPatil/gorilla/blob/main/berkeley-function-call-leaderboard/bfcl_eval/model_handler/api_inference/qwen.py (and the eager import in
bfcl_eval/constants/model_config.py) - Issue: Top-level import of the Qwen model handler forces every BFCL user — even those running only Claude/GPT/Gemini/open-weights models — to satisfy Qwen's transitive dependencies (
qwen_agent,soundfile, libsndfile) at CLI startup. The CLI fails before printing--help. - Gorilla repo commit #: reproduced against
bfcl-eval2026.3.23 from PyPI (latest at time of filing); not tied to a specific commit because the issue is a packaging design rather than a bug introduced by a particular change.
What is the issue
Environment
bfcl-eval2026.3.23 (from PyPI)- Python 3.14.5 on macOS arm64
qwen_agentinstalled as a transitive dependency- No Qwen models being benchmarked — user is evaluating Claude Sonnet 4.6 only
Symptom
$ pip install bfcl-eval
[succeeds]
$ bfcl --help
Traceback (most recent call last):
File ".../bin/bfcl", line 3, in <module>
from bfcl_eval.__main__ import cli
...
File ".../bfcl_eval/constants/model_config.py", line 27, in <module>
from bfcl_eval.model_handler.api_inference.qwen import (
...
)
File ".../bfcl_eval/model_handler/api_inference/qwen.py", line 8, in <module>
from qwen_agent.llm import get_chat_model
File ".../qwen_agent/llm/__init__.py", line 18, in <module>
from .azure import TextChatAtAzure
File ".../qwen_agent/llm/azure.py", line 20, in <module>
from qwen_agent.llm.base import register_llm
File ".../qwen_agent/llm/base.py", line 29, in <module>
from qwen_agent.utils.utils import (extract_text_from_message, ...)
File ".../qwen_agent/utils/utils.py", line 34, in <module>
import soundfile as sf
ModuleNotFoundError: No module named 'soundfile'bfcl --help should work after pip install bfcl-eval with no further setup. It currently does not.
Reproduction
# Fresh Python 3.14 venv (3.13 reproduces the same way)
python3.14 -m venv /tmp/repro
source /tmp/repro/bin/activate
pip install bfcl-eval
bfcl --help # crashes with ModuleNotFoundError: No module named 'soundfile'Root cause
In bfcl_eval/constants/model_config.py:27, the Qwen model handler is imported unconditionally at module load:
from bfcl_eval.model_handler.api_inference.qwen import (
...
)bfcl_eval/__main__.py triggers this import chain via:
__main__ → _llm_response_generation → eval_runner_helper → model_config → qwen handler → qwen_agent → soundfile (missing)So every entry into the BFCL CLI — including bfcl --help, bfcl models, bfcl version — has to satisfy Qwen's full transitive dependency closure before doing anything else.
Proposed Changes
Two options, in increasing order of restructure:
Option 1 — Lazy import of model handlers in model_config.py. Move the handler imports inside a factory function or use a registry pattern that loads handlers on demand:
# Instead of top-level imports, register lazily:
def get_handler(model_name: str):
if model_name.startswith("qwen"):
from bfcl_eval.model_handler.api_inference.qwen import QwenAPIHandler
return QwenAPIHandler
if model_name.startswith("claude"):
from bfcl_eval.model_handler.api_inference.anthropic import AnthropicAPIHandler
return AnthropicAPIHandler
# ...This keeps bfcl --help fast and dependency-light, and only loads each handler's transitive deps when that handler is actually requested.
Option 2 — Make Qwen (and similar) handlers optional extras. Move qwen_agent to an extras_require group in setup.py / pyproject.toml:
[project.optional-dependencies]
qwen = ["qwen_agent>=X.Y", "soundfile", ...]Then pip install 'bfcl-eval[qwen]' opts in. The default install only pulls dependencies for handlers the user actually needs.
Option 1 alone fixes the CLI startup issue. Option 2 additionally reduces install footprint and the risk of unrelated dep-conflicts (e.g., numba/scipy versions pulled in by librosa if Qwen ever needs it).
(Note: the issue template's {'previous_datapoint': ..., 'updated_datapoint': ...} JSON skeleton is for dataset corrections rather than packaging bugs, so it's omitted here — this issue is about the model-handler import structure, not a datapoint.)
Additional context
Impact
- Every user running BFCL today on a fresh install hits this on
bfcl --helpif they don't happen to already havesoundfilein their environment. - The downstream Qwen audio stack (soundfile → libsndfile, optionally librosa → numba → scipy) introduces a significant transitive dependency surface that's irrelevant to most users.
- This bites particularly on Python versions where
qwen_agentor its transitive deps don't have wheels available — every install becomes a build-from-source exercise.
Workaround
For now, after pip install bfcl-eval:
pip install soundfile
# If a later import chain step also fails on a different missing module, install that one tooThis unblocks the CLI for Claude/GPT/Gemini-only use cases, at the cost of carrying audio deps the user doesn't need.
Discovery context
Found while installing the BFCL stack for Claude Sonnet 4.6 evaluation as part of an Anthropic-recommended hallucination-evaluation toolchain. The benchmark itself is excellent and the V4 Hallucination subscore is exactly the kind of metric Claude-focused users want. The dependency packaging is the only friction.
Source: ShishirPatil/gorilla