8000
Skip to content

fix: run evaluation on main thread when num_threads=1 - #9328

Merged
isaacbmiller merged 2 commits into
stanfordnlp:mainfrom
zamal-db:fix/evaluate-main-thread
Mar 4, 2026
Merged

fix: run evaluation on main thread when num_threads=1#9328
isaacbmiller merged 2 commits into
stanfordnlp:mainfrom
zamal-db:fix/evaluate-main-thread

Conversation

@zamal-db
Copy link
Copy Markdown
Contributor

Closes #9150

Problem

When using dspy.Evaluate with num_threads=1, work is still dispatched to a ThreadPoolExecutor. This breaks frameworks like AppWorld and Terminal Bench that call signal.signal() inside their task execution, because signal.signal() can only be called from the main thread:

\
ValueError: signal only works in main thread of the main interpreter
\\

Even setting num_threads=1 doesn't help, since ParallelExecutor unconditionally routes through ThreadPoolExecutor.

Solution

Add a _execute_sequential() method to ParallelExecutor that runs items directly on the calling thread when num_threads == 1. The sequential path preserves all existing behavior:

  • Error counting and max_errors cancellation
  • failed_indices and exceptions_map tracking
  • Progress bar updates (both regular and compare_results mode)
  • KeyboardInterrupt handling

When num_threads > 1, the existing _execute_parallel() path is used unchanged.

Changes

  • dspy/utils/parallelizer.py: Add _execute_sequential() and route to it from execute() when num_threads == 1
  • tests/utils/test_parallelizer.py: Add 5 tests for sequential execution (main-thread verification, error handling, max_errors, failed index tracking, compare_results)
  • tests/evaluate/test_evaluate.py: Add 1 end-to-end test verifying Evaluate(num_threads=1) runs the metric on the main thread

@isaacbmiller isaacbmiller left a comment
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the PR! Left some comments

Comment thread tests/evaluate/test_evaluate.py Outdated
Comment thread dspy/utils/parallelizer.py
…m docstring

- Extract _process_outcome() and _report_progress() helpers used
  by both _execute_sequential and _execute_parallel
- Remove issue reference and framework-specific justification
  from test docstring
@zamal-db
zamal-db commented Mar 4, 2026
Copy link
Copy Markdown
Contributor Author

Hey, pushed a fix for both comments. Extracted shared _process_outcome/_report_progress helpers so sequential and parallel use the same logic now, and trimmed the test docstring. Ready for another look when you get a chance!

@zamal-db
zamal-db requested a review from isaacbmiller March 4, 2026 07:30
@okhat
okhat commented Mar 4, 2026
Copy link
Copy Markdown
Collaborator

I like this!

@isaacbmiller
isaacbmiller merged commit c342407 into stanfordnlp:main Mar 4, 2026
12 checks passed
@isaacbmiller
Copy link
Copy Markdown
Collaborator

Thanks for the contribution @zamal-db !

@zamal-db
zamal-db deleted the fix/evaluate-main-thread branch March 5, 2026 09:27
MaximeRivest pushed a commit to MaximeRivest/dspy that referenced this pull request Mar 5, 2026
* fix: run evaluation on main thread when num_threads=1 (stanfordnlp#9150)

* refactor: address review  share logic between sequential/parallel, trim docstring

- Extract _process_outcome() and _report_progress() helpers used
  by both _execute_sequential and _execute_parallel
- Remove issue reference and framework-specific justification
  from test docstring
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

How to use DSPy with stateful agent benchmarks?

3 participants

0