Robotics
Transformers
Safetensors
English
Chinese
rynn_value_lang
feature-extraction
embodied-ai
value-foundation-model
reinforcement-learning
spatiotemporal
vision-language-model
custom_code
Instructions to use Alibaba-DAMO-Academy/RynnValue-4B-Quantile with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Alibaba-DAMO-Academy/RynnValue-4B-Quantile with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Alibaba-DAMO-Academy/RynnValue-4B-Quantile", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download conversations.py from Alibaba-DAMO-Academy/RynnValue-4B-Quantile: direct link, hf CLI and curl.
- Browser
- Download file 18.9 kB
-
https://huggingface.co/Alibaba-DAMO-Academy/RynnValue-4B-Quantile/resolve/main/conversations.py
- Command line
-
hf download hf://Alibaba-DAMO-Academy/RynnValue-4B-Quantile/conversations.py
-
curl -L -o conversations.py https://huggingface.co/Alibaba-DAMO-Academy/RynnValue-4B-Quantile/resolve/main/conversations.py
18.9 kB
| # conversations.py | |
| """Conversation builders for RynnValueLangProcessor. | |
| Each builder turns ``(instruction, num_frames, ...)`` into the chat-template | |
| content list and tells the processor how many ``<value>`` / ``<relative_value>`` | |
| tokens to expect for each conversation type. Three conversation types are | |
| supported: | |
| * ``progress`` — predict remaining time to task completion via ``<value>``; | |
| optionally appends the success block. | |
| * ``relative`` — predict remaining time to a target state via per-frame | |
| ``<relative_value>``. | |
| * ``progress_relative`` — progress prompt followed by a trailing target image | |
| and a single ``<relative_value>`` prediction (multi-task supervision). | |
| The processor stays agnostic to the prompt layout — it only reads | |
| ``progress_value_count`` to align target tensors. | |
| """ | |
| from abc import ABC, abstractmethod | |
| from typing import List, Optional | |
| from PIL.Image import Image | |
| def _meta_block( | |
| robot_description: Optional[str], | |
| camera_description: Optional[str], | |
| ) -> List[dict]: | |
| sentences = [] | |
| if robot_description is not None: | |
| sentences.append(f"The agent is {robot_description}.") | |
| if camera_description is not None: | |
| sentences.append(f"The observation is captured from {camera_description}.") | |
| return [{"type": "text", "text": " ".join(sentences)}] | |
| def _analysis_block( | |
| description: Optional[str] = None, | |
| match_answer: Optional[str] = None, | |
| success_answer: Optional[str] = None, | |
| ) -> List[dict]: | |
| lines = [] | |
| if description: | |
| lines.append(f"- Video Description: {description}") | |
| if match_answer is not None: | |
| lines.append(f"- Match: {match_answer}") | |
| if success_answer is not None: | |
| lines.append(f"- Success: {success_answer}") | |
| if not lines: | |
| return [] | |
| return [ | |
| {"type": "text", "text": "Analysis: \n"}, | |
| {"type": "text", "text": "\n".join(lines)}, | |
| ] | |
| class ConversationBuilder(ABC): | |
| """Strategy object that owns the prompt layout for one processor. | |
| Subclasses implement ``build_progress`` and ``build_relative`` and expose | |
| ``progress_value_count(num_frames)`` so the processor knows how many | |
| ``<value>`` tokens each progress sample contains. | |
| """ | |
| name: str = "base" | |
| PROGRESS_QUESTION = ( | |
| "Estimate the minimum remaining time in seconds until the agent completes the task." | |
| ) | |
| RELATIVE_QUESTION = ( | |
| "Estimate the minimum remaining time in seconds until the agent reaches the target state." | |
| ) | |
| def __init__( | |
| self, | |
| value_token: str, | |
| relative_value_token: Optional[str], | |
| use_meta: bool = False, | |
| value_token_repeat: int = 1, | |
| relative_value_token_repeat: int = 1, | |
| ): | |
| self.value_token = value_token | |
| self.relative_value_token = relative_value_token | |
| self.use_meta = use_meta | |
| if value_token_repeat < 1: | |
| raise ValueError( | |
| f"value_token_repeat must be >= 1, got {value_token_repeat}" | |
| ) | |
| self.value_token_repeat = int(value_token_repeat) | |
| if relative_value_token_repeat < 1: | |
| raise ValueError( | |
| f"relative_value_token_repeat must be >= 1, got {relative_value_token_repeat}" | |
| ) | |
| self.relative_value_token_repeat = int(relative_value_token_repeat) | |
| def _maybe_meta( | |
| self, | |
| robot_description: Optional[str], | |
| camera_description: Optional[str], | |
| ) -> List[dict]: | |
| if not self.use_meta: | |
| return [] | |
| if robot_description is None and camera_description is None: | |
| raise ValueError( | |
| "use_meta=True requires at least one of `robot_description` " | |
| "or `camera_description` to be provided." | |
| ) | |
| return _meta_block(robot_description, camera_description) | |
| def _instruction_block(self, instruction: str) -> List[dict]: | |
| return [ | |
| {"type": "text", "text": f"The agent is performing the following task: {instruction}."}, | |
| ] | |
| def _append_value_tokens(self, content: List[dict]) -> None: | |
| """Append ``value_token_repeat`` copies of ``<value>``. | |
| Repeated tokens supply extra positions per prediction slot; the model | |
| mean-pools their hidden states before feeding the value head. | |
| """ | |
| for _ in range(self.value_token_repeat): | |
| content.append({"type": "text", "text": f"{self.value_token}"}) | |
| def _append_relative_value_tokens(self, content: List[dict]) -> None: | |
| """Append ``relative_value_token_repeat`` copies of ``<relative_value>``. | |
| Mirrors :meth:`_append_value_tokens` for the relative head; the model | |
| mean-pools the R repeated hidden states into one prediction per slot. | |
| """ | |
| for _ in range(self.relative_value_token_repeat): | |
| content.append({"type": "text", "text": f"{self.relative_value_token}"}) | |
| def progress_value_count(self, num_frames: int) -> int: | |
| """Number of <value> prediction slots (targets) emitted by the progress prompt. | |
| Each slot may be represented by ``value_token_repeat`` consecutive | |
| ``<value>`` tokens in the prompt; the model pools them back down to | |
| one prediction per slot, so this returns the slot/target count, not | |
| the raw token count. | |
| """ | |
| def build_progress(self, **kwargs) -> List[dict]: | |
| """Return the chat-template content list for a progress sample.""" | |
| def build_relative( | |
| self, | |
| instruction: str, | |
| num_frames: int, | |
| target_image: Image, | |
| robot_description: Optional[str], | |
| camera_description: Optional[str], | |
| ) -> List[dict]: | |
| if self.relative_value_token is None: | |
| raise ValueError( | |
| "relative conversation requires a registered <relative_value> token " | |
| "(enable relative_value_head_config). Relative mode predicts only the " | |
| "relative value (time-to-target) and never the step <value>." | |
| ) | |
| if target_image is None: | |
| raise ValueError("relative conversation requires `target_image`.") | |
| content: List[dict] = self._instruction_block(instruction) | |
| content.extend(self._maybe_meta(robot_description, camera_description)) | |
| content.extend([ | |
| {"type": "image"}, | |
| {"type": "text", "text": "The image above shows the target state the agent needs to reach."}, | |
| ]) | |
| content.append({"type": "text", "text": f"Question: {self.RELATIVE_QUESTION}"}) | |
| for _ in range(num_frames): | |
| content.append({"type": "image"}) | |
| self._append_relative_value_tokens(content) | |
| return content | |
| def build_progress_relative( | |
| self, | |
| instruction: str, | |
| num_frames: int, | |
| target_image: Image, | |
| robot_description: Optional[str], | |
| camera_description: Optional[str], | |
| ) -> List[dict]: | |
| """Progress prompt followed by a trailing relative-value prediction. | |
| Reuses :meth:`build_progress` for the prefix and appends a target image | |
| plus a single ``<relative_value>`` token, supervised against the | |
| time-to-target. Independent of the concrete progress layout, so | |
| subclasses inherit this default. | |
| """ | |
| if self.relative_value_token is None: | |
| raise ValueError( | |
| "progress_relative conversation requires a registered " | |
| "<relative_value> token (enable relative_value_head_config)." | |
| ) | |
| if target_image is None: | |
| raise ValueError("progress_relative conversation requires `target_image`.") | |
| content = self.build_progress( | |
| instruction=instruction, | |
| num_frames=num_frames, | |
| robot_description=robot_description, | |
| camera_description=camera_description, | |
| ) | |
| prediction_tokens = " ".join( | |
| [self.relative_value_token] * self.relative_value_token_repeat | |
| ) | |
| content.extend([ | |
| {"type": "image"}, | |
| {"type": "text", "text": "The image above shows the target state the agent needs to reach."}, | |
| {"type": "text", "text": f"Question: {self.RELATIVE_QUESTION}"}, | |
| {"type": "text", "text": f"Prediction: {prediction_tokens}"}, | |
| ]) | |
| return content | |
| class InterleavedConversationBuilder(ConversationBuilder): | |
| """Legacy layout: instruction + meta + question, then per-frame [img]<value>. | |
| Each frame carries its own ``<value>`` token, so progress targets are | |
| per-frame remaining times. | |
| """ | |
| name = "interleaved" | |
| def progress_value_count(self, num_frames: int) -> int: | |
| return num_frames | |
| def build_progress( | |
| self, | |
| instruction: str, | |
| num_frames: int, | |
| robot_description: Optional[str], | |
| camera_description: Optional[str], | |
| ) -> List[dict]: | |
| content: List[dict] = self._instruction_block(instruction) | |
| content.extend(self._maybe_meta(robot_description, camera_description)) | |
| content.append({"type": "text", "text": f"Question: {self.PROGRESS_QUESTION}"}) | |
| for _ in range(num_frames): | |
| content.append({"type": "image"}) | |
| self._append_value_tokens(content) | |
| return content | |
| class InterleavedLangConversationBuilder(ConversationBuilder): | |
| """Interleaved layout with language + relative distance + value prediction. | |
| Per-frame layout: ``[img] {instruction} <relative_value> <value>`` | |
| - ``{instruction}`` is the language prediction target (causal LM loss). | |
| - ``<relative_value>`` predicts the signed time delta from previous frame. | |
| - ``<value>`` predicts the remaining time to task completion. | |
| Layout: | |
| [meta] + questions + per-frame([img] instruction <relative_value> <value>) | |
| """ | |
| name = "interleaved_lang" | |
| LANG_QUESTION = "For each frame, what task is the agent performing?" | |
| RELATIVE_DISTANCE_QUESTION = "For each frame, what is the time elapsed since the previous frame?" | |
| def progress_value_count(self, num_frames: int) -> int: | |
| return num_frames | |
| def build_progress( | |
| self, | |
| instructions: List[str], | |
| num_frames: int, | |
| robot_description: Optional[str], | |
| camera_description: Optional[str], | |
| ) -> List[dict]: | |
| if len(instructions) != num_frames: | |
| raise ValueError( | |
| f"instructions length ({len(instructions)}) must equal " | |
| f"num_frames ({num_frames})." | |
| ) | |
| if self.relative_value_token is None: | |
| raise ValueError( | |
| "interleaved_lang conversation requires a registered " | |
| "<relative_value> token (enable relative_value_head_config)." | |
| ) | |
| content: List[dict] = [] | |
| content.extend(self._maybe_meta(robot_description, camera_description)) | |
| content.append({"type": "text", "text": f"Question: {self.PROGRESS_QUESTION}"}) | |
| content.append({"type": "text", "text": f"Question: {self.LANG_QUESTION}"}) | |
| content.append({"type": "text", "text": f"Question: {self.RELATIVE_DISTANCE_QUESTION}"}) | |
| for i in range(num_frames): | |
| content.append({"type": "image"}) | |
| content.append({"type": "text", "text": f"Task: {instructions[i]};"}) | |
| self._append_relative_value_tokens(content) | |
| self._append_value_tokens(content) | |
| return content | |
| class InterleavedHistoryConversationBuilder(ConversationBuilder): | |
| """History layout with per-frame value + relative-value tokens + trailing analysis. | |
| Layout:: | |
| [meta] | |
| The agent is performing the following task: {instruction}. | |
| Question: <RELATIVE_DISTANCE_QUESTION> | |
| Question: <PROGRESS_QUESTION> | |
| # per-frame, i = 0..N-1: | |
| [img] | |
| (if i > 0) <relative_value>×R | |
| <value>×R | |
| Analyze this trajectory in terms of video description, match, and success. | |
| Analysis: | |
| Video Description: {description} | |
| Match: Yes/No | |
| Success: Yes/No | |
| """ | |
| name = "interleaved_history" | |
| RELATIVE_DISTANCE_QUESTION = ( | |
| "For each frame after the first, what is the time delta from the previous frame?" | |
| ) | |
| def _build_analysis_prompt(has_description: bool) -> str: | |
| lines = ["Analyze this trajectory. Provide:"] | |
| if has_description: | |
| lines.append("- Video Description: a brief description of what the agent is doing in the video.") | |
| lines.append("- Match: whether the video matches the stated task (Yes/No).") | |
| lines.append("- Success: whether the agent has completed the task (Yes/No).") | |
| return "\n".join(lines) | |
| def progress_value_count(self, num_frames: int) -> int: | |
| return num_frames | |
| def build_progress( | |
| self, | |
| instruction: str, | |
| description: Optional[str], | |
| num_frames: int, | |
| robot_description: Optional[str], | |
| camera_description: Optional[str], | |
| match_answer: Optional[str] = None, | |
| success_answer: Optional[str] = None, | |
| is_inference: bool = False, | |
| ) -> List[dict]: | |
| use_relative = self.relative_value_token is not None | |
| content: List[dict] = [] | |
| content.extend(self._maybe_meta(robot_description, camera_description)) | |
| content.extend(self._instruction_block(instruction)) | |
| if use_relative: | |
| content.append({"type": "text", "text": f"Question: {self.RELATIVE_DISTANCE_QUESTION}"}) | |
| content.append({"type": "text", "text": f"Question: {self.PROGRESS_QUESTION}"}) | |
| for i in range(num_frames): | |
| content.append({"type": "image"}) | |
| if i > 0 and use_relative: | |
| self._append_relative_value_tokens(content) | |
| self._append_value_tokens(content) | |
| # Inference: always include all questions so the model sees full context. | |
| # Training: only include description question when description data exists. | |
| prompt_has_description = True if is_inference else bool(description) | |
| analysis_prompt = self._build_analysis_prompt(has_description=prompt_has_description) | |
| analysis = _analysis_block( | |
| description=description, | |
| match_answer=match_answer, | |
| success_answer=success_answer, | |
| ) | |
| if not analysis and is_inference: | |
| # Inference: emit the analysis prompt questions together with | |
| # the "Analysis: \n" marker so generation has full context. | |
| content.append({"type": "text", "text": analysis_prompt}) | |
| analysis = [{"type": "text", "text": "Analysis: \n"}] | |
| else: | |
| content.append({"type": "text", "text": analysis_prompt}) | |
| content.extend(analysis) | |
| return content | |
| class InterleavedHistoryNormalizedConversationBuilder(InterleavedHistoryConversationBuilder): | |
| """Same layout as :class:`InterleavedHistoryConversationBuilder`, but the | |
| prompt asks for a normalized [0, 1] completion progress instead of the raw | |
| remaining time in seconds. | |
| Only the question strings differ; the frame / token layout, value counts and | |
| analysis block are inherited unchanged, so the model still emits one | |
| ``<value>`` per frame and ``<relative_value>`` between frames — the targets | |
| are just normalized upstream in the processor. | |
| """ | |
| name = "interleaved_history_normalized" | |
| PROGRESS_QUESTION = ( | |
| "Estimate the task completion progress as a value between 0 and 1, where " | |
| "0 means the task has just started and 1 means the task is complete." | |
| ) | |
| RELATIVE_DISTANCE_QUESTION = ( | |
| "For each frame after the first, what fraction of the total task time has " | |
| "elapsed since the previous frame?" | |
| ) | |
| class VideoPrefixConversationBuilder(ConversationBuilder): | |
| """New layout: meta + "this is a video" preface + frames (no value tokens), | |
| then task description + question + a single trailing ``<value>`` token, | |
| optionally followed by the success block. | |
| Progress targets become a single anchor (last-frame) remaining time. | |
| """ | |
| name = "video_prefix" | |
| INTRO_TEXT = "The following is a video of the agent completing a manipulation task." | |
| def progress_value_count(self, num_frames: int) -> int: | |
| return 1 | |
| def build_progress( | |
| self, | |
| instruction: str, | |
| num_frames: int, | |
| robot_description: Optional[str], | |
| camera_description: Optional[str], | |
| ) -> List[dict]: | |
| if self.relative_value_token is None: | |
| raise ValueError( | |
| "video_prefix conversation requires a registered " | |
| "<relative_value> token (enable relative_value_head_config)." | |
| ) | |
| content: List[dict] = [] | |
| content.extend(self._maybe_meta(robot_description, camera_description)) | |
| content.append({"type": "text", "text": self.INTRO_TEXT}) | |
| for _ in range(num_frames): | |
| content.append({"type": "image"}) | |
| self._append_relative_value_tokens(content) | |
| content.append({"type": "text", "text": f"The task is: {instruction}."}) | |
| content.append({"type": "text", "text": f"Question: {self.PROGRESS_QUESTION}"}) | |
| prediction_tokens = " ".join([self.value_token] * self.value_token_repeat) | |
| content.append({"type": "text", "text": f"Prediction: {prediction_tokens}"}) | |
| return content | |
| _REGISTRY = { | |
| InterleavedConversationBuilder.name: InterleavedConversationBuilder, | |
| InterleavedLangConversationBuilder.name: InterleavedLangConversationBuilder, | |
| InterleavedHistoryConversationBuilder.name: InterleavedHistoryConversationBuilder, | |
| InterleavedHistoryNormalizedConversationBuilder.name: InterleavedHistoryNormalizedConversationBuilder, | |
| VideoPrefixConversationBuilder.name: VideoPrefixConversationBuilder, | |
| } | |
| def build_conversation_builder( | |
| style: str, | |
| *, | |
| value_token: str, | |
| relative_value_token: Optional[str], | |
| use_meta: bool = False, | |
| value_token_repeat: int = 1, | |
| relative_value_token_repeat: int = 1, | |
| ) -> ConversationBuilder: | |
| if style not in _REGISTRY: | |
| raise ValueError( | |
| f"Unknown conversation_style: {style!r}. " | |
| f"Expected one of {sorted(_REGISTRY)}." | |
| ) | |
| return _REGISTRY[style]( | |
| value_token=value_token, | |
| relative_value_token=relative_value_token, | |
| use_meta=use_meta, | |
| value_token_repeat=value_token_repeat, | |
| relative_value_token_repeat=relative_value_token_repeat, | |
| ) | |
| __all__ = [ | |
| "ConversationBuilder", | |
| "InterleavedConversationBuilder", | |
| "InterleavedLangConversationBuilder", | |
| "InterleavedHistoryConversationBuilder", | |
| "InterleavedHistoryNormalizedConversationBuilder", | |
| "VideoPrefixConversationBuilder", | |
| "build_conversation_builder", | |
| ] | |