refactor: detect async modules at discovery time instead of per-request#41
refactor: detect async modules at discovery time instead of per-request#41isaacbmiller wants to merge 1 commit intomainfrom
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c078488854
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if 'aforward' not in cls.__dict__: | ||
| return False |
There was a problem hiding this comment.
Detect inherited async overrides during discovery
The new cls.__dict__ check only marks modules as async when aforward is defined directly on the concrete class, so subclasses that inherit a user-defined aforward from an intermediate base class are now misclassified as sync. In that case execute_pipeline uses instance(**inputs) instead of await instance.acall(**inputs), which can bypass intended async behavior or fail for async-only module hierarchies even though they previously worked with the per-request hasattr check.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Summary
- Fixed async capability detection to walk the class MRO and treat
aforwardimplemented on any user-defined base class as native async, while still stopping atdspy.Moduleto avoid false positives from DSPy’s default method. This addresses inherited async module hierarchies called out in review feedback. src/dspy_cli/discovery/module_finder.pyL47-L59 - Added targeted unit tests covering: (1) default-only
dspy.Modulebehavior, (2) directaforwardoverride, and (3) inherited user-definedaforwardoverride from an intermediate base class. tests/discovery/test_module_finder.pyL253-L290 - Committed the fix on the current branch as
6967a04and created a follow-up PR via themake_prtool with a title/body describing this delta on top of the original PR.
Testing
⚠️ pytest tests/discovery/test_module_finder.py tests/test_execution.py(fails during collection in this environment: missingdspydependency).- ✅
python -m compileall src/dspy_cli/discovery/module_finder.py tests/discovery/test_module_finder.py
Check for user-implemented aforward() once during module discovery. Walks the MRO up to (but not including) dspy.Module, so aforward inherited from an intermediate base class is still detected while the default base-class method is ignored. Store result as has_native_async on DiscoveredModule and use it in execute_pipeline. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
c078488 to
4165260
Compare
|
Dropping this -- hasattr per-request is fine for a nanosecond check against second-long LLM calls, and the cls.dict/MRO approach adds complexity + inheritance edge cases for no measurable gain. PR #42 (executor) will keep using hasattr. |
Split from #38 (2/5)
Changes
Check for user-implemented
aforward()once during module discovery usingcls.__dict__(avoids false positives from inherited base-class default methods). The result is stored ashas_native_asynconDiscoveredModuleand used inexecute_pipelineinstead of a per-requesthasattr()check.Why cls.dict instead of hasattr
dspy.Moduleprovides a defaultaforwardthat just delegates toforward(). Usinghasattrwould incorrectly route all modules through the async path. Checkingcls.__dict__ensures only user-writtenaforwardmethods are detected.Files changed
src/dspy_cli/discovery/module_finder.py-- new field + helper functionsrc/dspy_cli/server/execution.py-- usemodule.has_native_asynctests/test_execution.py-- updated mocksTesting
All 123 existing tests pass.