| |
| |
| |
| |
| |
|
|
| """ |
| Shared serialization and deserialization utilities for OpenEnv HTTP servers. |
| |
| This module provides common utilities for converting between JSON dictionaries |
| and Pydantic models (Action/Observation) to eliminate code duplication across |
| HTTP server and web interface implementations. |
| """ |
|
|
| from typing import Any, Dict, Type |
|
|
| from .types import Action, Observation |
|
|
|
|
| def deserialize_action(action_data: Dict[str, Any], action_cls: Type[Action]) -> Action: |
| """ |
| Convert JSON dict to Action instance using Pydantic validation. |
| |
| This is a basic deserialization that works for most environments. |
| For special cases (e.g., tensor fields, custom type conversions), |
| use deserialize_action_with_preprocessing(). |
| |
| Args: |
| action_data: Dictionary containing action data |
| action_cls: The Action subclass to instantiate |
| |
| Returns: |
| Action instance |
| |
| Raises: |
| ValidationError: If action_data is invalid for the action class |
| |
| Note: |
| This uses Pydantic's model_validate() for automatic validation. |
| """ |
| return action_cls.model_validate(action_data) |
|
|
|
|
| def deserialize_action_with_preprocessing( |
| action_data: Dict[str, Any], action_cls: Type[Action] |
| ) -> Action: |
| """ |
| Convert JSON dict to Action instance with preprocessing for special types. |
| |
| This version handles common type conversions needed for web interfaces: |
| - Converting lists/strings to tensors for 'tokens' field |
| - Converting string action_id to int |
| - Other custom preprocessing as needed |
| |
| Args: |
| action_data: Dictionary containing action data |
| action_cls: The Action subclass to instantiate |
| |
| Returns: |
| Action instance |
| |
| Raises: |
| ValidationError: If action_data is invalid for the action class |
| """ |
| processed_data = {} |
|
|
| for key, value in action_data.items(): |
| if key == "tokens" and isinstance(value, (list, str)): |
| |
| if isinstance(value, str): |
| |
| try: |
| import json |
|
|
| value = json.loads(value) |
| except Exception: |
| |
| value = [] |
| if isinstance(value, list): |
| try: |
| import torch |
|
|
| processed_data[key] = torch.tensor(value, dtype=torch.long) |
| except ImportError: |
| |
| processed_data[key] = value |
| else: |
| processed_data[key] = value |
| elif key == "action_id" and isinstance(value, str): |
| |
| try: |
| processed_data[key] = int(value) |
| except ValueError: |
| |
| processed_data[key] = value |
| else: |
| processed_data[key] = value |
|
|
| return action_cls.model_validate(processed_data) |
|
|
|
|
| def serialize_observation(observation: Observation) -> Dict[str, Any]: |
| """ |
| Convert Observation instance to JSON-compatible dict using Pydantic. |
| |
| Args: |
| observation: Observation instance |
| |
| Returns: |
| Dictionary compatible with EnvClient._parse_result() |
| |
| The format matches what EnvClient expects: |
| { |
| "observation": {...}, # Observation fields |
| "reward": float | None, |
| "done": bool, |
| } |
| """ |
| |
| obs_dict = observation.model_dump( |
| exclude={ |
| "reward", |
| "done", |
| "metadata", |
| } |
| ) |
|
|
| |
| reward = observation.reward |
| done = observation.done |
|
|
| |
| return { |
| "observation": obs_dict, |
| "reward": reward, |
| "done": done, |
| } |
|
|