Repository: Pythagora-io/gpt-pilot
File: core/agents/orchestrator.py (Python)

Code before:
        # INPUT_REQUIRED, we'll first ask the user to provide the required input.
        return await self.import_files()

    def create_agent(self, prev_response: Optional[AgentResponse]) -> BaseAgent:
        state = self.current_state

        if prev_response:
            if prev_response.type in [ResponseType.CANCEL, ResponseType.ERROR]:
                return ErrorHandler(self.state_manager, self.ui, prev_response=prev_response)
            if prev_response.type == ResponseType.CODE_REVIEW:
                return CodeReviewer(self.state_manager, self.ui, prev_response=prev_response)
            if prev_response.type == ResponseType.CODE_REVIEW_FEEDBACK:
                return CodeMonkey(self.state_manager, self.ui, prev_response=prev_response, step=state.current_step)
            if prev_response.type == ResponseType.DESCRIBE_FILES:
                return CodeMonkey(self.state_manager, self.ui, prev_response=prev_response)
            if prev_response.type == ResponseType.INPUT_REQUIRED:
                # FIXME: HumanInput should be on the whole time and intercept chat/interrupt
                return HumanInput(self.state_manager, self.ui, prev_response=prev_response)
            if prev_response.type == ResponseType.TASK_REVIEW_FEEDBACK:
                return Developer(self.state_manager, self.ui, prev_response=prev_response)

        status = state.current_task.get("status") if state.current_task else None
        if status:
            # Status of the current task is set first time after the task was reviewed by user
            log.info(f"Status of current task: {status}")
            if status == "reviewed":
                # User reviewed the task, call TechnicalWriter to see if documentation needs to be updated
                return TechnicalWriter(self.state_manager, self.ui)
            elif status == "documented":
                # After documentation is done, call TechLead update the development plan (remaining tasks)
                return TechLead(self.state_manager, self.ui)
            elif status in ["done", "skipped"]:
                # Task is fully done or skipped, call TaskCompleter to mark it as completed
                return TaskCompleter(self.state_manager, self.ui)

        if not state.specification.description:
            # Ask the Spec Writer to refine and save the project specification
            return SpecWriter(self.state_manager, self.ui)
        elif not state.specification.architecture:
            # Ask the Architect to design the project architecture and determine dependencies
            return Architect(self.state_manager, self.ui, process_manager=self.process_manager)
        elif (
            not state.epics
            or not self.current_state.unfinished_tasks
            or (state.specification.template and not state.files)
        ):
            # Ask the Tech Lead to break down the initial project or feature into tasks and apply project template
            return TechLead(self.state_manager, self.ui, process_manager=self.process_manager)
        elif not state.steps and not state.iterations:
            # Ask the Developer to break down current task into actionable steps
            return Developer(self.state_manager, self.ui)

Review comment (performance):
Can we have all the statuses listed TaskStatus.REVIEWED ... (etc), (same like we did with specification.Complexity?) Helps with us poor typo-friendly devs :D  No need to subclass Enum (since it complicates things a bit, hence I didn't do that when converting Complexity), but just having those as constants will make me sleep easier at night.

Code after:
        # this is a good time to add them to the project. If any of them have
        # INPUT_REQUIRED, we'll first ask the user to provide the required input.
        return await self.import_files()

    def create_agent(self, prev_response: Optional[AgentResponse]) -> BaseAgent:
        state = self.current_state

        if prev_response:
            if prev_response.type in [ResponseType.CANCEL, ResponseType.ERROR]:
                return ErrorHandler(self.state_manager, self.ui, prev_response=prev_response)
            if prev_response.type == ResponseType.CODE_REVIEW:
                return CodeReviewer(self.state_manager, self.ui, prev_response=prev_response)
            if prev_response.type == ResponseType.CODE_REVIEW_FEEDBACK:
                return CodeMonkey(self.state_manager, self.ui, prev_response=prev_response, step=state.current_step)
            if prev_response.type == ResponseType.DESCRIBE_FILES:
                return CodeMonkey(self.state_manager, self.ui, prev_response=prev_response)
            if prev_response.type == ResponseType.INPUT_REQUIRED:
                # FIXME: HumanInput should be on the whole time and intercept chat/interrupt
                return HumanInput(self.state_manager, self.ui, prev_response=prev_response)
            if prev_response.type == ResponseType.TASK_REVIEW_FEEDBACK:
                return Developer(self.state_manager, self.ui, prev_response=prev_response)

        if not state.specification.description:
            # Ask the Spec Writer to refine and save the project specification
            return SpecWriter(self.state_manager, self.ui)
        elif not state.specification.architecture:
            # Ask the Architect to design the project architecture and determine dependencies
            return Architect(self.state_manager, self.ui, process_manager=self.process_manager)
        elif (
            not state.epics
            or not self.current_state.unfinished_tasks
            or (state.specification.template and not state.files)
        ):
            # Ask the Tech Lead to break down the initial project or feature into tasks and apply project template
            return TechLead(self.state_manager, self.ui, process_manager=self.process_manager)
        elif not state.steps and not state.iterations:
            # Ask the Developer to break down current task into actionable steps
            return Developer(self.state_manager, self.ui)

        current_task_status = state.current_task.get("status") if state.current_task else None
        if current_task_status:
            # Status of the current task is set first time after the task was reviewed by user
            log.info(f"Status of current task: {current_task_status}")
            if current_task_status == TaskStatus.REVIEWED:
                # User reviewed the task, call TechnicalWriter to see if documentation needs to be updated
                return TechnicalWriter(self.state_manager, self.ui)
            elif current_task_status == TaskStatus.DOCUMENTED:
                # After documentation is done, call TechLead update the development plan (remaining tasks)
                return TechLead(self.state_manager, self.ui)
            elif current_task_status in [TaskStatus.EPIC_UPDATED, TaskStatus.SKIPPED]:
                # Task is fully done or skipped, call TaskCompleter to mark it as completed