From 3c793608bf7b223f5bb34352e6bf482b7af21f10 Mon Sep 17 00:00:00 2001 From: Mollie Munoz Date: Tue, 5 Nov 2024 17:26:01 -0800 Subject: [PATCH] Updates for gc outline feedback (#218) - Fixes config to correctly get user decision, final response, and conversation status as expected for document agent piping. Coupling is strong and will be addressed later. - Resolves issue when `result.is_conversation_over` did not align with `conversation_status` in Article as `user_completed`. - Cleans up mode Status.INITIATED logic - Add state write where it was missing. --- .../gc_draft_outline_feedback_config.py | 11 +++-- .../agents/document/guided_conversation.py | 31 ++++++++------ .../assistant/agents/document_agent.py | 40 +++++++------------ 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/assistants/prospector-assistant/assistant/agents/document/gc_draft_outline_feedback_config.py b/assistants/prospector-assistant/assistant/agents/document/gc_draft_outline_feedback_config.py index 6b68d8c0..cc75ac86 100644 --- a/assistants/prospector-assistant/assistant/agents/document/gc_draft_outline_feedback_config.py +++ b/assistants/prospector-assistant/assistant/agents/document/gc_draft_outline_feedback_config.py @@ -19,22 +19,21 @@ class ArtifactModel(BaseModel): final_response: str = Field(description="The final response from the agent to the user.") conversation_status: str = Field(description="The status of the conversation.") + user_decision: str = Field(description="The decision of the user on what should happen next.") # Rules - These are the do's and don'ts that the agent should follow during the conversation. rules = [ "Terminate the conversation immediately if the user asks for harmful or inappropriate content.", - "If the conversation is complete due to the user wanting the outline updated, set the conversation_status to update_outline.", - "If the conversation is complete due to the user ready to move on to drafting the paper, set the conversation_state to user_completed.", ] # Conversation Flow (optional) - This defines in natural language the steps of the conversation. conversation_flow = """1. Start by asking the user to review the drafted outline. 2. Answer any questions about the outline or the drafting process the user might want to explore. -3. At any time, if the user asks for a change to the outline or updates the attachment file list, consider the conversation complete. -In this scenario, your final message should inform the user that a new outline is being generated based off the new info or request. -4. At any time, if the user is good with the outline in its current form and ready to move on to drafting a paper from it, consider the -conversation complete. In this scenario, your final message should inform the user that you will start drafting the beginning of the +3. At any time, if the user asks for a change to the outline or updates the attachment file list, the conversation_status must be +marked as user_completed. The user_decision must be marked as update_outline. The final_response should inform the user that a new outline is being generated based off the new info or request. +4. At any time, if the user is good with the outline in its current form and ready to move on to drafting a paper from it, the conversation_status must be +marked as user_completed. The user_decision must be marked as draft_paper. The final_response should inform the user that you will start drafting the beginning of the document based on this outline. """ diff --git a/assistants/prospector-assistant/assistant/agents/document/guided_conversation.py b/assistants/prospector-assistant/assistant/agents/document/guided_conversation.py index dd5ad040..51f1bee9 100644 --- a/assistants/prospector-assistant/assistant/agents/document/guided_conversation.py +++ b/assistants/prospector-assistant/assistant/agents/document/guided_conversation.py @@ -105,6 +105,7 @@ async def step_conversation( final_response: str = "" conversation_status: str | None = None + user_decision: str = "" response: str = "" # to_json is actually to dict, not to json. @@ -115,27 +116,33 @@ async def step_conversation( if artifact_item is not None: final_response = artifact_item.get("final_response") conversation_status = artifact_item.get("conversation_status") + user_decision = artifact_item.get("user_decision") # should be returning str and Status for Document Agent to consume. Update doc agent logic accordingly. status: Status = Status.UNDEFINED if conversation_status is not None: - if result.is_conversation_over is True: - _delete_guided_conversation_state(conversation_context) - if conversation_status == "user_completed": - status = Status.USER_COMPLETED - response = final_response - elif conversation_status == "update_outline": - status = Status.UPDATE_OUTLINE - response = final_response - else: - status = Status.USER_EXIT_EARLY - response = final_response - else: + if conversation_status == "Unanswered": if result.ai_message is not None: response = result.ai_message else: response = "" status = Status.NOT_COMPLETED + elif conversation_status == "user_completed": + _delete_guided_conversation_state(conversation_context) + response = final_response + if user_decision is None: + status = Status.USER_COMPLETED + else: + if user_decision == "update_outline": # this code is becoming highly coupled fyi to the gc configs + status = Status.UPDATE_OUTLINE + elif user_decision == "draft_paper": + status = Status.USER_COMPLETED + else: + logger.error("unknown user decision") + else: + _delete_guided_conversation_state(conversation_context) + status = Status.USER_EXIT_EARLY + response = final_response return response, status diff --git a/assistants/prospector-assistant/assistant/agents/document_agent.py b/assistants/prospector-assistant/assistant/agents/document_agent.py index bd8d2489..039eda2f 100644 --- a/assistants/prospector-assistant/assistant/agents/document_agent.py +++ b/assistants/prospector-assistant/assistant/agents/document_agent.py @@ -381,36 +381,16 @@ async def _run_mode( step_name = step.get_name() step_status = step.get_status() - # This Status.INITIATED will occur when the mode is setting up the first step on its first run - if step_status is Status.INITIATED: - logger.info("Document Agent mode (%s) at beginning.", mode_name) - self._state.mode.get_step().set_status(Status.NOT_COMPLETED) - self._write_state(context) - - step = self._state.mode.get_step() - step_name = step.get_name() - step_status = step.get_status() - - # This Status.INITIATED will occur when a new step is setup upon a prior step's Status.USER_COMPLETED. while step_status is Status.INITIATED or step_status is Status.NOT_COMPLETED: step_method = self._get_step_method(step) if step_method: logger.info("Document Agent in step: %s", step_name) step_status = await step_method(config, context, message, metadata) - match step_status: - case Status.UNDEFINED: - logger.error( - "Calling corresponding step method for %s resulted in status %s. Resetting mode %s.", - step_name, - step_status, - mode_name, - ) - self._state.mode.reset() - break # problem - + match step_status: # resulting status of step_method() case Status.NOT_COMPLETED: self._state.mode.get_step().set_status(step_status) + self._state.mode.set_status(step_status) break # ok - get more user input case Status.USER_COMPLETED: @@ -418,8 +398,9 @@ async def _run_mode( if next_step is not None: step = next_step step_name = next_step.get_name() - step_status = next_step.get_status() + step_status = next_step.get_status() # new step is Status.INITIATED self._state.mode.set_step(next_step) + self._write_state(context) continue # ok - don't need user input yet else: self._state.mode.get_step().set_status(step_status) @@ -430,6 +411,16 @@ async def _run_mode( self._state.mode.get_step().set_status(step_status) self._state.mode.set_status(step_status) break # ok - done early :) + + case _: # UNDEFINED, INITIATED + logger.error( + "Document Agent: Calling corresponding step method for %s resulted in status %s. Resetting mode %s.", + step_name, + step_status, + mode_name, + ) + self._state.mode.reset() + break # problem else: logger.error( "Document Agent failed to find a corresponding step method for %s. Resetting mode %s.", @@ -482,9 +473,9 @@ async def _mode_draft_outline( StepName.DO_FINAL_OUTLINE, ], ) + logger.info("Document Agent mode (%s) at beginning.", mode_name) first_step_name = self._state.mode.get_step_order()[0] self._state.mode.set_step(Step(name=first_step_name, status=Status.INITIATED)) - self._state.mode.set_status(Status.NOT_COMPLETED) self._write_state(context) self._step_name_to_method: dict[StepName, Callable] = { @@ -606,7 +597,6 @@ async def _step_gc_get_outline_feedback( return self._state.mode.get_status() # Run - # Because the last user message will be ending a prior step, and not be related to this step. user_message: ConversationMessage | None if step_status is Status.INITIATED: user_message = None