| 49 | |
| 50 | @dataclass |
| 51 | class ActionStep(MemoryStep): |
| 52 | step_number: int |
| 53 | timing: Timing |
| 54 | model_input_messages: list[ChatMessage] | None = None |
| 55 | tool_calls: list[ToolCall] | None = None |
| 56 | error: AgentError | None = None |
| 57 | model_output_message: ChatMessage | None = None |
| 58 | model_output: str | list[dict[str, Any]] | None = None |
| 59 | code_action: str | None = None |
| 60 | observations: str | None = None |
| 61 | observations_images: list["PIL.Image.Image"] | None = None |
| 62 | action_output: Any = None |
| 63 | token_usage: TokenUsage | None = None |
| 64 | is_final_answer: bool = False |
| 65 | |
| 66 | def dict(self): |
| 67 | # We overwrite the method to parse the tool_calls and action_output manually |
| 68 | return { |
| 69 | "step_number": self.step_number, |
| 70 | "timing": self.timing.dict(), |
| 71 | "model_input_messages": [ |
| 72 | make_json_serializable(get_dict_from_nested_dataclasses(msg)) for msg in self.model_input_messages |
| 73 | ] |
| 74 | if self.model_input_messages |
| 75 | else None, |
| 76 | "tool_calls": [tc.dict() for tc in self.tool_calls] if self.tool_calls else [], |
| 77 | "error": self.error.dict() if self.error else None, |
| 78 | "model_output_message": make_json_serializable(get_dict_from_nested_dataclasses(self.model_output_message)) |
| 79 | if self.model_output_message |
| 80 | else None, |
| 81 | "model_output": self.model_output, |
| 82 | "code_action": self.code_action, |
| 83 | "observations": self.observations, |
| 84 | "observations_images": [image.tobytes() for image in self.observations_images] |
| 85 | if self.observations_images |
| 86 | else None, |
| 87 | "action_output": make_json_serializable(self.action_output), |
| 88 | "token_usage": asdict(self.token_usage) if self.token_usage else None, |
| 89 | "is_final_answer": self.is_final_answer, |
| 90 | } |
| 91 | |
| 92 | def to_messages(self, summary_mode: bool = False) -> list[ChatMessage]: |
| 93 | messages = [] |
| 94 | if self.model_output is not None and not summary_mode: |
| 95 | messages.append( |
| 96 | ChatMessage(role=MessageRole.ASSISTANT, content=[{"type": "text", "text": self.model_output.strip()}]) |
| 97 | ) |
| 98 | |
| 99 | if self.tool_calls is not None: |
| 100 | messages.append( |
| 101 | ChatMessage( |
| 102 | role=MessageRole.TOOL_CALL, |
| 103 | content=[ |
| 104 | { |
| 105 | "type": "text", |
| 106 | "text": "Calling tools:\n" + str([tc.dict() for tc in self.tool_calls]), |
| 107 | } |
| 108 | ], |
no outgoing calls
searching dependent graphs…