Download server/ppt_agent_environment.py from Berlm/ppt_agent: direct link, hf CLI and curl.
- Browser
- Download file 28.7 kB
-
https://huggingface.co/spaces/Berlm/ppt_agent/resolve/main/server/ppt_agent_environment.py
- Command line
-
hf download hf://spaces/Berlm/ppt_agent/server/ppt_agent_environment.py
-
curl -L -o ppt_agent_environment.py https://huggingface.co/spaces/Berlm/ppt_agent/resolve/main/server/ppt_agent_environment.py
28.7 kB
| from __future__ import annotations | |
| import logging | |
| import random | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Any | |
| from uuid import uuid4 | |
| from openenv.core.env_server.interfaces import Environment | |
| from openenv.core.env_server.types import State | |
| from ..models import PptAgentAction, PptAgentObservation | |
| from .debug_logging import debug_context, write_debug_event | |
| from .llm_client import LLMClient | |
| from .task_registry import DEFAULT_TASK_REGISTRY, TaskRegistry, TaskScenario | |
| from .tools.pptx_tools import ( | |
| create_slide, | |
| delete_slide, | |
| register_theme, | |
| save_presentation, | |
| update_slide, | |
| update_theme, | |
| ) | |
| from .utils.pptx_extraction import PptxExtractionService | |
| from .utils.pptx_functions import PptxEditor | |
| from .utils.rendering.pptx_render_service import PptxRenderService | |
| from .utils.reward_kernel import ( | |
| build_eval_spec, | |
| evaluate_presentation, | |
| evaluate_slide, | |
| ) | |
| from .utils.reward_metrics import clamp_reward | |
| from .utils.reward_models import EvalSpec, ExtractedPresentation, to_serializable | |
| from .utils.slidesgenbench import ( | |
| QuantitativeQuizJudgeService, | |
| QuizBankGenerationService, | |
| SlidesGenQuantitativeJudgeService, | |
| SlidesGenQuizBankService, | |
| ) | |
| _DEFAULT_MAX_STEPS = 30 | |
| _INVALID_ACTION_PENALTY = 0.0 | |
| logger = logging.getLogger(__name__) | |
| class _ActionExecutionResult: | |
| action_type: str | |
| tool_result: dict[str, Any] | |
| affected_slide_index: int | None = None | |
| reward: float = 0.0 | |
| class PptAgentEnvironment(Environment): | |
| """Prompt-to-PPT environment backed by ``PptxEditor`` and reward kernel.""" | |
| SUPPORTS_CONCURRENT_SESSIONS: bool = True | |
| def __init__( | |
| self, | |
| max_steps: int = _DEFAULT_MAX_STEPS, | |
| *, | |
| task_registry: TaskRegistry | None = None, | |
| quiz_bank_service: QuizBankGenerationService | None = None, | |
| quantitative_quiz_judge_service: QuantitativeQuizJudgeService | None = None, | |
| render_service: Any | None = None, | |
| ): | |
| super().__init__() | |
| self._max_steps = max_steps | |
| self._rng = random.Random() | |
| self._inspection_service = PptxExtractionService() | |
| self._task_registry = task_registry or DEFAULT_TASK_REGISTRY | |
| self._scenario_queue: list[TaskScenario] = [] | |
| self._eval_spec_cache: dict[str, EvalSpec] = {} | |
| llm_client: LLMClient | None = None | |
| if quiz_bank_service is None or quantitative_quiz_judge_service is None: | |
| llm_client = LLMClient() | |
| self._quiz_bank_service = quiz_bank_service or SlidesGenQuizBankService( | |
| llm_client | |
| ) | |
| self._quantitative_quiz_judge_service = ( | |
| quantitative_quiz_judge_service | |
| or SlidesGenQuantitativeJudgeService(llm_client) | |
| ) | |
| self._render_service = render_service or PptxRenderService() | |
| self._state = State(episode_id=str(uuid4()), step_count=0) | |
| self._done = False | |
| self._termination_reason: str | None = None | |
| self._scenario: TaskScenario | None = None | |
| self._eval_spec: EvalSpec | None = None | |
| self._editor: PptxEditor | None = None | |
| self._last_score = 0.0 | |
| self._last_action_result: dict[str, Any] | None = None | |
| self._last_reward_details: dict[str, Any] | None = None | |
| self._known_named_shapes_by_slide: dict[int, dict[str, int]] = {} | |
| def reset( | |
| self, | |
| seed: int | None = None, | |
| episode_id: str | None = None, | |
| **kwargs: Any, | |
| ) -> PptAgentObservation: | |
| requested_task_id = kwargs.get("task_id") | |
| requested_difficulty = kwargs.get("difficulty") | |
| next_episode_id = episode_id or str(uuid4()) | |
| with debug_context(episode_id=next_episode_id): | |
| logger.info("env.reset start episode_id=%s seed=%s", next_episode_id, seed) | |
| write_debug_event( | |
| "env.reset.start", | |
| { | |
| "seed": seed, | |
| "requested_task_id": requested_task_id, | |
| "requested_difficulty": requested_difficulty, | |
| }, | |
| ) | |
| self._reset_rubric() | |
| if seed is not None: | |
| self._rng.seed(seed) | |
| self._scenario_queue = [] | |
| self._done = False | |
| self._termination_reason = None | |
| self._last_score = 0.0 | |
| self._last_action_result = None | |
| self._last_reward_details = None | |
| self._known_named_shapes_by_slide = {} | |
| self._scenario = self._resolve_scenario( | |
| task_id=requested_task_id, | |
| difficulty=requested_difficulty, | |
| ) | |
| with debug_context( | |
| task_id=self._scenario.task_id, | |
| difficulty=self._scenario.difficulty, | |
| ): | |
| logger.info("env.reset sampled task task_id=%s", self._scenario.task_id) | |
| write_debug_event( | |
| "env.reset.scenario", | |
| { | |
| "task_id": self._scenario.task_id, | |
| "difficulty": self._scenario.difficulty, | |
| }, | |
| ) | |
| self._eval_spec = self._build_eval_spec(self._scenario) | |
| self._editor = self._initialize_editor(self._scenario.theme) | |
| self._state = State(episode_id=next_episode_id, step_count=0) | |
| logger.info( | |
| "env.reset ready episode_id=%s task_id=%s max_steps=%s", | |
| self._state.episode_id, | |
| self._scenario.task_id, | |
| self._max_steps, | |
| ) | |
| write_debug_event( | |
| "env.reset.ready", | |
| { | |
| "max_steps": self._max_steps, | |
| "spec_hash": self._eval_spec.spec_hash, | |
| }, | |
| ) | |
| return self._build_observation( | |
| reward=0.0, | |
| done=False, | |
| last_action_error=None, | |
| ) | |
| def step( | |
| self, | |
| action: PptAgentAction, | |
| timeout_s: float | None = None, | |
| **kwargs: Any, | |
| ) -> PptAgentObservation: | |
| del timeout_s, kwargs | |
| if self._editor is None or self._scenario is None or self._eval_spec is None: | |
| raise RuntimeError("Environment must be reset before step()") | |
| if self._done: | |
| return self._build_observation( | |
| reward=0.0, | |
| done=True, | |
| last_action_error="Episode already terminated", | |
| ) | |
| with debug_context( | |
| episode_id=self._state.episode_id, | |
| task_id=self._scenario.task_id, | |
| difficulty=self._scenario.difficulty, | |
| ): | |
| self._state.step_count += 1 | |
| logger.info( | |
| "env.step start episode_id=%s step=%s action_type=%s slide_index=%s", | |
| self._state.episode_id, | |
| self._state.step_count, | |
| action.action_type, | |
| action.slide_index, | |
| ) | |
| write_debug_event( | |
| "env.step.start", | |
| { | |
| "step": self._state.step_count, | |
| "action": action.model_dump(mode="json"), | |
| }, | |
| ) | |
| reward = 0.0 | |
| invalid_reason: str | None = None | |
| inspection: ExtractedPresentation | None = None | |
| try: | |
| execution_result = self._execute_action(action) | |
| self._update_known_named_shapes(action, execution_result) | |
| self._last_action_result = { | |
| "action_type": execution_result.action_type, | |
| "slide_index": self._result_slide_index(action, execution_result), | |
| "tool_result": execution_result.tool_result, | |
| } | |
| write_debug_event( | |
| "action.executed", | |
| { | |
| "step": self._state.step_count, | |
| "action_type": execution_result.action_type, | |
| "tool_result": execution_result.tool_result, | |
| "affected_slide_index": execution_result.affected_slide_index, | |
| }, | |
| ) | |
| if execution_result.affected_slide_index is not None: | |
| inspection = self._inspection_service.inspect_presentation( | |
| self._editor | |
| ) | |
| intermediate_result = self._score_intermediate_step( | |
| slide_index=execution_result.affected_slide_index, | |
| inspection=inspection, | |
| ) | |
| reward = clamp_reward(intermediate_result.reward_total) | |
| self._last_reward_details = { | |
| "kind": "intermediate_slide", | |
| "result": { | |
| **to_serializable(intermediate_result), | |
| "reward_total": reward, | |
| }, | |
| } | |
| write_debug_event( | |
| "reward.intermediate", | |
| { | |
| "step": self._state.step_count, | |
| "reward_result": to_serializable(intermediate_result), | |
| }, | |
| ) | |
| else: | |
| reward = clamp_reward(execution_result.reward) | |
| self._last_reward_details = { | |
| "kind": "action", | |
| "result": {"reward_total": reward}, | |
| } | |
| write_debug_event( | |
| "reward.action", | |
| { | |
| "step": self._state.step_count, | |
| "reward_total": reward, | |
| }, | |
| ) | |
| logger.info( | |
| "env.step action complete episode_id=%s step=%s action_type=%s reward=%s tool_result=%s", | |
| self._state.episode_id, | |
| self._state.step_count, | |
| action.action_type, | |
| reward, | |
| execution_result.tool_result, | |
| ) | |
| except (IndexError, KeyError, ValueError) as error: | |
| invalid_reason = str(error) | |
| reward = clamp_reward(_INVALID_ACTION_PENALTY) | |
| self._last_action_result = None | |
| self._last_reward_details = { | |
| "kind": "invalid_action", | |
| "result": {"reward_total": reward, "error": invalid_reason}, | |
| } | |
| write_debug_event( | |
| "env.step.invalid_action", | |
| { | |
| "step": self._state.step_count, | |
| "action": action.model_dump(mode="json"), | |
| "error": invalid_reason, | |
| "reward_total": reward, | |
| }, | |
| ) | |
| logger.warning( | |
| "env.step invalid action episode_id=%s step=%s action_type=%s error=%s", | |
| self._state.episode_id, | |
| self._state.step_count, | |
| action.action_type, | |
| invalid_reason, | |
| ) | |
| termination_reason = self._should_terminate() | |
| reward = clamp_reward(float(reward)) | |
| if termination_reason is not None: | |
| reward = self._finalize_episode(termination_reason) | |
| logger.info( | |
| "env.step end episode_id=%s step=%s done=%s reward=%s score=%s error=%s", | |
| self._state.episode_id, | |
| self._state.step_count, | |
| self._done, | |
| reward, | |
| self._last_score, | |
| invalid_reason, | |
| ) | |
| write_debug_event( | |
| "env.step.end", | |
| { | |
| "step": self._state.step_count, | |
| "done": self._done, | |
| "reward": reward, | |
| "score": self._last_score, | |
| "error": invalid_reason, | |
| "termination_reason": self._termination_reason, | |
| }, | |
| ) | |
| return self._build_observation( | |
| reward=reward, | |
| done=self._done, | |
| last_action_error=invalid_reason, | |
| inspection=inspection, | |
| ) | |
| def state(self) -> State: | |
| slide_count = len(self._editor.prs.slides) if self._editor is not None else 0 | |
| return State( | |
| episode_id=self._state.episode_id, | |
| step_count=self._state.step_count, | |
| done=self._done, | |
| slide_count=slide_count, | |
| max_steps=self._max_steps, | |
| task_name=self._current_task_name(), | |
| score=self._last_score, | |
| ) | |
| def _sample_scenario(self) -> TaskScenario: | |
| if not self._scenario_queue: | |
| self._scenario_queue = list(self._task_registry.all()) | |
| self._rng.shuffle(self._scenario_queue) | |
| return self._scenario_queue.pop(0) | |
| def _resolve_scenario( | |
| self, | |
| *, | |
| task_id: Any | None, | |
| difficulty: Any | None, | |
| ) -> TaskScenario: | |
| if task_id is not None: | |
| return self._task_registry.get(str(task_id)) | |
| if difficulty is not None: | |
| return self._task_registry.sample(self._rng, difficulty=str(difficulty)) | |
| return self._sample_scenario() | |
| def _build_eval_spec(self, scenario: TaskScenario) -> EvalSpec: | |
| cached_eval_spec = self._eval_spec_cache.get(scenario.task_id) | |
| if cached_eval_spec is not None: | |
| logger.info("env.eval_spec cache hit task_id=%s", scenario.task_id) | |
| return cached_eval_spec | |
| logger.info("env.eval_spec build start task_id=%s", scenario.task_id) | |
| eval_spec = build_eval_spec( | |
| scenario.prompt_text, | |
| scenario.source_pack, | |
| scenario.task_constraints, | |
| quiz_bank_service=self._quiz_bank_service, | |
| ) | |
| self._eval_spec_cache[scenario.task_id] = eval_spec | |
| logger.info( | |
| "env.eval_spec build complete task_id=%s question_count=%s spec_hash=%s", | |
| scenario.task_id, | |
| len(eval_spec.slidesgenbench.quiz_bank), | |
| eval_spec.spec_hash, | |
| ) | |
| return eval_spec | |
| def _initialize_editor(self, theme: dict[str, Any]) -> PptxEditor: | |
| editor = PptxEditor() | |
| register_theme(editor, theme) | |
| return editor | |
| def _build_observation( | |
| self, | |
| *, | |
| reward: float, | |
| done: bool, | |
| last_action_error: str | None, | |
| inspection: ExtractedPresentation | None = None, | |
| ) -> PptAgentObservation: | |
| if self._editor is None: | |
| raise RuntimeError("Environment must be reset before building observations") | |
| inspection = inspection or self._inspection_service.inspect_presentation( | |
| self._editor | |
| ) | |
| last_action_result = ( | |
| { | |
| **self._last_action_result, | |
| "reward_details": self._last_reward_details or {}, | |
| } | |
| if self._last_action_result is not None | |
| else None | |
| ) | |
| return PptAgentObservation( | |
| task_name=self._current_task_name(), | |
| difficulty=self._difficulty(), | |
| slide_count=inspection.slide_count, | |
| task_prompt=self._task_prompt(), | |
| source_context=self._source_context(), | |
| last_action_error=last_action_error, | |
| score=self._last_score, | |
| prompt_summary=self._prompt_summary(), | |
| last_action_result=last_action_result, | |
| termination_reason=self._termination_reason, | |
| done=done, | |
| reward=reward, | |
| metadata={ | |
| "reward_details": self._last_reward_details or {}, | |
| "current_theme": self._editor.get_theme(), | |
| "known_named_shapes_by_slide": { | |
| slide_index: dict(named_shapes) | |
| for slide_index, named_shapes in self._known_named_shapes_by_slide.items() | |
| }, | |
| "slide_constraints": self._slide_constraints(), | |
| "default_save_path": self._default_output_path(), | |
| "max_steps": self._max_steps, | |
| "step_count": self._state.step_count, | |
| }, | |
| ) | |
| def _current_task_name(self) -> str: | |
| return self._scenario.task_id if self._scenario is not None else "" | |
| def _prompt_summary(self) -> str: | |
| if self._scenario is None: | |
| return "" | |
| prompt = self._scenario.prompt_text.strip() | |
| return prompt if len(prompt) <= 220 else f"{prompt[:217]}..." | |
| def _task_prompt(self) -> str: | |
| if self._scenario is None: | |
| return "" | |
| return self._scenario.prompt_text | |
| def _source_context(self) -> str: | |
| if self._scenario is None: | |
| return "" | |
| sections: list[str] = [] | |
| if self._scenario.source_pack.brief: | |
| sections.append(f"[Source Pack Brief]\n{self._scenario.source_pack.brief}") | |
| for document in self._scenario.source_pack.documents: | |
| title = document.title.strip() if document.title else document.doc_id | |
| if document.pages: | |
| body = "\n\n".join( | |
| f"Page {page_index}:\n{page_text.strip()}" | |
| for page_index, page_text in enumerate(document.pages, start=1) | |
| if page_text and page_text.strip() | |
| ) | |
| else: | |
| body_parts = [document.text] if document.text else [] | |
| body = "\n".join( | |
| part.strip() for part in body_parts if part and part.strip() | |
| ) | |
| if body: | |
| sections.append(f"[{title} | {document.doc_id}]\n{body}") | |
| return "\n\n".join(sections) | |
| def _difficulty(self) -> str: | |
| if self._scenario is None: | |
| return "" | |
| return self._scenario.difficulty | |
| def _execute_action(self, action: PptAgentAction) -> _ActionExecutionResult: | |
| handler = getattr(self, f"_handle_{action.action_type.lower()}", None) | |
| if handler is None: | |
| raise ValueError(f"Unsupported action type '{action.action_type}'") | |
| return handler(action) | |
| def _resolve_slide_id(self, action: PptAgentAction) -> int: | |
| if self._editor is None: | |
| raise RuntimeError("Environment must be reset before resolving slide ids") | |
| if action.slide_index is None: | |
| raise ValueError(f"{action.action_type} requires slide_index") | |
| return self._editor.get_slide_id(action.slide_index - 1) | |
| def _handle_create_slide(self, action: PptAgentAction) -> _ActionExecutionResult: | |
| if self._editor is None: | |
| raise RuntimeError("Environment must be reset before create_slide") | |
| payload = dict(action.payload) | |
| tool_result = create_slide( | |
| self._editor, | |
| background_color=payload.get("background_color", "<surface>"), | |
| shapes=payload.get("shapes") or [], | |
| ) | |
| return _ActionExecutionResult( | |
| action_type=action.action_type, | |
| tool_result=tool_result, | |
| affected_slide_index=len(self._editor.prs.slides), | |
| ) | |
| def _handle_update_slide(self, action: PptAgentAction) -> _ActionExecutionResult: | |
| if action.slide_index is None: | |
| raise ValueError("update_slide requires slide_index") | |
| slide_id = self._resolve_slide_id(action) | |
| payload = dict(action.payload) | |
| tool_result = update_slide( | |
| self._editor, | |
| slide_id, | |
| background_color=payload.get("background_color"), | |
| delete_shape_ids=payload.get("delete_shape_ids") or [], | |
| add_shapes=payload.get("add_shapes") or [], | |
| update_shapes=payload.get("update_shapes") or [], | |
| ) | |
| return _ActionExecutionResult( | |
| action_type=action.action_type, | |
| tool_result=tool_result, | |
| affected_slide_index=action.slide_index, | |
| ) | |
| def _handle_delete_slide(self, action: PptAgentAction) -> _ActionExecutionResult: | |
| if self._editor is None: | |
| raise RuntimeError("Environment must be reset before delete_slide") | |
| slide_id = self._resolve_slide_id(action) | |
| tool_result = delete_slide(self._editor, slide_id) | |
| return _ActionExecutionResult( | |
| action_type=action.action_type, | |
| tool_result=tool_result, | |
| affected_slide_index=None, | |
| reward=0.0, | |
| ) | |
| def _handle_save_presentation( | |
| self, action: PptAgentAction | |
| ) -> _ActionExecutionResult: | |
| if self._editor is None: | |
| raise RuntimeError("Environment must be reset before save_presentation") | |
| payload = dict(action.payload) | |
| tool_result = save_presentation( | |
| self._editor, | |
| payload.get("path") or self._default_output_path(), | |
| ) | |
| return _ActionExecutionResult( | |
| action_type=action.action_type, | |
| tool_result=tool_result, | |
| affected_slide_index=None, | |
| reward=0.0, | |
| ) | |
| def _handle_set_theme(self, action: PptAgentAction) -> _ActionExecutionResult: | |
| if self._editor is None: | |
| raise RuntimeError("Environment must be reset before set_theme") | |
| payload = dict(action.payload) | |
| update_theme(self._editor, payload) | |
| return _ActionExecutionResult( | |
| action_type=action.action_type, | |
| tool_result={"theme": self._editor.get_theme()}, | |
| affected_slide_index=None, | |
| reward=0.0, | |
| ) | |
| def _default_output_path(self) -> str: | |
| task_name = self._current_task_name() or "presentation" | |
| episode_id = self._state.episode_id or str(uuid4()) | |
| return str(Path("outputs") / f"{task_name}_{episode_id}.pptx") | |
| def _slide_constraints(self) -> dict[str, int]: | |
| if self._eval_spec is None: | |
| return {} | |
| task_spec = self._eval_spec.task_spec | |
| constraints: dict[str, int] = {} | |
| if isinstance(task_spec.min_slides, int): | |
| constraints["min_slides"] = task_spec.min_slides | |
| if isinstance(task_spec.max_slides, int): | |
| constraints["max_slides"] = task_spec.max_slides | |
| return constraints | |
| def _result_slide_index( | |
| self, action: PptAgentAction, execution_result: _ActionExecutionResult | |
| ) -> int | None: | |
| if execution_result.action_type == "create_slide": | |
| return execution_result.affected_slide_index | |
| return action.slide_index | |
| def _update_known_named_shapes( | |
| self, action: PptAgentAction, execution_result: _ActionExecutionResult | |
| ) -> None: | |
| tool_result = execution_result.tool_result | |
| named_shapes = tool_result.get("named_shapes") | |
| normalized_named_shapes = { | |
| name: shape_id | |
| for name, shape_id in (named_shapes or {}).items() | |
| if isinstance(name, str) and isinstance(shape_id, int) | |
| } | |
| if execution_result.action_type == "create_slide": | |
| slide_index = execution_result.affected_slide_index | |
| if isinstance(slide_index, int): | |
| self._known_named_shapes_by_slide[slide_index] = dict( | |
| normalized_named_shapes | |
| ) | |
| return | |
| if execution_result.action_type == "update_slide": | |
| slide_index = action.slide_index | |
| if not isinstance(slide_index, int): | |
| return | |
| current = dict(self._known_named_shapes_by_slide.get(slide_index, {})) | |
| deleted_shape_ids = { | |
| shape_id | |
| for shape_id in tool_result.get("deleted_shape_ids") or [] | |
| if isinstance(shape_id, int) | |
| } | |
| if deleted_shape_ids: | |
| current = { | |
| name: shape_id | |
| for name, shape_id in current.items() | |
| if shape_id not in deleted_shape_ids | |
| } | |
| current.update(normalized_named_shapes) | |
| self._known_named_shapes_by_slide[slide_index] = current | |
| return | |
| if execution_result.action_type == "delete_slide": | |
| slide_index = action.slide_index | |
| if not isinstance(slide_index, int): | |
| return | |
| reindexed: dict[int, dict[str, int]] = {} | |
| for ( | |
| existing_index, | |
| existing_shapes, | |
| ) in self._known_named_shapes_by_slide.items(): | |
| if existing_index < slide_index: | |
| reindexed[existing_index] = existing_shapes | |
| elif existing_index > slide_index: | |
| reindexed[existing_index - 1] = existing_shapes | |
| self._known_named_shapes_by_slide = reindexed | |
| def _score_intermediate_step( | |
| self, | |
| *, | |
| slide_index: int, | |
| inspection: ExtractedPresentation, | |
| ) -> Any: | |
| if self._eval_spec is None: | |
| raise RuntimeError("Environment must be reset before scoring") | |
| slide_extraction = inspection.slides[slide_index - 1] | |
| previous_slide_extractions = inspection.slides[: slide_index - 1] or None | |
| return evaluate_slide( | |
| self._eval_spec, | |
| slide_index, | |
| slide_extraction=slide_extraction, | |
| previous_slide_extractions=previous_slide_extractions, | |
| ) | |
| def _should_terminate(self) -> str | None: | |
| if ( | |
| self._last_action_result is not None | |
| and self._last_action_result.get("action_type") == "save_presentation" | |
| ): | |
| return "presentation_saved" | |
| if self._state.step_count >= self._max_steps: | |
| return "step_budget_exhausted" | |
| return None | |
| def _finalize_episode(self, termination_reason: str) -> float: | |
| if self._eval_spec is None or self._editor is None: | |
| raise RuntimeError("Environment must be reset before finalization") | |
| self._done = True | |
| self._termination_reason = termination_reason | |
| logger.info( | |
| "env.finalize start episode_id=%s reason=%s", | |
| self._state.episode_id, | |
| termination_reason, | |
| ) | |
| result = evaluate_presentation( | |
| self._eval_spec, | |
| self._editor, | |
| render_service=self._render_service, | |
| inspection_service=self._inspection_service, | |
| quantitative_quiz_judge_service=self._quantitative_quiz_judge_service, | |
| ) | |
| self._last_score = result.reward_total | |
| self._last_reward_details = { | |
| "kind": "terminal_presentation", | |
| "result": to_serializable(result), | |
| } | |
| write_debug_event( | |
| "reward.terminal", | |
| { | |
| "termination_reason": termination_reason, | |
| "reward_result": to_serializable(result), | |
| }, | |
| ) | |
| logger.info( | |
| "env.finalize complete episode_id=%s reward=%s metadata=%s", | |
| self._state.episode_id, | |
| self._last_score, | |
| result.metadata, | |
| ) | |
| return self._last_score | |
| if __name__ == "__main__": # pragma: no cover | |
| env = PptAgentEnvironment() | |
| observation = env.reset(seed=7) | |
| print("Reset prompt:", observation.prompt_summary) | |
| observation = env.step(PptAgentAction(action_type="create_slide")) | |
| print("Slides after create_slide:", observation.slide_count) | |
| observation = env.step( | |
| PptAgentAction( | |
| action_type="update_slide", | |
| slide_index=1, | |
| payload={ | |
| "add_shapes": [ | |
| { | |
| "type": "text", | |
| "text": "Hello from the PPT skeleton", | |
| "x": 0.8, | |
| "y": 1.2, | |
| "w": 8.4, | |
| "h": 1.0, | |
| "style": { | |
| "font_name": "<font>", | |
| "font_size_pt": "<body_size>", | |
| "color_hex": "<primary>", | |
| }, | |
| } | |
| ] | |
| }, | |
| ) | |
| ) | |
| while not observation.done: | |
| observation = env.step(PptAgentAction(action_type="create_slide")) | |
| print("Terminal reward:", observation.reward) | |