-
Notifications
You must be signed in to change notification settings - Fork 4.3k
[rollout, trainer] feat: extend agent loop for custom implementations #4548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
8775425
e3dc8e8
4b62ab5
3b00d00
85f1d62
8560165
e77999b
58315a2
b879412
687fb36
7c26cac
9039efe
2b08e0c
c8a4b99
3b78336
36c1d5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| # Copyright 2025 Bytedance Ltd. and/or its affiliates | ||
| import logging | ||
| import os | ||
| from typing import Any | ||
|
|
||
| from jinja2 import TemplateError | ||
|
|
||
|
|
@@ -40,3 +41,84 @@ def extract_system_prompt_and_generation(tokenizer): | |
| generate_prompt = token3[len(token1) :] | ||
|
|
||
| return system_prompt, generate_prompt | ||
|
|
||
|
|
||
| # ============================================================================= | ||
| # Tokenization utility functions | ||
| # These functions are extracted from ToolAgentLoop for reusability in external | ||
| # projects like remote rollout servers. | ||
| # ============================================================================= | ||
|
|
||
|
|
||
| def apply_chat_template_with_processor( | ||
| processor, | ||
| messages: list[dict[str, Any]], | ||
| *, | ||
| tools: list[dict[str, Any]] | None = None, | ||
| add_generation_prompt: bool = True, | ||
| apply_chat_template_kwargs: dict[str, Any] | None = None, | ||
| ) -> str: | ||
| """Apply chat template via processor (tokenize=False). | ||
|
|
||
| NOTE: To preserve ToolAgentLoop's original behavior, callers should decide | ||
| whether this runs in an executor or on the event loop thread. | ||
| """ | ||
| if apply_chat_template_kwargs is None: | ||
| apply_chat_template_kwargs = {} | ||
| if tools is None: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a bit redundant here, we can safely pass
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reverted! |
||
| return processor.apply_chat_template( | ||
| messages, | ||
| add_generation_prompt=add_generation_prompt, | ||
| tokenize=False, | ||
| **apply_chat_template_kwargs, | ||
| ) | ||
| return processor.apply_chat_template( | ||
| messages, | ||
| tools=tools, | ||
| add_generation_prompt=add_generation_prompt, | ||
| tokenize=False, | ||
| **apply_chat_template_kwargs, | ||
| ) | ||
|
|
||
|
|
||
| def apply_chat_template_with_tokenizer( | ||
| tokenizer, | ||
| messages: list[dict[str, Any]], | ||
| *, | ||
| tools: list[dict[str, Any]] | None = None, | ||
| add_generation_prompt: bool = True, | ||
| apply_chat_template_kwargs: dict[str, Any] | None = None, | ||
| ) -> list[int]: | ||
| """Apply chat template via tokenizer (tokenize=True). | ||
|
|
||
| IMPORTANT: Some call sites intentionally do NOT pass any extra kwargs | ||
| (e.g. incremental tool/user message tokenization). When no kwargs are needed, | ||
| simply omit the apply_chat_template_kwargs parameter. | ||
| """ | ||
| if apply_chat_template_kwargs is None: | ||
| apply_chat_template_kwargs = {} | ||
| if tools is None: | ||
| return tokenizer.apply_chat_template( | ||
| messages, | ||
| add_generation_prompt=add_generation_prompt, | ||
| tokenize=True, | ||
| **apply_chat_template_kwargs, | ||
| ) | ||
| return tokenizer.apply_chat_template( | ||
| messages, | ||
| tools=tools, | ||
| add_generation_prompt=add_generation_prompt, | ||
| tokenize=True, | ||
| **apply_chat_template_kwargs, | ||
| ) | ||
|
|
||
|
|
||
| def tokenize_with_processor( | ||
| processor, | ||
| *, | ||
| raw_prompt: str, | ||
| image_data: Any | None, | ||
| ) -> list[int]: | ||
| """Tokenize a pre-rendered prompt string via processor.""" | ||
| model_inputs = processor(text=[raw_prompt], images=image_data, return_tensors="pt") | ||
| return model_inputs.pop("input_ids").squeeze(0).tolist() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,6 +71,7 @@ class AgentLoopConfig(BaseConfig): | |
| default_agent_loop: str = "single_turn_agent" | ||
| agent_loop_config_path: Optional[str] = None | ||
| custom_async_server: CustomAsyncServerConfig = field(default_factory=CustomAsyncServerConfig) | ||
| agent_loop_manager_class: Optional[str] = None | ||
|
|
||
|
|
||
| @dataclass | ||
|
|
@@ -179,6 +180,9 @@ class RolloutConfig(BaseConfig): | |
| # Use Prometheus to collect and monitor rollout statistics | ||
| prometheus: PrometheusConfig = field(default_factory=PrometheusConfig) | ||
|
|
||
| # Extension point for custom configurations | ||
| custom: Optional[dict] = None | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This field seems not used?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This field is designed as an extension point for downstream projects to pass custom configurations without modifying core config classes. For example, a project implementing remote rollout could use: actor_rollout_ref:
rollout:
custom:
remote_rollout:
server_url: "https://..."
callback_url: "http://..."
timeout_seconds: 300Then access it via Happy to add a docstring clarifying this is an extension point! |
||
|
|
||
| update_weights_bucket_megabytes: int = 512 | ||
|
|
||
| skip_rollout: bool = False | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this to a util function
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion!
I've refactored this by adding a
load_class_from_fqn()utility function toverl/utils/import_utils.py. This function handles FQN parsing, dynamic import, and provides clear error messages. Commit: 3b78336The code in
ray_trainer.pyis now simplified from ~20 lines to just 4 lines:This also removes the now-unused import importlib from the file.