Skip to content

Commit

Permalink
Updates for gc outline feedback (#218)
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
momuno authored Nov 6, 2024
1 parent 451fe7e commit 3c79360
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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

Expand Down
40 changes: 15 additions & 25 deletions assistants/prospector-assistant/assistant/agents/document_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,45 +381,26 @@ 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:
next_step = self._state.mode.get_next_step()
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)
Expand All @@ -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.",
Expand Down Expand Up @@ -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] = {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 3c79360

Please sign in to comment.