Spaces:
Runtime error
Runtime error
| from langgraph.types import interrupt | |
| from googleapiclient.errors import HttpError | |
| from app.schemas.email_writing_agent_tools_schema import CreateDraftSchema, SendDraftSchema | |
| from langchain.tools import tool | |
| from langchain_google_community import GmailToolkit | |
| from typing import Annotated, Union | |
| from langchain_core.tools import InjectedToolCallId, tool | |
| from langchain.tools import ToolRuntime | |
| from langgraph.types import Command | |
| from langchain_core.messages import SystemMessage, HumanMessage,ToolMessage,AIMessage,BaseMessage | |
| from langgraph.graph import END | |
| def create_gmail_draft( | |
| to: Union[str, list], | |
| subject: str, | |
| body: str, | |
| tool_call_id: Annotated[str, InjectedToolCallId] # Injected ID | |
| ): | |
| """Creates a new Gmail draft after human approval.""" | |
| if isinstance(to, list): | |
| to = str(to[0]) if len(to) > 0 else "ERROR" | |
| if to == "ERROR": | |
| return "ERROR: 'to' parameter is empty." | |
| # 1. Human-in-the-loop Interrupt | |
| response = interrupt({ | |
| "action": "review_draft", | |
| "data": {"to": to, "subject": subject, "body": body} | |
| }) | |
| toolkit = GmailToolkit() | |
| draft_tool = [t for t in toolkit.get_tools() if t.name == "create_gmail_draft"][0] | |
| # 2. Handle Logic | |
| if response.get("status") == "approved": | |
| reply = draft_tool.invoke({"message": body, "to": [to], "subject": subject}) | |
| try: | |
| draft_id = reply.split(":")[1].strip() | |
| content = f"Draft created. User You MUST confirm before calling send_draft()." | |
| # UPDATE STATE: Save draft_id directly | |
| return Command( | |
| update={ | |
| "draft_id": draft_id, | |
| "reply_subject": subject, | |
| "reply_email_body": body, | |
| "messages": [ToolMessage(content, tool_call_id=tool_call_id)] | |
| },goto=END | |
| ) | |
| except IndexError: | |
| return f"Draft created, but response parsing failed: {reply}" | |
| else: | |
| feedback = response.get("feedback", "User rejected.") | |
| return f"DRAFT REJECTED BY USER. Feedback: {feedback}. Please rewrite." | |
| #--------------------------------------------------------------------------- | |
| def send_draft( | |
| tool_call_id: Annotated[str, InjectedToolCallId],runtime: ToolRuntime # Injected ID | |
| ): | |
| """Sends a finalized Gmail draft by its ID.""" | |
| try: | |
| toolkit = GmailToolkit() | |
| result = toolkit.api_resource.users().drafts().send( | |
| userId="me", body={"id": runtime.state["draft_id"]} | |
| ).execute() | |
| sent_id = result['id'] | |
| content = f"SUCCESS: Sent! a Gmail" | |
| # UPDATE STATE: Save sent_message_id directly | |
| return Command( | |
| update={ | |
| "sent_message_id": sent_id, | |
| "messages": [ToolMessage(content, tool_call_id=tool_call_id)] | |
| } | |
| ) | |
| except HttpError as error: | |
| error_msg = f"ERROR: {error}" | |
| return Command( | |
| update={"messages": [ToolMessage(error_msg, tool_call_id=tool_call_id)]} | |
| ) | |
| email_writing_agent_tools = [ | |
| create_gmail_draft, | |
| send_draft | |
| ] |