Skip to content

Commit 00ae446

Browse files
ploytsweidankongXiaoBoAIhelloml0326gemini-code-assist[bot]
authored
refactor(runner): runner for grader&executor (#100)
* refactor(runner): runner for new version * fix: history result ui render (#94) * fix: history result ui render * fix: history result ui - fix duplicate ids * feat(ui): Add Auto Rubric feature for automatic grading criteria gene… (#92) * feat(ui): Add Auto Rubric feature for automatic grading criteria generation Add new Auto Rubric feature that automatically generates evaluation rubrics for LLM applications. Includes simple and iterative generation modes, data upload, history management, and export functionality. * fix: Rename test_panel.py to rubric_tester.py to avoid pytest collection Pytest was treating test_panel.py as a test file due to the 'test_' prefix, causing import errors in CI. Renamed to rubric_tester.py to fix this issue. * fix(ui): stabilize widget state across language switch and persist language preference - Use stable values (e.g., "_custom_", "python") instead of translated labels as selectbox options - Prevents widget state loss when UI language changes - Add localStorage persistence for language preference - Add inject_language_loader() to restore language setting on page load - Update navigation to use stable feature_ids for feature selector * Feat/UI paper review (#101) * feat(ui): add Paper Review feature with progress tracking - Add Streamlit-based Paper Review UI with real-time progress display - Enhance pipeline with progress callback and stage tracking (ReviewStage, ReviewProgress) - Support bib_content parameter for direct .bib content verification - Add i18n translations for Paper Review feature (EN/ZH) * fix(auto_arena): fix file import and session state initialization issues - Fix file import re-processing on rerun by tracking processed file ID - Fix session state default values for evaluation and output settings * feat(ui): add workspace isolation for multi-user support - Add WorkspaceManager and StorageManager for workspace-based data isolation - Add workspace selector component in sidebar with create/switch/delete support - Update all history managers to use workspace-based paths - Add auto storage cleanup (500MB limit, 30-day retention) - Add browser ID based anonymous workspace for user identification - Add workspace i18n translations (EN/ZH) - Fix: auto-refresh page after Arena evaluation completes * refactor(ui): combine workspace and language selectors in one row - Add language selector option to workspace_selector component - Remove separate language selector from sidebar - Simplify sidebar layout by combining related controls * fix: improve error handling and workspace isolation - Add GraderError handling in pipeline to continue with partial results - Update paper review history service to use workspace-based paths - Add OPENJUDGE_ENABLE_SHARED_WORKSPACES env var to control shared workspace feature (disabled by default for security) * fix: resolve pre-commit linting issues - Remove unused imports (ReviewTaskConfig, ReviewStage, STATE_CURRENT_WORKSPACE) - Fix f-strings without placeholders - Fix unused variables by using underscore convention - Fix line-too-long issues by breaking long lines - Add pylint disable comments for intentional patterns - Apply auto-formatting (black, isort, autoflake, trailing-whitespace) * fix: bugs * feature(graders): add trajectory accuracy grader (#102) * add trajectory_accuracy_grader * add trajectory_accuracy_grader * add trajectory_accuracy_grader * feature (executor): implement executor patterns (#97) * feature (executor): implement executor patterns * fix: minors * format trajectory_accuracy_grader (#104) * format trajectory_accuracy_grader * Apply suggestion from @gemini-code-assist[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * feat(ui): improve Grader Generator feature with better UX (#103) ## Changes ### Renamed Feature - Renamed 'Auto Rubric' to 'Grader Generator' (Grader 生成) for clarity ### Data Upload Improvements - Removed hardcoded 500 record limit, allowing flexible data selection - Added data sampling feature with quick select buttons (100, 500, all) - Auto-detect score range from uploaded data (pointwise mode) - Simplified UI with clear required/optional labels ### Progress & Feedback - Added progress indicator with estimated time during generation - Added total elapsed time display after completion - Added warning about task cancellation limitations ### UI/UX Improvements - Reorganized config panel with clear section headers - Added required/optional badges for each config section - Replaced sliders with number inputs in advanced settings - Fixed HTML rendering issues in result panel - Fixed duplicate element ID error in history panel ### Bug Fixes - Fixed NoneType error in export_service when optional fields are None - Fixed </div> tag rendering issue in listwise mode - Added unique keys to download buttons to prevent ID conflicts ### i18n - Added new translation keys for all new UI text (EN/ZH) * refactor(graders): standardize prompt template format for common graders (#105) Standardize the prompt XML tag format across all common graders: - English prompts: use English tags (Rubrics, Steps, Constraints, Scale, etc.) - Chinese prompts: use Chinese tags (评分标准, 评估步骤, 注意事项, 评分量表, etc.) Affected files: - correctness.py - hallucination.py - harmfulness.py - instruction_following.py - relevance.py Co-authored-by: Cursor <cursoragent@cursor.com> * fix trajectory_accuracy_grader (#107) * fix trajectory_accuracy_grader * fix trajectory_accuracy_grader * Chore/update dependencies (#106) * chore(deps): update openai to v2 and add UI dependencies - Upgrade openai from >=1.92.0,<2.0.0 to >=2.8.0 - Add PyYAML, matplotlib, litellm to UI requirements - Improve installation instructions in ui/requirements.txt Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(ui): optimize layout and simplify language selector - Remove JavaScript injection for language loading, use URL params instead - Add render_sidebar_language_selector for standalone language selection - Hide header and reduce top padding for more compact layout - Conditionally show workspace selector based on ENABLE_SHARED_WORKSPACES - Refactor result_panel score card HTML for better readability Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): resolve pre-commit linting issues - Fix line-too-long in result_panel.py (195 > 120 chars) - Format imports with isort in app.py - Remove unnecessary pass statements in i18n/core.py Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(ui): move divider outside conditional block for consistency Move the divider after workspace/language selector outside the conditional block so it's always rendered regardless of whether shared workspaces are enabled. Co-authored-by: Cursor <cursoragent@cursor.com> * style(ui): improve HTML readability in result_panel.py Re-introduce indentation within the multi-line f-string for better HTML structure visibility and maintainability. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(i18n): make unused placeholder function private Make render_header_language_selector a private function (_render_header_language_selector) and remove it from public exports since it's not implemented and not used anywhere. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(css): add warning comment for fragile CSS selector Add a warning comment explaining that the .stMain > div > div > div:first-child selector depends on Streamlit's internal DOM structure and may break with future updates. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(graders/agent): standardize prompts to XML tag format (#108) * refactor(graders/agent): standardize prompts to XML tag format Convert all agent grader prompts to use standardized XML tags: - tool: tool_call_accuracy, tool_call_success, tool_parameter_check, tool_selection - reflection: reflection_accuracy, reflection_outcome_understanding, reflection_progress_awareness - memory: memory_accuracy, memory_detail_preservation, memory_retrieval_effectiveness - plan: plan_feasibility - action: action_alignment - trajectory: trajectory_accuracy, trajectory_comprehensive Standard tags: <Rubrics>, <Steps>, <Constraints>, <Scale>, <Output Schema> Support optional tags with (Optional) suffix for Context and History Add prompt_format_checker.py utility for validating prompt format Update format_history() in utils.py to support include_tags parameter * fix(prompt_format_checker): handle optional suffix in tag pair matching Fix _check_all_tag_pairs to correctly match tags with (Optional) or (可选) suffix. e.g., <Context (Optional)> now correctly pairs with </Context> * fix(prompt_format_checker): handle optional suffix in tag pair matching Fix _check_tag_pair to correctly match tags with (Optional) or (可选) suffix. - <Context (Optional)> now correctly pairs with </Context> - <Output Schema> still correctly pairs with </Output Schema> - <历史记录(可选)> now correctly pairs with </历史记录> * refactor(ui): simplify theme styles and improve sidebar layout (#109) - Add explicit dark theme base setting in config.toml - Remove unnecessary divider in app.py - Simplify CSS by removing aggressive header hiding styles - Adjust sidebar header positioning for better alignment - Add hover effect for collapse button - Hide deploy button and theme selector in settings dialog Co-authored-by: Cursor <cursoragent@cursor.com> * feat(ui): add single evaluation logging for analytics (#110) Add logging functionality to capture single (interactive) grader evaluations for analytics and usage tracking. Changes: - Add SingleEvaluationLogger service with JSON Lines format storage - Integrate logging into result_panel.py for both success and error cases - Log grader name, input data, results, threshold, elapsed time, and model info - Use workspace-based paths for multi-user isolation This enables operators to analyze: - Which graders are most frequently used - Common input patterns and use cases - Success/error rates by grader type - Performance metrics (evaluation time) Co-authored-by: Cursor <cursoragent@cursor.com> * feature(strategy): implement evaluation strategy (#96) * feature(strategy): implement evaluation strategy * fix: minors * fix: bugs * feat: update docker file for judge model post training (#111) * refactor(grader): refactor graders for strategy/executor (#99) * refactor(grader): refactor graders for strategy/executor * fix: minors * refactor(runner): runner for new version * fix: bugs * fix: pre-commmit bugs --------- Co-authored-by: Weidan Kong <42156564+weidankong@users.noreply.github.com> Co-authored-by: xiaobo <143202311+XiaoBoAI@users.noreply.github.com> Co-authored-by: helloml0326 <zz297429@alibaba-inc.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: XieLipeng0830 <49193414+XieLipeng0830@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: jc200808 <qiao.cai@alibaba-inc.com>
1 parent ed29f28 commit 00ae446

5 files changed

Lines changed: 68 additions & 222 deletions

File tree

openjudge/graders/agent/trajectory/trajectory_accuracy.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
from loguru import logger
1313

14+
from openjudge.evaluation_strategy.base_evaluation_strategy import (
15+
BaseEvaluationStrategy,
16+
)
1417
from openjudge.graders.base_grader import GraderError, GraderMode, GraderScore
1518
from openjudge.graders.llm_grader import LLMGrader
1619
from openjudge.models.base_chat_model import BaseChatModel
@@ -193,6 +196,7 @@ def __init__(
193196
model: BaseChatModel | dict,
194197
template: Optional[PromptTemplate] = DEFAULT_TRAJECTORY_ACCURACY_TEMPLATE,
195198
language: LanguageEnum = LanguageEnum.EN,
199+
strategy: BaseEvaluationStrategy | None = None,
196200
):
197201
"""Initialize the TrajectoryAccuracyGrader.
198202
@@ -202,6 +206,7 @@ def __init__(
202206
be used to initialize an OpenAIChatModel.
203207
template: Evaluation template. Defaults to DEFAULT_TRAJECTORY_ACCURACY_TEMPLATE.
204208
language: Language for evaluation prompts (default: LanguageEnum.EN).
209+
strategy: Evaluation strategy. Defaults to None.
205210
"""
206211
super().__init__(
207212
name="trajectory_accuracy",
@@ -210,6 +215,7 @@ def __init__(
210215
model=model,
211216
template=template or DEFAULT_TRAJECTORY_ACCURACY_TEMPLATE,
212217
language=language,
218+
strategy=strategy,
213219
)
214220

215221
def _format_messages(
@@ -255,7 +261,7 @@ def _format_messages(
255261

256262
return "\n\n".join(formatted_parts)
257263

258-
async def aevaluate(
264+
async def _aevaluate(
259265
self,
260266
messages: List[Dict[str, Any]],
261267
) -> GraderScore | GraderError:
@@ -306,7 +312,7 @@ async def aevaluate(
306312
try:
307313
# Call parent evaluate method with the structured data
308314
# The prompt will extract query and response from messages automatically
309-
result = await super().aevaluate(
315+
result = await super()._aevaluate(
310316
messages=formatted_messages,
311317
)
312318
score = max(1.0, min(3.0, result.score))

openjudge/runner/grading_runner.py

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66
organizes results by sample for further analysis.
77
88
Classes:
9-
GraderConfig: Configuration for a grader including the grader instance and data mapper.
109
RunnerResult: Result container for grading runs.
1110
GradingRunner: Main runner class for executing evaluators.
1211
"""
1312

1413
import asyncio
1514
from dataclasses import dataclass
16-
from typing import Any, Callable, Dict, List, Tuple
15+
from typing import Any, Callable, Dict, List, Tuple, Union
1716

1817
from loguru import logger
1918
from tqdm.asyncio import tqdm_asyncio
@@ -22,7 +21,12 @@
2221
from openjudge.graders.schema import GraderError, GraderResult
2322
from openjudge.runner.aggregator.base_aggregator import BaseAggregator
2423
from openjudge.runner.base_runner import BaseRunner, RunnerResult
25-
from openjudge.utils.concurrency import ConcurrencyManager
24+
from openjudge.runner.resource_executor.base_resource_executor import (
25+
BaseResourceExecutor,
26+
)
27+
from openjudge.runner.resource_executor.semaphore_resource_executor import (
28+
SemaphoreResourceExecutor,
29+
)
2630
from openjudge.utils.mapping import parse_data_with_mapper
2731

2832

@@ -147,8 +151,9 @@ def __init__(
147151
self,
148152
grader_configs: Dict[str, GraderConfig | BaseGrader | Tuple[BaseGrader, Dict[str, str] | Callable | None]],
149153
max_concurrency: int = 32,
150-
aggregators: BaseAggregator | Callable | List[BaseAggregator | Callable] | None = None,
154+
aggregators: Union[BaseAggregator, Callable, List[Union[BaseAggregator, Callable]], None] = None,
151155
show_progress: bool = True,
156+
executor: BaseResourceExecutor | None = None,
152157
) -> None:
153158
"""Initialize the grading runner.
154159
@@ -161,6 +166,8 @@ def __init__(
161166
aggregators: Optional aggregator or list of aggregators to combine results
162167
from multiple graders.
163168
show_progress: Whether to display a progress bar during execution. Defaults to True.
169+
executor: Optional execution resource to manage task execution.
170+
Defaults to LocalController if not provided.
164171
165172
Example:
166173
>>> # Initialize with multiple graders
@@ -173,8 +180,7 @@ def __init__(
173180
self.grader_configs = {name: GraderConfig.create(config) for name, config in grader_configs.items()}
174181
self.max_concurrency = max_concurrency
175182
self.show_progress = show_progress
176-
concurrency_manager = ConcurrencyManager()
177-
concurrency_manager.set_max_concurrency(max_concurrency)
183+
self.executor = executor or SemaphoreResourceExecutor(max_concurrency)
178184

179185
# Handle aggregators
180186
if not aggregators:
@@ -190,12 +196,12 @@ async def _arun(
190196
data: dict,
191197
grader: BaseGrader,
192198
mapper: Dict[str, str] | Callable | None,
199+
executor: BaseResourceExecutor,
193200
) -> GraderResult:
194201
"""Run a single evaluation asynchronously.
195202
196-
This internal method runs a single evaluation by applying the mapper to
197-
the input data and then passing the result to the grader. It handles exceptions
198-
that may occur during evaluation and wraps them in a GraderError.
203+
This internal method runs a single evaluation.
204+
It handles exceptions that may occur during evaluation and wraps them in a GraderError.
199205
200206
Args:
201207
data: Input data for the evaluation. This is typically a dictionary containing
@@ -206,6 +212,7 @@ async def _arun(
206212
- A dictionary mapping (e.g., {"input_text": "query"}) that renames fields
207213
- A callable function that takes the data dict and returns a transformed dict
208214
- None, in which case data is passed to the grader unchanged
215+
executor: Execution resource to manage the execution of the task
209216
210217
Returns:
211218
GraderResult: The result of the evaluation from the grader. This can be:
@@ -228,25 +235,21 @@ async def _arun(
228235
... }
229236
>>> result = await GradingRunner._arun(data, ContextGrader(), custom_mapper)
230237
"""
231-
concurrency_manager = ConcurrencyManager()
232-
233-
async def _evaluate(data) -> GraderResult:
234-
try:
235-
data = parse_data_with_mapper(data, mapper)
236-
return await grader.aevaluate(**data)
237-
except Exception as e:
238-
error_msg = f"Error in {grader.name} during evaluation: {str(e)}"
239-
logger.error(error_msg)
240-
return GraderError(
241-
name=grader.name,
242-
reason=f"Error in {grader.name} during evaluation",
243-
error=error_msg,
244-
)
245-
246-
# Use the concurrency manager to control execution
247-
return await concurrency_manager.run_with_concurrency_control(
248-
_evaluate(data),
249-
)
238+
try:
239+
data = parse_data_with_mapper(data, mapper)
240+
# Create an isolated grader instance for this evaluation to prevent state sharing
241+
isolated_grader = grader.copy()
242+
243+
# The grader itself handles the mapping internally
244+
return await isolated_grader.aevaluate(executor=executor, **data)
245+
except Exception as e:
246+
error_msg = f"Error in {grader.name} during evaluation: {str(e)}"
247+
logger.error(error_msg)
248+
return GraderError(
249+
name=grader.name,
250+
reason=f"Error in {grader.name} during evaluation",
251+
error=error_msg,
252+
)
250253

251254
async def arun(
252255
self,
@@ -327,6 +330,10 @@ async def arun(
327330
all_coroutines = []
328331
coroutine_info = [] # Track (grader_name, sample_index) for each coroutine
329332

333+
# Use the executor from self
334+
executor = self.executor
335+
336+
# Execute executor lifecycle
330337
for name, config in self.grader_configs.items():
331338
grader = config.grader
332339
mapper = config.mapper
@@ -335,7 +342,7 @@ async def arun(
335342
# Create coroutines for the current evaluator on all samples
336343
for i, case in enumerate(dataset):
337344
all_coroutines.append(
338-
self._arun(data=case, grader=grader, mapper=mapper),
345+
self._arun(data=case, grader=grader, mapper=mapper, executor=executor),
339346
)
340347
coroutine_info.append(
341348
(name, i),

openjudge/utils/concurrency.py

Lines changed: 0 additions & 88 deletions
This file was deleted.

tests/graders/agent/trajectory/test_trajectory_accuracy.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,6 @@ async def test_successful_evaluation_accurate_trajectory(self):
106106
assert result.score == 3.0
107107
assert "successfully" in result.reason.lower() or "achieves" in result.reason.lower()
108108

109-
# Verify model was called correctly
110-
mock_achat.assert_called_once()
111-
112109
@pytest.mark.asyncio
113110
async def test_evaluation_with_unnecessary_steps(self):
114111
"""Test evaluation detecting trajectory with unnecessary steps"""
@@ -166,9 +163,6 @@ async def test_evaluation_with_unnecessary_steps(self):
166163
assert result.score == 2.0
167164
assert "unnecessary" in result.reason.lower() or "unrelated" in result.reason.lower()
168165

169-
# Verify model was called correctly
170-
mock_achat.assert_called_once()
171-
172166
@pytest.mark.asyncio
173167
async def test_evaluation_failed_goal(self):
174168
"""Test evaluation detecting trajectory that fails to achieve goal"""
@@ -214,9 +208,6 @@ async def test_evaluation_failed_goal(self):
214208
assert result.score == 1.0
215209
assert "fail" in result.reason.lower() or "not achieve" in result.reason.lower()
216210

217-
# Verify model was called correctly
218-
mock_achat.assert_called_once()
219-
220211
@pytest.mark.asyncio
221212
async def test_empty_messages(self):
222213
"""Test error handling when messages are empty"""

0 commit comments

Comments
 (0)