class OODALoop:
    class Step:
        """
        Represents a step in the OODA loop.

        Attributes
            name (str): The name of the step.
            prompt_function (function): The function used to generate the prompt for the step.
            input_data: The input data for the step.
            output_data: The output data generated by the step.

        """

        def __init__(self, name, prompt_function):
            self.name = name
            self.prompt_function = prompt_function
            self.input_data = None
            self.output_data = None

        def execute(self, objective, llm, history):
            """
            Executes the step by generating a prompt using the prompt function,
            getting a response from the LLM, and storing the output data.

            Args:
                objective: The overall objective of the OODA loop.
                llm: The LLM (Language Learning Model) used to get the response.
                history: The history of previous prompts and responses.

            Returns:
                The output data generated by the step.

            """
            common_prompt = (
                "I want you to be a great problem-solver, expert in the use of OODA control loops,"
                " with the Observe, Orient, Decide, Act steps."
                f" The overall objective is '{objective}.'"
                f" We are in the '{self.name}' step."
                " Be concise yet informative in your responses."
            )

            prompt = f"{common_prompt}\n{self.prompt_function(self.input_data)}"
            self.output_data = llm.get_response(prompt, history)

    def __init__(self, objective):
        self.objective = objective
        self.steps = {
            "Observe": self.Step(
                "Observe",
                lambda _: f"Observe the situation related to the objective '{objective}'",
            ),
            "Orient": self.Step(
                "Orient",
                lambda input_data: f"Orient based on the observation: {input_data}",
            ),
            "Decide": self.Step(
                "Decide",
                lambda input_data: f"Decide the next action based on the orientation: {input_data}",
            ),
            "Act": self.Step(
                "Act", lambda input_data: f"Act based on the decision: {input_data}"
            ),
        }

    def run(self, llm, history):
        output = None
        for step in self.steps.values():
            step.input_data = output
            step.execute(self.objective, llm, history)
            output = step.output_data
        return output  # The final output comes from the 'Act' step
