From 9f914f8d479d58d7ccc045784fe614dfea9a1219 Mon Sep 17 00:00:00 2001 From: Navan Chauhan Date: Fri, 13 Oct 2023 23:50:10 -0600 Subject: langchain --- call_transcript_utils.py | 32 ++++++++++++++++++++++++ lang_prompt_demo.py | 37 ++++++++++++++++++++++++++++ stdout_filterer.py | 15 ++++++++++++ tools/contacts.py | 19 ++++++++++++++ tools/vocode.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+) create mode 100644 call_transcript_utils.py create mode 100644 lang_prompt_demo.py create mode 100644 stdout_filterer.py create mode 100644 tools/contacts.py create mode 100644 tools/vocode.py diff --git a/call_transcript_utils.py b/call_transcript_utils.py new file mode 100644 index 0000000..ce0764c --- /dev/null +++ b/call_transcript_utils.py @@ -0,0 +1,32 @@ +import os +from typing import Optional + +CALL_TRANSCRIPTS_DIR = os.path.join(os.path.dirname(__file__), "call_transcripts") + + +def add_transcript(conversation_id: str, transcript: str) -> None: + transcript_path = os.path.join( + CALL_TRANSCRIPTS_DIR, "{}.txt".format(conversation_id) + ) + with open(transcript_path, "a") as f: + f.write(transcript) + + +def get_transcript(conversation_id: str) -> Optional[str]: + transcript_path = os.path.join( + CALL_TRANSCRIPTS_DIR, "{}.txt".format(conversation_id) + ) + if os.path.exists(transcript_path): + with open(transcript_path, "r") as f: + return f.read() + return None + + +def delete_transcript(conversation_id: str) -> bool: + transcript_path = os.path.join( + CALL_TRANSCRIPTS_DIR, "{}.txt".format(conversation_id) + ) + if os.path.exists(transcript_path): + os.remove(transcript_path) + return True + return False diff --git a/lang_prompt_demo.py b/lang_prompt_demo.py new file mode 100644 index 0000000..d63aea3 --- /dev/null +++ b/lang_prompt_demo.py @@ -0,0 +1,37 @@ +import os +import sys +import typing +from dotenv import load_dotenv + +from tools.contacts import get_all_contacts +from tools.vocode import call_phone_number +from langchain.memory import ConversationBufferMemory + +from stdout_filterer import RedactPhoneNumbers + +load_dotenv() + +from langchain.chat_models import ChatOpenAI +from langchain.agents import initialize_agent +from langchain.agents import AgentType + +if __name__ == "__main__": + # Redirect stdout to our custom class + sys.stdout = typing.cast(typing.TextIO, RedactPhoneNumbers(sys.stdout)) + + OBJECTIVE = ( + input("Objective: ") + or "Find a random person in my contacts and tell them a joke" + ) + llm = ChatOpenAI(temperature=0, model_name="gpt-4") # type: ignore + memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) + # Logging of LLMChains + verbose = True + agent = initialize_agent( + tools=[get_all_contacts, call_phone_number], + llm=llm, + agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION, + verbose=verbose, + memory=memory, + ) + agent.run(OBJECTIVE) diff --git a/stdout_filterer.py b/stdout_filterer.py new file mode 100644 index 0000000..c4cf845 --- /dev/null +++ b/stdout_filterer.py @@ -0,0 +1,15 @@ +import re + + +class RedactPhoneNumbers: + def __init__(self, stream): + self.stream = stream + + def write(self, text): + # Regular expression to match phone numbers + phone_regex = r"(\+\d{1,2}\s?)?1?\-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}" + redacted_text = re.sub(phone_regex, "****", text) + self.stream.write(redacted_text) + + def flush(self): + self.stream.flush() diff --git a/tools/contacts.py b/tools/contacts.py new file mode 100644 index 0000000..4f4a3be --- /dev/null +++ b/tools/contacts.py @@ -0,0 +1,19 @@ +from typing import List +from langchain.agents import tool + +from dotenv import load_dotenv +load_dotenv() + +import os + +CONTACTS = [ + { + "name": "Greg", + "phone" : os.getenv("TEST_PHONE_NUMBER") + } +] + +@tool("get_all_contacts") +def get_all_contacts(placeholder: str) -> List[dict]: + """Returns all contacts in the user's phone book.""" + return CONTACTS \ No newline at end of file diff --git a/tools/vocode.py b/tools/vocode.py new file mode 100644 index 0000000..f752eff --- /dev/null +++ b/tools/vocode.py @@ -0,0 +1,64 @@ +import logging +import asyncio +import os +from langchain.agents import tool +from dotenv import load_dotenv + +from vocode.streaming.models.message import BaseMessage +from vocode.streaming.models.synthesizer import ElevenLabsSynthesizerConfig +from vocode.streaming.models.transcriber import DeepgramTranscriberConfig, PunctuationEndpointingConfig + + +load_dotenv() + +from call_transcript_utils import delete_transcript, get_transcript + +from vocode.streaming.telephony.conversation.outbound_call import OutboundCall +from vocode.streaming.telephony.config_manager.redis_config_manager import ( + RedisConfigManager, +) +from vocode.streaming.models.agent import ChatGPTAgentConfig +import time + +LOOP = asyncio.new_event_loop() +asyncio.set_event_loop(LOOP) + +@tool("call phone number") +def call_phone_number(input: str) -> str: + """calls a phone number as a bot and returns a transcript of the conversation. + the input to this tool is a pipe separated list of a phone number, a prompt, and the first thing the bot should say. + The prompt should instruct the bot with what to do on the call and be in the 3rd person, + like 'the assistant is performing this task' instead of 'perform this task'. + + should only use this tool once it has found an adequate phone number to call. + + for example, `+15555555555|the assistant is explaining the meaning of life|i'm going to tell you the meaning of life` will call +15555555555, say 'i'm going to tell you the meaning of life', and instruct the assistant to tell the human what the meaning of life is. + """ + phone_number, prompt, initial_message = input.split("|",2) + call = OutboundCall( + base_url=os.environ["BASE_URL"], + to_phone=phone_number, + from_phone=os.environ['TWILIO_PHONE'], + config_manager=RedisConfigManager(), + agent_config=ChatGPTAgentConfig( + initial_message=BaseMessage(text=initial_message), + prompt_preamble=prompt, + generate_responses=True, + ), + synthesizer_config=ElevenLabsSynthesizerConfig.from_telephone_output_device( + api_key=os.getenv("ELEVENLABS_API_KEY"), + voice_id=os.getenv("ELEVENLABS_VOICE_ID") + ), + transcriber_config=DeepgramTranscriberConfig.from_telephone_input_device( + endpointing_config=PunctuationEndpointingConfig() + ), + logger=logging.Logger("OutboundCall") + ) + LOOP.run_until_complete(call.start()) + while True: + maybe_transcript = get_transcript(call.conversation_id) + if maybe_transcript: + delete_transcript(call.conversation_id) + return maybe_transcript + else: + time.sleep(1) \ No newline at end of file -- cgit v1.2.3