fix(providers): defer builtin provider instantiation to fix gunicorn startup crash#3403
fix(providers): defer builtin provider instantiation to fix gunicorn startup crash#3403fancyboi999 wants to merge 2 commits intoagentscope-ai:mainfrom
Conversation
…startup crash When gunicorn workers start with PYTHONPATH pointing at src/ while the package is also installed, the same module can be loaded under two different paths. Python then creates two distinct ModelInfo classes. Instances built with class-A fail pydantic v2 isinstance checks against class-B, crashing the worker at import time with: ValidationError: Input should be a valid dictionary or instance of ModelInfo Root cause: provider_manager.py created ~80 ModelInfo pydantic instances and ~20 Provider instances at module level (import time). Fix: store model data as plain dicts instead of ModelInfo instances, and move all Provider construction into a lazily-called factory function _create_builtin_providers(). Plain dicts carry no class identity, so pydantic coerces them using whichever ModelInfo class is current at validation time — no mismatch possible. Closes agentscope-ai#3375
|
Hi @fancyboi999, this is your 55th Pull Request. 📋 About PR TemplateTo help maintainers review your PR faster, please make sure to include:
Complete PR information helps speed up the review process. You can edit the PR description to add these details. 🙌 Join Developer CommunityThanks so much for your contribution! We'd love to invite you to join the official QwenPaw developer group! You can find the Discord and DingTalk group links under the "Developer Community" section on our docs page: We truly appreciate your enthusiasm—and look forward to your future contributions! 😊 We'll review your PR soon. |
There was a problem hiding this comment.
Pull request overview
This PR addresses a gunicorn multi-worker startup crash caused by Pydantic v2 class-identity mismatches when modules are imported via multiple paths. It does so by avoiding module-import-time creation of Pydantic ModelInfo/Provider instances and deferring built-in provider instantiation until ProviderManager initialization.
Changes:
- Replace built-in model definitions from
List[ModelInfo]to plainList[dict]to avoid cross-moduleisinstance()mismatches. - Move built-in provider instantiation from module-level constants to a lazy
_create_builtin_providers()factory invoked byProviderManager._init_builtins(). - Update unit tests to validate built-in providers via
_create_builtin_providers()/ updated model definitions.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/qwenpaw/providers/provider_manager.py |
Defers built-in provider instantiation and switches model definitions to plain dicts to prevent gunicorn/Pydantic import-identity crashes. |
tests/unit/providers/test_siliconflow_provider.py |
Adjusts tests to locate SiliconFlow providers via the new factory instead of removed module-level constants. |
tests/unit/providers/test_opencode_provider.py |
Adjusts tests to locate OpenCode provider via the new factory instead of removed module-level constants. |
tests/unit/providers/test_kimi_provider.py |
Adjusts tests for new Kimi model data structure (dicts) and provider factory usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address Copilot review: _find_provider returns from a List[Provider], so the annotation should be Provider, not OpenAIProvider.


Problem
gunicorn 启动时 worker 进程直接崩溃,报
pydantic ValidationError: Input should be a valid dictionary or instance of ModelInfo。uvicorn 不受影响。Root Cause
provider_manager.py在模块顶层创建了 ~80 个ModelInfopydantic 实例和 ~20 个Provider实例。当用户用
PYTHONPATH=src/配合 gunicorn 启动时,同一个.py文件可以通过两条路径被 Python 加载——一次走 PYTHONPATH,一次走 site-packages。Python 把它们当成两个不同模块,分别创建了两个ModelInfo类。MODELSCOPE_MODELS的元素用的是 class-A 的ModelInfo,但OpenAIProvider的 pydantic schema 绑的是 class-B。pydantic v2 做isinstance()检查时发现不匹配,直接抛 ValidationError。本地复现脚本:
Fix
两处改动:
ModelInfo来构造,不存在 A/B 不匹配_create_builtin_providers()工厂函数 — 在ProviderManager.__init__()时才调用,此时所有 import 已完成,类身份稳定Validation
Closes #3375