Untitled

 avatar
unknown
plain_text
a month ago
9.6 kB
2
Indexable
@tool
def create_meeting_tool(user_query, state: Annotated[dict, InjectedState], config: RunnableConfig) -> str:
    """
    Tool that creates the meeting once the meeting details are confirmed
    Args:
        user_query: The user query request for creating a meeting with 1 or more participants.
    """
    access_token = config["configurable"]["access_token"]
    user_timestamp = config["configurable"]["user_timestamp"]
    user_timezone=config["configurable"]["user_timezone"]
    llm = model_categories["l_model"]
    parser = JsonOutputParser(pydantic_object=MeetingInvite)
    meeting_invite_prompt = PromptTemplate(
        template="""
    You are a system that parses a user's meeting request: {query}
    and get the meeting details from the conversation {conversation_history}

    You must return valid JSON with these exact keys:
    1. "meeting_start"
    2. "meeting_end"
    3. "attendees"
    4. "email_subject"
    5. "email_body"

    Requirements:
    - "meeting_start" / "meeting_end":
     -Get the meeting start and meeting end time from the conversation .
     -After gathering the meeting start and end_date, refer to the  current_user timestamp {user_timestamp}.
     Use the user timestamp to calculate the appropriate meeting_start and meeting_end time.
     The meeting_start and meeting_end must be in the same format as current_user timestamp format
    - "attendees":
      - A list of attendee email addresses.Get the list of attendees from the conversation 
    - "email_subject":
      - Get the email_subject from the user.If the user has not provided any email_subject then create one based on the conversation.
    - "email_body":
      - Get the email_body from the user.If the user has not provided any email_subject then create one based on the conversation.
      -Use the email topics as reference.

    No additional keys beyond these five should be in your output.

    {format_instructions}
    """,
        input_variables=["query", "conversation_history","current_utc_time"],
        partial_variables={"format_instructions": parser.get_format_instructions()},
    )
    chain = meeting_invite_prompt | llm | parser
    response = chain.invoke({"query": user_query, "conversation_history": state["messages"],"user_timestamp":user_timestamp})


    # TODO:fix the time handling part properly that can take user data regarding timezone etc and use that data else pass default meeting time
    meeting_start_iso = convert_to_utc(response["meeting_start"], user_timezone)
    meeting_end_iso = convert_to_utc(response["meeting_end"], user_timezone)
    attendees = response["attendees"]
    formatted_attendees = [str2(attendee) for attendee in attendees]
    meeting_subject_email = response.get("email_subject", f"Meeting link: {meeting_start_iso}-{meeting_end_iso}")
    meeting_body_email = response.get("email_body", f"Meeting link: {meeting_start_iso}-{meeting_end_iso}")
    #
    # #API calls
    outlook_details_of_user_url = f"{storage_api_url}/outlook/list-connected-outlook-accounts"
    response = requests.request("GET", outlook_details_of_user_url, headers={'Authorization': f"Bearer {access_token}"})
    if response.status_code == 200:
         outlook_details_of_user_data = response.json()
    else:
         return "Failed fetching outlook account_id details of sender"
    user_account_details = outlook_details_of_user_data.get("accounts", [])
    user_outlook_account_id = user_account_details[0].get("outlook_account_id", "") if user_account_details else ""
    meeting_details_url = f"{storage_api_url}/outlook/meeting-invite"
    meeting_details_payload = json.dumps({
        "account_id": user_outlook_account_id,
        "reply_content": meeting_body_email,
        "attendees": formatted_attendees,
        "from_date": meeting_start_iso,
        "to_date": meeting_end_iso,
        "subject": meeting_subject_email
    })
    meeting_request_api = requests.request("POST", meeting_details_url,
                                           headers={'Authorization': f"Bearer {access_token}"},
                                           data=meeting_details_payload)
    print("finished sending invite")
    return meeting_request_api.text


@tool
def meeting_confirmation_details_tool(user_query, state: Annotated[dict, InjectedState], config: RunnableConfig) -> str:
    """
    Tool that handles and confirms the details of the meeting like the meeting day,meeting time and meeting attendees.
    Args:
        user_query: The user query request for creating a meeting with 1 or more participants.
    """
    access_token = config["configurable"]["access_token"]
    user_timezone = config["configurable"]["user_timezone"]
    workspace_user_details_url = f"{xnode_auth_url}/user/get_all_users"
    response = requests.request("GET", workspace_user_details_url, headers={'Authorization': f"Bearer {access_token}"})
    if response.status_code == 200:
        user_data_json = response.json()
        user_to_email = {f"{user['first_name']} {user['last_name']}": user['email'] for user in user_data_json}
    else:
        return "Failed fetching user details"
    llm = model_categories["l_model"]
    user_timestamp = config["configurable"]["user_timestamp"]
    meeting_invite_prompt = PromptTemplate(
        template="""
    You are a system that processes a user's meeting request. The user has asked: {query}. Using the information in 
    the user_to_email map {user_to_email} , user timestamp {user_timestamp} and conversation_history {conversation_history} 
    you need to construct a clear response that addresses the following:

    1. Data Issues:
       - If there is ambiguity about which user to add due to similar names, ask for clarification.
       - If there is a closely matching name (e.g., the user says 'john' but only 'john_doe' exists), consider that as a attendee to the conversation.
         Do not clarify but notify the user all the attendees to your conversation in this scenario.
       - If a requested user is not found, notify the user that the person is not in the email list.
       - If it's unclear when the meeting should start or end, ask for details about the  date, and duration.{user_timezone}
       - Ask the user to clarify the topics of the meeting and let this be in a separate section called "Meeting topics" in case any is their and let this be a seperate heading.

    2. Meeting Start and End Times:
       -  Use the user timestamp to calculate the appropriate meeting_start and meeting_end time. Refer user timezone as well

    3. Attendees:
       - Compile a list of attendee email addresses based on the corrected input from the data issues step. If the
         user adds specific email addresses not part of the user_to_email map ,include them as part of attendees as well.

    If there are data issues, your response should primarily focus on resolving these before confirming meeting details. 
     Do not include sign-offs or greetings in your response.
    """,
        input_variables=["query", "user_to_email", "conversation_history","user_timestamp"],
    )
    chain = meeting_invite_prompt | llm|StrOutputParser()
    response = chain.invoke({"query": user_query, "user_to_email": user_to_email,"conversation_history":state["messages"],
                             "user_timestamp": user_timestamp,"user_timezone":user_timezone })
    return response



class MeetingInvite(BaseModel):
    # data_issues: str = Field(
    #     description="String describing any issues or needed clarifications. Empty if none."
    # )
    meeting_start: str = Field(
        description="Timestamp for the meeting start . Empty if not provided."
    )
    meeting_end: str = Field(
        description="  Timestamp for the meeting end . Empty if not provided."
    )
    attendees: List[str] = Field(
        description="List of attendee email addresses."
    )
    email_subject: str = Field(
        description="Meeting subject if user provides one,generate one if not provided by user based on conversation."
    )
    email_body: str = Field(
        description="Meeting body/description if user provides one, generate one if not provided by user based on conversation."
    )

meeting_invite_prompt = """
        You are an AI assistant capable of helping users schedule meetings . 
        You have access to a tool called `meeting_confirmation_details_tool`.
        Use `meeting_confirmation_details_tool` when the user specifically wants to:
        1. Schedule a new meeting.

        Do not call this tool for general questions that do not involve scheduling a meeting. For those, you can provide 
        a direct answer or call other tools. Do not call this tool to create the meeting. 
        """
        create_meeting_prompt = """
        You are an AI assistant capable of helping creating the meeting requested by the user once the details of the
        meetings are validated by the user from calling the `meeting_confirmation_details_tool` . 
        You have access to a tool called 'create_meeting_tool'.
        Use 'create_meeting_tool' when the user specifically validates the meeting details

        Do not call this tool for general questions that do not involve scheduling a meeting. For those, you can provide 
        a direct answer or call other tools. This tool must be called only after meeting details are verified from user
        from the  `meeting_confirmation_details_tool`.
        """
Editor is loading...
Leave a Comment