diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 5ce43932..fab75569 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -66,11 +66,58 @@ def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: ], ) ) + case protocol_schema.TaskRequestType.rank_initial_prompts: + logger.info("Generating a RankInitialPromptsTask.") + task = protocol_schema.RankInitialPromptsTask( + prompts=[ + "Please write a story about a time you were happy.", + "Please write a story about a time you were sad.", + ] + ) + case protocol_schema.TaskRequestType.rank_user_replies: + logger.info("Generating a RankUserRepliesTask.") + task = protocol_schema.RankUserRepliesTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, what's going on in the world?", + is_assistant=False, + ), + protocol_schema.ConversationMessage( + text="I'm not sure I understood correctly, could you rephrase that?", + is_assistant=True, + ), + ], + ), + replies=[ + "Oh come oooooon!", + "What are the news?", + ], + ) + + case protocol_schema.TaskRequestType.rank_assistant_replies: + logger.info("Generating a RankAssistantRepliesTask.") + task = protocol_schema.RankAssistantRepliesTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, what's going on in the world?", + is_assistant=False, + ), + ], + ), + replies=[ + "I'm not sure I understood correctly, could you rephrase that?", + "The world is fine. All good.", + "Crap is hitting the fan. Start farming.", + ], + ) case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, detail="Invalid request type.", ) + logger.info(f"Generated {task=}.") if request.user is not None: task.addressed_user = request.user @@ -107,26 +154,33 @@ def acknowledge_task( db: Session = Depends(deps.get_db), api_key: APIKey = Depends(deps.get_api_key), task_id: UUID, - response: protocol_schema.AnyTaskResponse, + ack_request: protocol_schema.TaskAck, ) -> Any: """ The frontend acknowledges a task. """ deps.api_auth(api_key, db) - match (type(response)): - case protocol_schema.PostCreatedTaskResponse: - logger.info(f"Frontend acknowledged {task_id=} and created {response.post_id=}.") - # here we would store the post id in the database for the task - case protocol_schema.RatingCreatedTaskResponse: - logger.info(f"Frontend acknowledged {task_id=} for {response.post_id=}.") - # here we would store the rating id in the database for the task - case _: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid response type.", - ) + logger.info(f"Frontend acknowledges task {task_id=}, {ack_request=}.") + # here we would store the post id in the database for the task + return {} + +@router.post("/{task_id}/nack") +def acknowledge_task_failure( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + task_id: UUID, + nack_request: protocol_schema.TaskNAck, +) -> Any: + """ + The frontend reports failure to implement a task. + """ + deps.api_auth(api_key, db) + + logger.info(f"Frontend reports failure to implement task {task_id=}, {nack_request=}.") + # here we would store the post id in the database for the task return {} @@ -142,7 +196,7 @@ def post_interaction( """ deps.api_auth(api_key, db) - match (type(interaction)): + match type(interaction): case protocol_schema.TextReplyToPost: logger.info( f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." @@ -162,6 +216,16 @@ def post_interaction( reply_to_post_id=interaction.post_id, addressed_user=interaction.user, ) + case protocol_schema.PostRanking: + logger.info( + f"Frontend reports ranking of {interaction.post_id=} with {interaction.ranking=} by {interaction.user=}." + ) + # TODO: check if the ranking is valid + # here we would store the ranking in the database + return protocol_schema.TaskDone( + reply_to_post_id=interaction.post_id, + addressed_user=interaction.user, + ) case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index 059031af..bd4997c2 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -45,6 +45,18 @@ class TaskRequest(BaseModel): user: Optional[User] = None +class TaskAck(BaseModel): + """The frontend acknowledges that it has received a task and created a post.""" + + post_id: str + + +class TaskNAck(BaseModel): + """The frontend acknowledges that it has received a task but cannot create a post.""" + + reason: str + + class Task(BaseModel): """A task is a unit of work that the backend gives to the frontend.""" @@ -53,41 +65,6 @@ class Task(BaseModel): addressed_user: Optional[User] = None -class TaskResponse(BaseModel): - """A task response is a message from the frontend to acknowledge that an initial piece of work has been done on the task.""" - - type: str - status: Literal["success", "failure"] = "success" - - -class PostCreatedTaskResponse(TaskResponse): - """The frontend signals to the backend that a post has been created.""" - - type: Literal["post_created"] = "post_created" - post_id: str - - -class RatingCreatedTaskResponse(TaskResponse): - """The frontend signals to the backend that a rating input has been created for a given post.""" - - type: Literal["rating_created"] = "rating_created" - post_id: str - - -class RankingCreatedTaskResponse(TaskResponse): - """The frontend signals to the backend that a ranking input has been created for a given post.""" - - type: Literal["ranking_created"] = "ranking_created" - post_id: str - - -AnyTaskResponse = Union[ - PostCreatedTaskResponse, - RatingCreatedTaskResponse, - RankingCreatedTaskResponse, -] - - class SummarizeStoryTask(Task): """A task to summarize a story.""" diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index 44b7cbc5..9ac1dcea 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -39,7 +39,7 @@ def main(backend_url: str, api_key: str): typer.echo(task["story"]) # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) summary = typer.prompt("Enter your summary") @@ -63,7 +63,7 @@ def main(backend_url: str, api_key: str): typer.echo(f"Rating scale: {task['scale']['min']} - {task['scale']['max']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "rating_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) rating = typer.prompt("Enter your rating", type=int) # send interaction @@ -82,7 +82,7 @@ def main(backend_url: str, api_key: str): if task["hint"]: typer.echo(f"Hint: {task['hint']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) prompt = typer.prompt("Enter your prompt") # send interaction new_task = _post( @@ -105,7 +105,7 @@ def main(backend_url: str, api_key: str): if task["hint"]: typer.echo(f"Hint: {task['hint']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) reply = typer.prompt("Enter your reply") # send interaction new_task = _post( @@ -126,7 +126,7 @@ def main(backend_url: str, api_key: str): for message in task["conversation"]["messages"]: typer.echo(_render_message(message)) # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) reply = typer.prompt("Enter your reply") # send interaction new_task = _post( @@ -141,6 +141,27 @@ def main(backend_url: str, api_key: str): ) tasks.append(new_task) + case "rank_initial_prompts": + typer.echo("Rank the following prompts:") + for idx, prompt in enumerate(task["prompts"], start=1): + typer.echo(f"{idx}: {prompt}") + # acknowledge task + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + + typer.prompt("Enter the prompt numbers in order of preference, separated by commas") + + case "rank_user_replies" | "rank_assistant_replies": + typer.echo("Here is the conversation so far:") + for message in task["conversation"]["messages"]: + typer.echo(_render_message(message)) + typer.echo("Rank the following replies:") + for idx, reply in enumerate(task["replies"], start=1): + typer.echo(f"{idx}: {reply}") + # acknowledge task + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + + typer.prompt("Enter the reply numbers in order of preference, separated by commas") + case "task_done": if addressed_user := task["addressed_user"]: typer.echo(f"Hey, {addressed_user['display_name']}! Thank you!")