Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trevor #1

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"editor.formatOnSave": true,
"python.formatting.provider": "black",
"python.formatting.provider": "none",
"python.formatting.blackArgs": ["--line-length", "120"],
"python.linting.pylintUseMinimalCheckers": false,
"editor.showUnused": true
"editor.showUnused": true,
"python.analysis.typeCheckingMode": "basic",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
}
}
48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ Setup a Haiku hotline with Twilio that can be called like any other phone number
from gevent import monkey

monkey.patch_all()

from llm_convo.agents import OpenAIChat, TwilioCaller
from llm_convo.twilio_io import TwilioServer
from llm_convo.conversation import run_conversation
Expand Down Expand Up @@ -70,3 +69,50 @@ tws.on_session = run_chat
# You can also have ChatGPT actually start the call, e.g. for automated ordering
# tws.start_call("+18321231234")
```

# solv notes

- need to `brew install portaudio`
- install ngrok (brew)
- run ngrok and configure twilio with ngrok url
- pip install gevent python-dotenv twilio flask flask-sock
- lots of typos (need llm\_ in front of some things)

# instructions for setting up twilio webhook + ngrok

To point your Twilio Voice webhook to an ngrok URL, follow these steps:

1. Install and set up ngrok on your local machine. Ngrok provides a secure tunnel to expose your local development server to the internet. You can download ngrok from the official website: https://ngrok.com/download.

2. Start ngrok by running the following command in your terminal:

```
ngrok http 8000
```

Replace 8000 with the port number where your local development server is running. Ensure that your local server is running before starting ngrok.

3. Once ngrok is running, it will display a Forwarding URL. It should look something like this:

```
Forwarding https://abcdef.ngrok.io -> http://localhost:8000
```

Note the HTTPS URL provided by ngrok (https://abcdef.ngrok.io in this example).

4. Go to the Twilio Console (https://www.twilio.com/console) and navigate to the phone number you want to configure.

5. In the Phone Number settings, locate the Voice section and find the "A CALL COMES IN" field. Enter the ngrok URL followed by the path to your voice webhook endpoint. For example:

```

https://abcdef.ngrok.io/audio/incoming-voice
```

Replace https://abcdef.ngrok.io with your ngrok URL and /audio/incoming-voice with the path to your voice webhook endpoint.

6. Save the changes to apply the new webhook URL.

Now, when a voice call comes to your Twilio phone number, Twilio will forward the call to the ngrok URL, which will then redirect it to your local development server running on the specified port.

Remember that ngrok generates a temporary URL for each session, and it may change every time you restart ngrok. Make sure to update the webhook URL in the Twilio Console accordingly whenever you restart ngrok.
61 changes: 61 additions & 0 deletions demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from gevent import monkey
from dotenv import load_dotenv
import os

load_dotenv()

monkey.patch_all()


# HERE
from llm_convo.agents import OpenAIChat, TwilioCaller
from llm_convo.twilio_io import TwilioServer
from llm_convo.conversation import run_conversation
import logging
import time


print("WERE DOING SOMETHING")

logging.getLogger().setLevel(logging.INFO)

tws = TwilioServer(
remote_host="a8db-2603-8000-d100-14c9-810f-3c78-5c9a-603.ngrok.io",
port=8080,
static_dir=r"/static",
)
# Point twilio voice webhook to https://abcdef.ngrok.app/audio/incoming-voice
tws.start()

# agent_a = OpenAIChat(
# system_prompt="You are a Haiku Assistant. Answer whatever the user wants but always in a rhyming Haiku.",
# init_phrase="This is Haiku Bot, how can I help you.",
# )
agent_a = OpenAIChat(
system_prompt="""
You are an ordering bot that is going to call a pizza place an order a pizza.
When you need to say numbers space them out (e.g. 1 2 3) and do not respond with abbreviations.
If they ask for information not known, make something up that's reasonable.

The customer's details are:
* Address: 1234 Candyland Road, Apt 506
* Credit Card: 1234 5555 8888 9999 (CVV: 010)
* Name: Bob Joe
* Order: 1 large pizza with only pepperoni
""",
init_phrase="Hi, I would like to order a pizza.",
)


def run_chat(sess):
print("running chat", sess)
agent_b = TwilioCaller(sess)
while not agent_b.session.media_stream_connected():
time.sleep(0.1)
run_conversation(agent_a, agent_b)


tws.on_session = run_chat

# You can also have ChatGPT actually start the call, e.g. for automated ordering
tws.start_call("+15035761174")
15 changes: 10 additions & 5 deletions llm_convo/agents.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import List, Optional
from abc import ABC, abstractmethod

from convo.audio_input import WhisperMicrophone
from convo.audio_output import TTSClient, GoogleTTS
from convo.openai_io import OpenAIChatCompletion
from convo.twilio_io import TwilioCallSession
from llm_convo.audio_input import WhisperMicrophone
from llm_convo.audio_output import TTSClient, GoogleTTS
from llm_convo.openai_io import OpenAIChatCompletion
from llm_convo.twilio_io import TwilioCallSession


class ChatAgent(ABC):
Expand Down Expand Up @@ -48,7 +48,12 @@ def get_response(self, transcript: List[str]) -> str:


class TwilioCaller(ChatAgent):
def __init__(self, session: TwilioCallSession, tts: Optional[TTSClient] = None, thinking_phrase: str = "OK"):
def __init__(
self,
session: TwilioCallSession,
tts: Optional[TTSClient] = None,
thinking_phrase: str = "OK",
):
self.session = session
self.speaker = tts or GoogleTTS()
self.thinking_phrase = thinking_phrase
Expand Down
2 changes: 1 addition & 1 deletion llm_convo/conversation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from convo.agents import ChatAgent
from llm_convo.agents import ChatAgent


def run_conversation(agent_a: ChatAgent, agent_b: ChatAgent):
Expand Down
25 changes: 21 additions & 4 deletions llm_convo/twilio_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import simple_websocket
import audioop

from convo.audio_input import WhisperTwilioStream
from llm_convo.audio_input import WhisperTwilioStream


XML_MEDIA_STREAM = """
Expand All @@ -36,21 +36,38 @@ def __init__(self, remote_host: str, port: int, static_dir: str):
self.on_session = None

account_sid = os.environ["TWILIO_ACCOUNT_SID"]
auth_token = os.environ["TWILIO_AUTH_TOKEN"]
# auth_token = os.environ["TWILIO_AUTH_TOKEN"]
self.from_phone = os.environ["TWILIO_PHONE_NUMBER"]
self.client = Client(account_sid, auth_token)
# self.client = Client(account_sid, auth_token)
api_key = os.environ["TWILIO_API_KEY"] # might just be SID
api_secret = os.environ["TWILIO_API_SECRET"] # dami gave
Copy link

@Abe-Telo Abe-Telo Aug 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove these lines? We do not need API keys, API Secret, do we?

account_sid = os.environ["TWILIO_ACCOUNT_SID"] # test from twilio
print("api_key: ", api_key)
print("api_secret: ", api_secret)
print("account_sid: ", account_sid)
self.client = Client(api_key, api_secret, account_sid)

@self.app.route("/audio/<key>")
def audio(key):
return send_from_directory(self.static_dir, str(int(key)) + ".mp3")

@self.app.route("/test")
def test():
return "hello world"

@self.app.route("/incoming-voice", methods=["POST"])
def incoming_voice():
logging.info("Incoming call")
return XML_MEDIA_STREAM.format(host=self.remote_host)

@self.sock.route("/audiostream", websocket=True)
def on_media_stream(ws):
session = TwilioCallSession(ws, self.client, remote_host=self.remote_host, static_dir=self.static_dir)
session = TwilioCallSession(
ws,
self.client,
remote_host=self.remote_host,
static_dir=self.static_dir,
)
if self.on_session is not None:
thread = threading.Thread(target=self.on_session, args=(session,))
thread.start()
Expand Down
Loading