Skip to content

Rename TaskSelector to DataSelector.#519

Open
chenyushuo wants to merge 1 commit intoagentscope-ai:mainfrom
chenyushuo:ref/rename_task_selector_to_data_selector
Open

Rename TaskSelector to DataSelector.#519
chenyushuo wants to merge 1 commit intoagentscope-ai:mainfrom
chenyushuo:ref/rename_task_selector_to_data_selector

Conversation

@chenyushuo
Copy link
Collaborator

Description

  1. Rename TaskSelector to DataSelector
  2. Add DataSelector to ExperienceFileReader
  3. Refactor tokenize_and_mask_messages_default

Checklist

Please check the following items before code is ready to be reviewed.

  • Code has passed all tests
  • Docstrings have been added/updated in Google Style
  • Documentation has been updated
  • Code is ready for review

2. Add `DataSelector` to `ExperienceFileReader`
3. Refactor `tokenize_and_mask_messages_default`
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR renames the configuration concept of TaskSelector to DataSelector, propagates the new config through buffer readers (including experience file reading), and refactors the default assistant-token masking logic used with chat templates.

Changes:

  • Rename TaskSelectorConfig/task_selector to DataSelectorConfig/data_selector across config, validators, scheduler, selector code, and tests.
  • Add selector support to ExperienceFileReader by refactoring common dataset-reading logic into _DatasetFileReader.
  • Refactor tokenize_and_mask_messages_default to batch apply_chat_template calls when building assistant masks.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
trinity/common/models/utils.py Refactors default chat-template tokenization/masking logic.
trinity/common/config_validator.py Updates selector validation to use data_selector (incl. experience buffer validation).
trinity/common/config.py Renames selector config type and renames config fields to data_selector; adds to experience buffer config.
trinity/buffer/task_scheduler.py Updates scheduler to read selector type from data_selector.
trinity/buffer/selector/selector.py Updates selector constructors/types to accept DataSelectorConfig.
trinity/buffer/reader/file_reader.py Refactors file readers and adds selector initialization for experience file reading.
tests/trainer/trainer_test.py Updates tests to use DataSelectorConfig.
tests/buffer/task_scheduler_test.py Updates scheduler tests to use data_selector arguments/config.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +93 to +102
prompt_token_ids_list = tokenizer.apply_chat_template(
generation_messages,
add_generation_prompt=True,
**common_kwargs,
)["input_ids"]
response_token_ids_list = tokenizer.apply_chat_template(
response_messages,
add_generation_prompt=False,
**common_kwargs,
)["input_ids"]
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

tokenizer.apply_chat_template(...) is indexed with ["input_ids"], but tokenize_and_mask_messages_default no longer sets tokenize=True / return_dict=True (and also removed return_tensors). With HF tokenizers, apply_chat_template defaults to tokenize=False and will return a string, causing a runtime error here. Please explicitly request tokenization and a dict return (e.g., tokenize=True + return_dict=True) and keep the downstream code consistent with the returned type (list vs tensor).

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +97
generation_messages = []
response_messages = []
for idx, message in enumerate(messages):
if message["role"] == "assistant":
prompt_token_ids = tokenizer.apply_chat_template(
messages[:idx],
tools=tools,
chat_template=chat_template,
add_generation_prompt=True,
enable_thinking=enable_thinking,
padding=False,
truncation=True,
return_tensors="pt",
add_special_tokens=False,
return_dict=False,
)
prompt_length = prompt_token_ids.shape[1]
prompt_response_token_ids = tokenizer.apply_chat_template(
messages[: idx + 1],
tools=tools,
chat_template=chat_template,
add_generation_prompt=False,
enable_thinking=enable_thinking,
padding=False,
truncation=True,
return_tensors="pt",
add_special_tokens=False,
return_dict=False,
)
prompt_response_length = prompt_response_token_ids.shape[1]
assistant_token_mask[prompt_length:prompt_response_length] = 1
generation_messages.append(messages[:idx])
response_messages.append(messages[: idx + 1])
elif idx == len(messages) - 1:
response_messages.append(messages)
prompt_token_ids_list = tokenizer.apply_chat_template(
generation_messages,
add_generation_prompt=True,
**common_kwargs,
)["input_ids"]
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

generation_messages can be an empty list when there are no assistant turns. In that case apply_chat_template(generation_messages, ...) is ambiguous (empty conversation vs empty batch) and will not produce a per-assistant list aligned with response_messages, which can break the zip(...) logic and masking. Consider handling the no-assistant case explicitly (return all-zero mask) and/or ensuring you always pass a non-empty batch of conversations to apply_chat_template.

Copilot uses AI. Check for mistakes.
Comment on lines +120 to +122
self.selector: BaseSelector = SELECTORS.get(config.data_selector.selector_type)(
self.dataset, config.data_selector
)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

_init_selector calls SELECTORS.get(config.data_selector.selector_type)(...) without validating that the registry lookup succeeded. If selector_type is None or unsupported (or if config.check_and_update() was not run), this will raise a confusing TypeError: 'NoneType' object is not callable. Add an explicit check and raise a clear ValueError (or fall back to sequential) before instantiating the selector.

Suggested change
self.selector: BaseSelector = SELECTORS.get(config.data_selector.selector_type)(
self.dataset, config.data_selector
)
selector_type = getattr(config.data_selector, "selector_type", None)
selector_cls = SELECTORS.get(selector_type)
if selector_cls is None:
raise ValueError(
f"Unknown or missing data selector type {selector_type!r} for dataset [{self.name}]."
)
self.selector = selector_cls(self.dataset, config.data_selector)

Copilot uses AI. Check for mistakes.
Comment on lines 932 to 938
# check if selector is supported
selector = SELECTORS.get(taskset.task_selector.selector_type)
selector = SELECTORS.get(taskset.data_selector.selector_type)
if selector is None:
raise ValueError(
f"Selector {taskset.task_selector.selector_type} is not supported."
f"Selector `{taskset.data_selector.selector_type}` "
f"in {taskset.name} is not supported."
)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

This selector validation assumes taskset.data_selector is always set. However, the runtime codepath supports disabling selection by setting data_selector = None (e.g., _DatasetFileReader checks for None, and SimpleTasksetScheduler explicitly sets it to None). If a user config sets data_selector: null, this will raise an AttributeError. Please guard for None (skip validation / treat as sequential) before accessing selector_type.

Copilot uses AI. Check for mistakes.
Comment on lines +1014 to +1020
# check if selector is supported
selector = SELECTORS.get(experience_buffer.data_selector.selector_type)
if selector is None:
raise ValueError(
f"Selector {experience_buffer.data_selector.selector_type} "
"in `experience_buffer` is not supported."
)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

This validation unconditionally checks experience_buffer.data_selector.selector_type. If data_selector is explicitly set to None in config (which other components treat as a valid way to disable selection), this will crash with AttributeError. Please handle experience_buffer.data_selector is None (and/or selector_type is None) by skipping selector validation or applying a default selector type.

Suggested change
# check if selector is supported
selector = SELECTORS.get(experience_buffer.data_selector.selector_type)
if selector is None:
raise ValueError(
f"Selector {experience_buffer.data_selector.selector_type} "
"in `experience_buffer` is not supported."
)
# check if selector is supported (if configured)
data_selector = getattr(experience_buffer, "data_selector", None)
selector_type = getattr(data_selector, "selector_type", None) if data_selector is not None else None
if selector_type is not None:
selector = SELECTORS.get(selector_type)
if selector is None:
raise ValueError(
f"Selector {selector_type} in `experience_buffer` is not supported."
)

Copilot uses AI. Check for mistakes.
"""
taskset_configs = config.buffer.explorer_input.tasksets
if len(taskset_configs) == 1 and taskset_configs[0].task_selector.selector_type == "sequential":
if len(taskset_configs) == 1 and taskset_configs[0].data_selector.selector_type == "sequential":
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

get_taskset_scheduler assumes taskset_configs[0].data_selector is non-None. Since other code paths support data_selector = None to disable selection, a config that sets data_selector: null would raise an AttributeError here. Consider treating None as equivalent to sequential/no-selection in this condition.

Suggested change
if len(taskset_configs) == 1 and taskset_configs[0].data_selector.selector_type == "sequential":
data_selector = taskset_configs[0].data_selector
if len(taskset_configs) == 1 and (
data_selector is None or data_selector.selector_type == "sequential"
):

Copilot uses AI. Check for mistakes.
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.

2 participants