├── .gitignore ├── README.md ├── low_code └── old_ui │ └── README.md ├── server ├── Node_Examples │ └── AI_Calendar_Demo │ │ ├── .env.example │ │ ├── README.md │ │ ├── SWML_JSON.json │ │ ├── SignalwireML.js │ │ ├── app.js │ │ ├── package-lock.json │ │ └── package.json ├── Perl_Examples │ ├── Babelfish │ │ ├── Procfile │ │ ├── app.pl │ │ ├── assets │ │ │ ├── babelfish.png │ │ │ ├── icon.png │ │ │ └── signalwire.png │ │ ├── cpanfile │ │ ├── css │ │ │ ├── events.css │ │ │ └── index.css │ │ ├── env.example │ │ ├── js │ │ │ ├── events.js │ │ │ ├── index.js │ │ │ ├── languages.json │ │ │ └── voices.json │ │ ├── start_babelfish.sh │ │ └── template │ │ │ ├── events.tmpl │ │ │ └── index.tmpl │ ├── BobbysTable.ai │ │ ├── .buildpacks │ │ ├── .env │ │ ├── Procfile │ │ ├── README.md │ │ ├── app.pl │ │ ├── assets │ │ │ ├── bobbystable.png │ │ │ ├── crintaspot.png │ │ │ ├── dinedash.png │ │ │ ├── mealzoom.png │ │ │ ├── rapidfeast.png │ │ │ └── style.css │ │ ├── bobbystable.sql │ │ ├── cpanfile │ │ ├── post_prompt.md │ │ ├── prompt.md │ │ └── template │ │ │ ├── conversation.tmpl │ │ │ ├── conversations.tmpl │ │ │ ├── index.tmpl │ │ │ └── update.tmpl │ ├── FlosFlowers2 │ │ ├── .buildpacks │ │ ├── .env │ │ ├── Procfile │ │ ├── README.md │ │ ├── app.pl │ │ ├── assets │ │ │ ├── box.wav │ │ │ ├── flosflowers.png │ │ │ ├── sending.wav │ │ │ └── style.css │ │ ├── cpanfile │ │ ├── post_prompt.md │ │ ├── prompt.md │ │ └── template │ │ │ ├── conversation.tmpl │ │ │ ├── conversations.tmpl │ │ │ └── index.tmpl │ ├── MFA │ │ ├── README.md │ │ ├── function_send_mfa.md │ │ ├── function_send_mfa_argument.md │ │ ├── function_verify_mfa.md │ │ ├── function_verify_mfa_argument.md │ │ └── prompt.md │ ├── README.md │ ├── Roomie_Serve │ │ ├── .buildpacks │ │ ├── .env │ │ ├── Procfile │ │ ├── README.md │ │ ├── app.pl │ │ ├── assets │ │ │ ├── cai.png │ │ │ ├── cai.webp │ │ │ ├── roomieserve.webp │ │ │ └── style.css │ │ ├── backend.pl │ │ ├── cpanfile │ │ ├── menu.sql │ │ ├── post_prompt.md │ │ ├── prompt.md │ │ ├── roomie.sql │ │ ├── swaig_cli │ │ └── template │ │ │ ├── conversation.tmpl │ │ │ ├── conversations.tmpl │ │ │ └── index.tmpl │ ├── Zen │ │ ├── .buildpacks │ │ ├── Procfile │ │ ├── README.md │ │ ├── ai-zen_data.sql │ │ ├── ai-zen_schema.sql │ │ ├── app.pl │ │ ├── assets │ │ │ ├── style.css │ │ │ └── zen.png │ │ ├── cpanfile │ │ ├── function_responses.md │ │ ├── modem_diagnostics.md │ │ ├── modem_swap.md │ │ ├── post_prompt.md │ │ ├── prompt.md │ │ ├── speed_test.md │ │ ├── template │ │ │ ├── conversation.tmpl │ │ │ ├── conversations.tmpl │ │ │ └── index.tmpl │ │ └── verify_customer.md │ └── aical │ │ ├── Procfile │ │ ├── README.md │ │ ├── SWML.json │ │ ├── app.pl │ │ ├── assets │ │ ├── calendar.png │ │ ├── calendar_logo.png │ │ └── style.css │ │ ├── cpanfile │ │ ├── data │ │ ├── timezones.conf.xml │ │ ├── timezones.txt │ │ └── tz_to_txt.pl │ │ └── template │ │ ├── error.tmpl │ │ ├── index.tmpl │ │ ├── privacy.tmpl │ │ ├── settings.tmpl │ │ ├── success.tmpl │ │ └── tos.tmpl ├── Python_Examples │ ├── bobbystable │ │ ├── Procfile │ │ ├── README.md │ │ ├── SWML.json │ │ ├── SWML.yaml │ │ ├── app.py │ │ ├── env.example │ │ ├── post_prompt.md │ │ ├── prompt.md │ │ ├── requirements.txt │ │ ├── reservation_system.py │ │ ├── runtime.txt │ │ ├── static │ │ │ ├── img │ │ │ │ └── bobbystable.png │ │ │ └── reservation.html │ │ ├── swaig_cli │ │ ├── swaig_cli.1 │ │ └── test_reservation_system.py │ ├── dental_office │ │ ├── .replit │ │ ├── README.md │ │ ├── prompt.md │ │ ├── requirements.txt │ │ ├── setup_all_with_ngrok.sh │ │ └── setup_all_with_replit.sh │ ├── moviebot │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── Procfile │ │ ├── README.md │ │ ├── SETUP.md │ │ ├── app.py │ │ ├── env.example │ │ ├── moviebot.html │ │ ├── moviebot.swml.json │ │ ├── prompt.md │ │ ├── requirements.txt │ │ ├── runtime.txt │ │ └── signalwire-agent-example │ │ │ ├── moviebot.md │ │ │ ├── moviebot.py │ │ │ └── tmdb_api.py │ └── zen_cable │ │ ├── .gitignore │ │ ├── .replit │ │ ├── README.md │ │ ├── app.py │ │ ├── init_db.py │ │ ├── init_test_data.py │ │ ├── mfa_util.py │ │ ├── prompt.md │ │ ├── replit.nix │ │ ├── requirements.txt │ │ ├── schema.sql │ │ ├── setup.bat │ │ ├── setup.sh │ │ ├── static │ │ ├── css │ │ │ └── style.css │ │ └── sw_cable.png │ │ └── templates │ │ ├── appointments.html │ │ ├── billing.html │ │ ├── dashboard.html │ │ ├── forgot_password.html │ │ ├── login.html │ │ ├── populate.html │ │ └── settings.html ├── README.md └── tools │ └── tap │ ├── README.md │ ├── SWML.json │ ├── prompt.md │ ├── tap.py │ └── web_tap.py └── serverless ├── Bartender ├── README.md ├── SWML.json └── SWML.yaml ├── Dr_Bob_Confirm ├── README.md ├── SWML1.json └── SWML2.json ├── ESP8266_Temperature_and_Humidity_Sensor_Bot ├── README.md ├── SWML.json ├── SWML.yaml └── arduino_sketch │ ├── README.md │ ├── WeatherStationFonts.h │ ├── Weather_StationImages.h │ └── temperature_sensor_humidity.ino ├── Flos_Flowers ├── README.md ├── SWML.json ├── SWML.yaml └── prompt.md ├── Multilingual_Support_Agent └── SWML.json ├── OpenWeather_Assistant ├── README.md ├── SWML.json └── SWML.yml ├── README.md ├── Santa ├── SWML.json └── SWML.yaml ├── Sigmond ├── README.md ├── SWML.json └── SWML.yaml ├── Thermal_Thrillers ├── README.md ├── SWML.json ├── SWML.yaml ├── post_prompt.md └── prompt.md └── Weather_Bot ├── README.md ├── SWML.json └── SWML.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore node_modules directory 2 | **/node_modules/ 3 | 4 | # Ignore environment variables file (if any) 5 | .env 6 | 7 | # Ignore npm debug log file 8 | npm-debug.log 9 | 10 | # Ignore generated log files 11 | logs/ 12 | *.log 13 | 14 | # Ignore OS or editor-specific files 15 | .DS_Store 16 | Thumbs.db 17 | 18 | # Ignore files generated by package managers 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | # Ignore local configuration 24 | /config/local.js 25 | 26 | # Ignore files in build directories 27 | /build/ 28 | 29 | # Ignore production dependencies 30 | /prod_modules/ 31 | 32 | # Ignore IDE/Editor files 33 | .vscode/ 34 | .idea/ 35 | 36 | # Ignore node.js process files 37 | *.pid 38 | 39 | # Ignore coverage directory 40 | /coverage/ 41 | 42 | # Ignore node debug directory 43 | /.node_debug/ 44 | 45 | # Ignore test coverage output 46 | /coverage 47 | 48 | # Ignore any dependency directories for other languages/tools 49 | # Example: Ignore Python virtual environment 50 | venv/ 51 | 52 | calender_database.db 53 | -------------------------------------------------------------------------------- /low_code/old_ui/README.md: -------------------------------------------------------------------------------- 1 | # Low Code 2 | 3 | ## Introduction 4 | 5 | 6 | Low Code provides an option to utilize the AI Agent left side menu tab located at [SignalWire dashboard](https://id.signalwire.com/login/session/new). Here you can define what you want your digital employee to be and what tasks it will perform. 7 | 8 | 9 | ## Name and Personality 10 | 11 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/12d3fdda-ddaf-4538-a213-58f3fd3f1824) 12 | 13 | 14 | 15 | 1. Name: 16 | - This is the name the digital Employee will use. 17 | 2. Introduction: 18 | - An introduction from the digital employee to the user. 19 | 3. Languages: 20 | - The different languages the digital employee can speak. 21 | 4. Personality: 22 | - Will provide a type of personallity the digital employee will be like. 23 | 5. Enable Conversation Transcriptions: 24 | - Enables the transcription of the conversation between the digital employee and the user. 25 | 26 | ### Advanced Config 27 | 28 | 1. Inactivity Timeout: 29 | - Amount of time before timeout. 30 | 2. Background File: 31 | - A url to a sound file to play during the conversation. 32 | 3. Background File Volume: 33 | - How loud or quiet the background sound is. 34 | 4. Local Time Zone: 35 | - local timezone the digital employee is in. 36 | 37 | 38 | 39 | 40 | 41 | ## Skills and Behavior 42 | 43 | The Digital Employee low code option has the following skills to choose from. 44 | 45 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/28db6c90-d208-4d47-a310-46ac08a57c43) 46 | 47 | 48 | 1. Transfer Calls: 49 | - Transfer to a target number. 50 | 2. Send SMS/MMS: 51 | - Send an SMS or MMS to a target number. **(Please be sure the phone number from your SignalWire space is on a campain)** 52 | 3. Hours of Operation: 53 | - Define what the hours of operation are so our digital employee has the information it needs if the user asks this specific question 54 | 4. Custom knowledge or behavior . 55 | - Define anything custom you want the digital employee to know. 56 | - Example: A food menu from a restaurant. List what is on the menu and the prices. 57 | 5. Manage Zendesk Ticket: 58 | - Interact with a Zendesk Ticketing system. 59 | 60 | 61 | 62 | ## Conversation Flow 63 | 64 | Define steps the digital employee will take to guide the conversion. 65 | 66 | ### Example: 67 | 68 | A restaurant gets frequent calls about what is on the menu. You can create conversational steps for the digital employee to follow. 69 | 70 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/55f8a37e-72d7-4178-91f7-807bdb9316ad) 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /server/Node_Examples/AI_Calendar_Demo/.env.example: -------------------------------------------------------------------------------- 1 | REMOTE_USER= 2 | CLIENT_ID= 3 | CLIENT_SECRET= 4 | DEBUG=true 5 | REDIRECT_URL=http://localhost:3000 -------------------------------------------------------------------------------- /server/Node_Examples/AI_Calendar_Demo/README.md: -------------------------------------------------------------------------------- 1 | # SignalWire AI Agent Calendar Demo Application 2 | 3 | Facilitate the creation of Google Calendar meetings seamlessly over the phone by dialing into an AI-enabled phone number supplied by SignalWire. This phone number is tailored to execute a SWML script, streamlining the process of scheduling meetings directly from your call 4 | 5 | ## Table of Contents 6 | 7 | - [Features](#features) 8 | - [Usage](#usage) 9 | - [Configuration](#Configuration) 10 | 11 | ## Features 12 | 13 | - Enables users to schedule Google Calendar meetings via phone call. 14 | - Utilizes AI-enabled phone numbers provided by SignalWire. 15 | - Configured with SWML script for efficient meeting scheduling. 16 | 17 | 18 | ## Usage 19 | 20 | 1. Clone the repository to your local machine. 21 | 2. Go to ./server/Node_Examples/AI_Calendar_Demo/ 22 | 3. Set up your environment variables by creating a .env file and adding the required configurations (refer to the .env.example for reference). 23 | 4. Run the application using node app.js. 24 | 5. Access the application through your preferred web browser. 25 | 26 | ## Configuration 27 | 28 | 1. Open http://localhost:3000/login select your Google account and click on continue to update Google OAuth tokens in the database 29 | 2. copy the Ngrok URL from the console open your Signalwire space and click on the Phone number tab 30 | 3. Click on the Phone number to configure the SWML script URL 31 | 4. Click on edit settings and change the `* ACCEPT INCOMING CALLS AS` drop down to Voice calls and `* HANDLE CALLS USING` a SWML Script 32 | 5. Place your Ngrok ULR along with a path like https://abc.ngrok-free.app/main_webhook in `* WHEN A CALL COMES IN:` textbox and click on the Save button 33 | 6. Now make the call to your signalwire number to create a Google Calendar event 34 | 35 | 36 | -------------------------------------------------------------------------------- /server/Node_Examples/AI_Calendar_Demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ai_calendar_demo", 3 | "version": "1.0.0", 4 | "description": "SignalWire AI Agent Calendar Demo Application", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "basic-auth": "^2.0.1", 13 | "dotenv": "^16.4.5", 14 | "express": "^4.18.2", 15 | "google-auth-library": "^9.6.3", 16 | "googleapis": "^134.0.0", 17 | "js-yaml": "^4.1.0", 18 | "moment-timezone": "^0.5.45", 19 | "ngrok": "^5.0.0-beta.2", 20 | "nodemon": "^3.1.0", 21 | "sqlite3": "^5.1.7", 22 | "uuid": "^9.0.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/Procfile: -------------------------------------------------------------------------------- 1 | web: perl ./app.pl daemon --listen "http://*:$PORT" 2 | 3 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/assets/babelfish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/Babelfish/assets/babelfish.png -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/Babelfish/assets/icon.png -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/assets/signalwire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/Babelfish/assets/signalwire.png -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/cpanfile: -------------------------------------------------------------------------------- 1 | requires 'SignalWire::RestAPI', '1.7'; 2 | requires 'SignalWire::ML', '1.20'; 3 | requires 'LWP::Protocol::https', '6.10'; 4 | requires 'Plack', '1.0050'; 5 | requires 'Plack::App::WebSocket', '0.08'; 6 | requires 'Twiggy', '0.1026'; 7 | requires 'Data::Dumper', '2.174'; 8 | requires 'LWP::UserAgent', '6.70'; 9 | requires 'JSON::PP', '4.16'; 10 | requires 'URI::Encode', 'v1.1.1'; 11 | requires 'HTTP::Request::Common', '6.44'; 12 | requires 'HTML::Template::Expr', '0.07'; 13 | requires 'DateTime', '1.59'; 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/css/events.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | background-color: #f4f4f4; 4 | margin: 0; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | justify-content: center; 9 | height: 100vh; 10 | } 11 | 12 | .container { 13 | background-color: white; 14 | padding: 20px; 15 | border-radius: 10px; 16 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 17 | text-align: center; 18 | width: 600px; 19 | } 20 | 21 | textarea.events { 22 | width: 100%; 23 | height: 600px; 24 | margin-bottom: 20px; 25 | padding: 10px; 26 | border: 1px solid #ccc; 27 | border-radius: 5px; 28 | box-sizing: border-box; 29 | overflow-y: scroll; 30 | } 31 | 32 | .button-group { 33 | display: flex; 34 | justify-content: space-between; 35 | } 36 | 37 | .button { 38 | background-color: #007BFF; 39 | color: white; 40 | padding: 10px; 41 | border: none; 42 | border-radius: 5px; 43 | cursor: pointer; 44 | width: 125px; /* Adjust width as needed */ 45 | } 46 | 47 | .button:hover { 48 | background-color: #0056b3; 49 | } 50 | 51 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/css/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | background-color: #f4f4f4; 4 | margin: 0; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | justify-content: center; 9 | height: 100vh; 10 | } 11 | 12 | .container { 13 | background-color: white; 14 | padding: 20px; 15 | border-radius: 10px; 16 | box-shadow: 0 4px 8px rgba(0,0,0,0.1); 17 | text-align: center; 18 | width: 400px; /* Adjust width */ 19 | } 20 | 21 | .logo { 22 | max-width: 100%; 23 | width: calc(100% - 50px); 24 | height: auto; 25 | margin-bottom: 20px; 26 | } 27 | 28 | form label { 29 | display: block; 30 | margin: 10px 0 5px; 31 | } 32 | 33 | form select, form input { 34 | width: calc(100% - 22px); 35 | padding: 10px; 36 | margin: 0 auto 10px; 37 | border: 1px solid #ccc; 38 | border-radius: 5px; 39 | } 40 | 41 | form button, #hangupButton { 42 | background-color: #007BFF; 43 | color: white; 44 | padding: 10px; 45 | border: none; 46 | border-radius: 5px; 47 | cursor: pointer; 48 | display: block; 49 | width: 105px; /* Increased width by 25px */ 50 | margin: 10px auto 0; 51 | } 52 | 53 | form button.disabled { 54 | background-color: grey; 55 | cursor: not-allowed; 56 | } 57 | 58 | .hidden { 59 | display: none; 60 | } 61 | 62 | .advanced-toggle { 63 | cursor: pointer; 64 | color: #007BFF; 65 | text-decoration: underline; 66 | margin: 10px 0; 67 | } 68 | 69 | .advanced-options { 70 | margin-top: 20px; 71 | } 72 | 73 | .advanced-option { 74 | text-align: center; 75 | margin-bottom: 10px; 76 | } 77 | 78 | .advanced-option label { 79 | display: block; 80 | } 81 | 82 | .advanced-option input { 83 | margin-top: 5px; 84 | } 85 | 86 | /* The switch - the box around the slider */ 87 | .switch { 88 | position: relative; 89 | display: inline-block; 90 | width: 60px; 91 | height: 34px; 92 | } 93 | 94 | /* Hide default HTML checkbox */ 95 | .switch input { 96 | opacity: 0; 97 | width: 0; 98 | height: 0; 99 | } 100 | 101 | /* The slider */ 102 | .slider { 103 | position: absolute; 104 | cursor: pointer; 105 | top: 0; 106 | left: 0; 107 | right: 0; 108 | bottom: 0; 109 | background-color: #ccc; 110 | -webkit-transition: .4s; 111 | transition: .4s; 112 | } 113 | 114 | .slider:before { 115 | position: absolute; 116 | content: ""; 117 | height: 26px; 118 | width: 26px; 119 | left: 4px; 120 | bottom: 4px; 121 | background-color: white; 122 | -webkit-transition: .4s; 123 | transition: .4s; 124 | } 125 | 126 | input:checked + .slider { 127 | background-color: #2196F3; 128 | } 129 | 130 | input:focus + .slider { 131 | box-shadow: 0 0 1px #2196F3; 132 | } 133 | 134 | input:checked + .slider:before { 135 | -webkit-transform: translateX(26px); 136 | -ms-transform: translateX(26px); 137 | transform: translateX(26px); 138 | } 139 | 140 | /* Rounded sliders */ 141 | .slider.round { 142 | border-radius: 34px; 143 | } 144 | 145 | .slider.round:before { 146 | border-radius: 50%; 147 | } 148 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/env.example: -------------------------------------------------------------------------------- 1 | FROM_NUMBER=+15555555555 2 | ACCOUNT_SID=xxx 3 | AUTH_TOKEN=xxx 4 | SPACE_NAME=name 5 | DOMAIN_NAME=signalwire.com 6 | 7 | 8 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/js/events.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | const eventsTextarea = document.getElementById('events'); 3 | const homeButton = document.getElementById('homeButton'); 4 | const hangupButton = document.getElementById('hangupButton'); 5 | const messageInput = document.getElementById("message-input"); 6 | 7 | homeButton.addEventListener('click', function() { 8 | window.location.href = '/'; 9 | }); 10 | 11 | hangupButton.addEventListener('click', function() { 12 | // Send hangup request 13 | const uuid = getUrlParameter('uuid'); 14 | if (uuid) { 15 | fetch('/hangup', { 16 | method: 'POST', 17 | headers: { 18 | 'Content-Type': 'application/json' 19 | }, 20 | body: JSON.stringify({ uuid: uuid }) 21 | }) 22 | .then(response => response.json()) 23 | .then(data => { 24 | console.log('Hangup successful:', data); 25 | window.location.href = '/'; 26 | }) 27 | .catch(error => { 28 | console.error('Error:', error); 29 | window.location.href = '/'; 30 | }); 31 | } 32 | }); 33 | 34 | // Function to get URL parameters 35 | function getUrlParameter(name) { 36 | const params = new URLSearchParams(window.location.search); 37 | return params.get(name); 38 | } 39 | 40 | // Get UUID from URL parameters 41 | const uuid = getUrlParameter('uuid'); 42 | 43 | // Establish WebSocket connection 44 | const ws = new WebSocket(`wss://${window.location.host}/websocket`); 45 | 46 | if (uuid) { 47 | 48 | ws.onopen = function() { 49 | console.log('WebSocket connection established'); 50 | // Send subscription message 51 | ws.send(JSON.stringify({ action: 'subscribe', uuid: uuid })); 52 | }; 53 | 54 | ws.onmessage = function(event) { 55 | const data = JSON.parse(event.data); 56 | const formattedMessage = JSON.stringify(data, null, 2); 57 | eventsTextarea.value += formattedMessage + '\n\n'; 58 | eventsTextarea.scrollTop = eventsTextarea.scrollHeight; 59 | }; 60 | 61 | ws.onclose = function() { 62 | const closeMessage = 'Connection closed'; 63 | eventsTextarea.value += closeMessage + '\n'; 64 | eventsTextarea.scrollTop = eventsTextarea.scrollHeight; 65 | }; 66 | 67 | ws.onerror = function(error) { 68 | console.error('WebSocket error:', error); 69 | }; 70 | } else { 71 | console.error('UUID not found in URL'); 72 | } 73 | 74 | function appendMessage(message) { 75 | eventsTextarea.value += message + "\n"; 76 | eventsTextarea.scrollTop = eventsTextarea.scrollHeight; 77 | } 78 | 79 | function sendMessage() { 80 | var message = messageInput.value.trim(); 81 | if (message && ws && ws.readyState === WebSocket.OPEN) { 82 | var data = { 83 | uuid: uuid, 84 | timestamp: new Date().toISOString(), 85 | message: message 86 | }; 87 | ws.send(JSON.stringify(data)); 88 | appendMessage("Sent:\n" + JSON.stringify(data, null, 2)); 89 | messageInput.value = ""; 90 | } 91 | } 92 | 93 | document.getElementById("message-input").addEventListener("keypress", function(event) { 94 | if (event.keyCode === 13) { 95 | event.preventDefault(); 96 | sendMessage(); 97 | } 98 | }); 99 | 100 | }); 101 | 102 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/js/languages.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "code": "bg", "name": "Bulgarian" }, 3 | { "code": "ca", "name": "Catalan" }, 4 | { "code": "zh", "name": "Chinese (Mandarin, Simplified)" }, 5 | { "code": "zh-CN", "name": "Chinese (Mandarin, Simplified)" }, 6 | { "code": "zh-Hans", "name": "Chinese (Mandarin, Simplified)" }, 7 | { "code": "zh-TW", "name": "Chinese (Mandarin, Traditional)" }, 8 | { "code": "zh-Hant", "name": "Chinese (Mandarin, Traditional)" }, 9 | { "code": "cs", "name": "Czech" }, 10 | { "code": "da", "name": "Danish" }, 11 | { "code": "da-DK", "name": "Danish" }, 12 | { "code": "nl", "name": "Dutch" }, 13 | { "code": "en", "name": "English" }, 14 | { "code": "en-US", "name": "English (US)" }, 15 | { "code": "en-AU", "name": "English (AU)" }, 16 | { "code": "en-GB", "name": "English (GB)" }, 17 | { "code": "en-NZ", "name": "English (NZ)" }, 18 | { "code": "en-IN", "name": "English (IN)" }, 19 | { "code": "et", "name": "Estonian" }, 20 | { "code": "fi", "name": "Finnish" }, 21 | { "code": "nl-BE", "name": "Flemish" }, 22 | { "code": "fr", "name": "French" }, 23 | { "code": "fr-CA", "name": "French (CA)" }, 24 | { "code": "de", "name": "German" }, 25 | { "code": "de-CH", "name": "German (Switzerland)" }, 26 | { "code": "el", "name": "Greek" }, 27 | { "code": "hi", "name": "Hindi" }, 28 | { "code": "hu", "name": "Hungarian" }, 29 | { "code": "id", "name": "Indonesian" }, 30 | { "code": "it", "name": "Italian" }, 31 | { "code": "ja", "name": "Japanese" }, 32 | { "code": "ko", "name": "Korean" }, 33 | { "code": "ko-KR", "name": "Korean" }, 34 | { "code": "lv", "name": "Latvian" }, 35 | { "code": "lt", "name": "Lithuanian" }, 36 | { "code": "ms", "name": "Malay" }, 37 | { "code": "multi", "name": "Multilingual (Spanish + English)" }, 38 | { "code": "no", "name": "Norwegian" }, 39 | { "code": "pl", "name": "Polish" }, 40 | { "code": "pt", "name": "Portuguese" }, 41 | { "code": "pt-BR", "name": "Portuguese (BR)" }, 42 | { "code": "ro", "name": "Romanian" }, 43 | { "code": "ru", "name": "Russian" }, 44 | { "code": "sk", "name": "Slovak" }, 45 | { "code": "es", "name": "Spanish" }, 46 | { "code": "es-419", "name": "Spanish (Latin America)" }, 47 | { "code": "sv", "name": "Swedish" }, 48 | { "code": "sv-SE", "name": "Swedish" }, 49 | { "code": "th", "name": "Thai" }, 50 | { "code": "th-TH", "name": "Thai" }, 51 | { "code": "tr", "name": "Turkish" }, 52 | { "code": "uk", "name": "Ukrainian" }, 53 | { "code": "vi", "name": "Vietnamese" } 54 | ] 55 | 56 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/js/voices.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "elevenlabs.EXAVITQu4vr4xnSDxMaL", 4 | "name": "Sarah" 5 | }, 6 | { 7 | "id": "elevenlabs.FGY2WhTYpPnrIDTdsKH5", 8 | "name": "Laura" 9 | }, 10 | { 11 | "id": "elevenlabs.IKne3meq5aSn9XLyUdCD", 12 | "name": "Charlie" 13 | }, 14 | { 15 | "id": "elevenlabs.JBFqnCBsd6RMkjVDRZzb", 16 | "name": "George" 17 | }, 18 | { 19 | "id": "elevenlabs.N2lVS1w4EtoT3dr4eOWO", 20 | "name": "Callum" 21 | }, 22 | { 23 | "id": "elevenlabs.TX3LPaxmHKxFdv7VOQHJ", 24 | "name": "Liam" 25 | }, 26 | { 27 | "id": "elevenlabs.XB0fDUnXU5powFXDhCwa", 28 | "name": "Charlotte" 29 | }, 30 | { 31 | "id": "elevenlabs.Xb7hH8MSUJpSbSDYk0k2", 32 | "name": "Alice" 33 | }, 34 | { 35 | "id": "elevenlabs.XrExE9yKIg1WjnnlVkGX", 36 | "name": "Matilda" 37 | }, 38 | { 39 | "id": "elevenlabs.bIHbv24MWmeRgasZH58o", 40 | "name": "Will" 41 | }, 42 | { 43 | "id": "elevenlabs.cgSgspJ2msm6clMCkdW9", 44 | "name": "Jessica" 45 | }, 46 | { 47 | "id": "elevenlabs.cjVigY5qzO86Huf0OWal", 48 | "name": "Eric" 49 | }, 50 | { 51 | "id": "elevenlabs.iP95p4xoKVk53GoZ742B", 52 | "name": "Chris" 53 | }, 54 | { 55 | "id": "elevenlabs.onwK4e9ZLuTAKqWW03F9", 56 | "name": "Daniel" 57 | }, 58 | { 59 | "id": "elevenlabs.pFZP5JQG7iQjIQuC4Bku", 60 | "name": "Lily" 61 | }, 62 | { 63 | "id": "elevenlabs.pqHfZKP75CvOlQylNhV4", 64 | "name": "Bill" 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/start_babelfish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | GITDIR=/usr/local/digital_employees 4 | WORKINGDIR=/usr/local/digital_employees/server/Perl_Examples/Babelfish 5 | echo -e "====================================" 6 | echo -e " " 7 | echo -e "Don't forget to cp -r env.example .env and edit the .env variables in ${WORKINGDIR}" 8 | echo -e " " 9 | echo -e "====================================" 10 | 11 | source .env 12 | 13 | #read -p "Please enter a FROM number associated with your SignalWire space. Formatted as +15551234567: " FROM_NUMBER 14 | 15 | # Clone 16 | if [ ! -d ${GITDIR} ]; then 17 | echo -ne "Downloading Source Code... " 18 | git clone --quiet https://github.com/signalwire/digital_employees.git ${GITDIR} > /dev/null 19 | echo -e "Complete!" 20 | else 21 | echo "Source code already exists, skipping download" 22 | fi 23 | 24 | cd ${WORKINGDIR} 25 | 26 | ## CPAN 27 | # NOTE: 28 | # Occasionally the C module will fail to install. Re-running seems to resolve the issue. 29 | # If the module fails, a re-run of this wrapper command should resolve and start the app. 30 | # 31 | echo -ne "Installing Perl Dependencies. This may take a few minutes... " 32 | cpanm --installdeps ${WORKINGDIR} > /dev/null 2>&1 33 | # Retry incase something failed (yes, this is hacky, but C seems to fail randomly) 34 | cpanm --installdeps ${WORKINGDIR} > /dev/null 2>&1 35 | cpanm Carton > /dev/null 2>&1 36 | carton install 37 | echo -e "Complete!" 38 | 39 | 40 | ## APPLICATION 41 | # create symlink from WORKINGDIR to /app 42 | 43 | rm /app 44 | if [ ! -h /app ]; then 45 | ln -s ${WORKINGDIR} /app > /dev/null 2>&1 46 | fi 47 | 48 | # Set the Application ENV 49 | export DEBUG=0 50 | #export FROM_NUMBER=${FROM_NUMBER} 51 | export SAVE_BLANK_CONVERSATIONS=1 52 | #export API_VERSION=api/relay/rest 53 | export TOP_P=0.6 54 | export TEMPERATURE=0.6 55 | echo -e "====================================" 56 | 57 | echo -e "====================================" 58 | # 59 | 60 | NGROK_HOST=${NGROK_URL:8} # Strip out https:// for now. 61 | RELAY_URL="https://${NGROK_HOST}/swml" 62 | 63 | carton exec perl ${WORKINGDIR}/app.pl 64 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/template/events.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Live Events 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

Live Events

20 | 21 |
22 |
23 |

24 |
25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /server/Perl_Examples/Babelfish/template/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Babelfish 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |

Babelfish

20 |
21 | 22 | 25 | 26 | 27 | 30 | 31 | 32 | 35 | 36 | 37 | 40 | 41 | 42 |
43 | 44 |
45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
Advanced Options
54 | 66 |
67 | 68 | 73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/.buildpacks: -------------------------------------------------------------------------------- 1 | https://github.com/polettix/heroku-buildpack-perl-procfile.git 2 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL=postgres://postgres:${DB_PASS}@localhost:5432/bobbystable 2 | USERNAME=bobbystable 3 | PASSWORD=$(openssl rand -base64 10 | tr -dc 'a-zA-Z0-9') 4 | DEBUG=1 5 | BOBBYSTABLE=${PHONE_NUMBER} 6 | TOP_P=0.6 7 | TEMPERATURE=0.6 8 | ASSISTANT=${PHONE_NUMBER} 9 | SAVE_BLANK_CONVERSATIONS=1 10 | API_VERSION=api/relay/rest -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/Procfile: -------------------------------------------------------------------------------- 1 | web: perl ./app.pl daemon --listen "http://*:$PORT" 2 | 3 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/README.md: -------------------------------------------------------------------------------- 1 | # Bobbystable.ai 2 | https://Bobbystable.ai AI Reservation bot powered by [Signalwire](https://signalwire.com/?utm_source=bobbystable.ai) 3 | 4 | Live demo: Call `+1-754-432-6229` 5 | 6 | Welcome to Bobbystable.ai. Bobbystable.ai is a functional demo of a restaurant reservation system using Signalwire's APIs. 7 | 8 | In this repository, you will find the full prompt used, along with all the functions. The functions are written in Perl. 9 | 10 | In addition to what is included in this repository, you will also need a web server (NGINX), a database (PostgreSQL), Signalwire's APIs, and a registered/apporoved campaign for SMS. 11 | 12 | ### Docker Container 13 | #### To use Bobbystable.ai with a docker container please see [WireStarter](https://github.com/signalwire/WireStarter) 14 | 15 | ------------------- 16 | 17 | The website is used to display the reservation details. This could be protected with a username and password, but it was left open to demonstrate the functionality. 18 | 19 | - `Date:` Reservation date. 20 | - `Time:` Reservation time. 21 | - `Party Size:` Number of guests. 22 | - `Guest Name:` Name of the guest. 23 | - `Guest Number:` Phone number of the guest. 24 | 25 | ![image](https://github.com/Len-PGH/Bobbystable.ai/assets/13131198/5a03a103-83df-495b-bc98-8de136fa5cdc) 26 | 27 | 28 | --------------------------- 29 | 30 | This is a backend interface gated by a login user name and password. 31 | 32 | - `user:` The caller's interaction. 33 | - `Assistant:` The AI bot's interaction. 34 | - `function:` This shows that the function was executed either correctly or incorrectly. 35 | - `system:` This is like the assistant but on a higher level. 36 | 37 | ![1705971341262](https://github.com/Len-PGH/Bobbystable.ai/assets/13131198/772e91b4-0338-41b3-aa33-29e5f295cc2d) 38 | 39 | 40 | ---------------------------- 41 | 42 | This is the text message sent that includes: 43 | 44 | - Reservation name. 45 | - Reservation date and time. 46 | - Instructions on how to update the reservation online with a unique link. 47 | - The option to STOP to help comply with sms regulation. 48 | 49 | ![1705971339655](https://github.com/Len-PGH/Bobbystable.ai/assets/13131198/1319f8e2-4cf2-4d8e-a1b1-e22ca9717649) 50 | 51 | 52 | --------------------- 53 | 54 | ### SignalWire 55 | 56 | #### SignalWire’s AI Agent for Voice allows you to build and deploy your own digital employee. Powered by advanced natural language processing (NLP) capabilities, your digital employee will understand caller intent, retain context, and generally behave in a way that feels “human-like”. In fact, you may find that it behaves exactly like your best employee, elevating the customer experience through efficient, intelligent, and personalized interactions. 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/assets/bobbystable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/BobbysTable.ai/assets/bobbystable.png -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/assets/crintaspot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/BobbysTable.ai/assets/crintaspot.png -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/assets/dinedash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/BobbysTable.ai/assets/dinedash.png -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/assets/mealzoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/BobbysTable.ai/assets/mealzoom.png -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/assets/rapidfeast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/BobbysTable.ai/assets/rapidfeast.png -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/assets/style.css: -------------------------------------------------------------------------------- 1 | .inline-form { 2 | display: inline-block; 3 | margin-right: 10px; 4 | } 5 | 6 | .pi-container { 7 | position: fixed; 8 | bottom: 10px; 9 | right: 10px; 10 | text-align: right; 11 | } 12 | 13 | .pi-symbol, .pi-link { 14 | font-size: small; 15 | color: #808080; 16 | text-decoration: none; 17 | } 18 | 19 | .pi-link:hover { 20 | text-decoration: underline; 21 | } 22 | 23 | .center-table { 24 | margin-left: auto; 25 | margin-right: auto; 26 | } 27 | 28 | body { 29 | font-family: Tahoma, Arial, sans-serif; 30 | color: #ffffff; 31 | background-color: #222222; 32 | } 33 | 34 | left-col { 35 | width: 20%; 36 | } 37 | 38 | .right-col { 39 | width: 80%; 40 | } 41 | 42 | table { 43 | width: 90%; 44 | margin: 0 auto; 45 | border: none; 46 | border-spacing: 0px; 47 | margin-left: auto; 48 | margin-right: auto; 49 | border-collapse: collapse; 50 | } 51 | 52 | table th, table td { 53 | border: none; 54 | text-align: left; 55 | } 56 | 57 | table td { 58 | padding: 10px; 59 | min-width: 100px; 60 | } 61 | 62 | h1, a { 63 | color: #000000; 64 | } 65 | 66 | h2 { 67 | text-align: center; 68 | color: #ffffff; 69 | } 70 | 71 | a { 72 | text-decoration: none; 73 | color: #50D2E9; /* If there's a conflict, choose one color */ 74 | } 75 | 76 | a:hover { 77 | color: #33CCFF; 78 | } 79 | 80 | button, .submit-button { 81 | font-size: 16px; 82 | color: #FFFFFF; 83 | background-color: #4CAF50; 84 | border: none; 85 | padding: 10px 20px; 86 | text-align: center; 87 | cursor: pointer; 88 | border-radius: 4px; 89 | } 90 | 91 | button, .edit-button { 92 | font-size: 16px; 93 | color: #FFFFFF; 94 | background-color: #4CAF50; 95 | border: none; 96 | padding: 10px 20px; 97 | text-align: center; 98 | cursor: pointer; 99 | border-radius: 4px; 100 | } 101 | 102 | button, .delete-button { 103 | font-size: 16px; 104 | color: #FFFFFF; 105 | background-color: #FF0000; 106 | border: none; 107 | padding: 10px 20px; 108 | text-align: center; 109 | cursor: pointer; 110 | border-radius: 4px; 111 | } 112 | 113 | button, .add-button { 114 | font-size: 16px; 115 | color: #FFFFFF; 116 | background-color: #4CAF50; 117 | border: none; 118 | padding: 10px 20px; 119 | text-align: center; 120 | cursor: pointer; 121 | border-radius: 4px; 122 | } 123 | 124 | button:hover, .submit-button:hover { 125 | background-color: #45a049; 126 | } 127 | 128 | header { 129 | background-color: #f2f2f2; 130 | padding: 10px; 131 | text-align: center; 132 | } 133 | 134 | header nav a { 135 | margin: 0 10px; 136 | text-decoration: none; 137 | color: #333; 138 | } 139 | 140 | .collapsible { 141 | cursor: pointer; 142 | text-align: center; 143 | width: auto; 144 | display: inline-block; 145 | } 146 | 147 | .content { 148 | display: none; 149 | overflow: hidden; 150 | background-color: #222222; 151 | margin: 0 auto; 152 | } 153 | 154 | .copy-button { 155 | text-align: center; 156 | width: auto; 157 | cursor: pointer; 158 | } 159 | 160 | .system { 161 | background-color: #872126; 162 | } 163 | 164 | .assistant { 165 | background-color: #364285; 166 | } 167 | 168 | .user { 169 | background-color: #236A1C; 170 | } 171 | 172 | .function { 173 | background-color: #CD00C4; 174 | } 175 | 176 | .center-table { 177 | margin-left: auto; 178 | margin-right: auto; 179 | border-collapse: collapse; 180 | } 181 | 182 | /* Optional: additional styling for the table */ 183 | .center-table, .center-table th, .center-table td { 184 | border: 0px; 185 | } 186 | 187 | .center-table th, .center-table td { 188 | padding: 8px; 189 | text-align: left; 190 | } 191 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/bobbystable.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO restaurants 2 | (name, open_time, close_time, days_open, capacity) 3 | VALUES 4 | ('Bobby''s Table', '09:00:00', '22:00:00', ARRAY['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], 50); 5 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/cpanfile: -------------------------------------------------------------------------------- 1 | requires 'File::Slurp', '9999.32'; 2 | requires 'SignalWire::RestAPI', '1.5'; 3 | requires 'SignalWire::ML', '1.20'; 4 | requires 'Time::Piece', '1.3401'; 5 | requires 'List::Util', '1.63'; 6 | requires 'LWP::Protocol::https', '6.10'; 7 | requires 'Plack', '1.0050'; 8 | requires 'Data::Dumper', '2.174'; 9 | requires 'LWP::UserAgent', '6.70'; 10 | requires 'JSON::PP', '4.16'; 11 | requires 'URL::Encode', '0.03'; 12 | requires 'HTTP::Request::Common', '6.44'; 13 | requires 'Env::C', '0.15'; 14 | requires 'DBI', '1.643'; 15 | requires 'DBD::Pg', '3.16.3'; 16 | requires 'HTML::Template::Expr', '0.07'; 17 | requires 'DateTime', '1.59'; 18 | requires 'UUID', '0.28'; 19 | requires 'MIME::Base64', '3.16'; 20 | requires 'Twiggy', '0.1026'; 21 | 22 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/post_prompt.md: -------------------------------------------------------------------------------- 1 | Summarize the conversation, including all the details. 2 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/prompt.md: -------------------------------------------------------------------------------- 1 | # Personality and Introduction 2 | 3 | You are a witty hostess and your name is Bobby. You work for bobby's table dot A eye. Greet the user with that information. 4 | 5 | # Your Skills, Knowledge, and Behavior 6 | 7 | # Address 8 | 327 Drop Table Lane 9 | Pompano Beach, FL 33060 10 | 11 | # Hours 12 | Open 2PM to 11PM, Monday through Saturday, Closed every Sunday. 13 | Same day reservations are allowed during business hours. 14 | 15 | # Check Availability 16 | Use function check_availability to check seat availability. 17 | If the time requested isn't available offer available times. 18 | 19 | # Create Reservation 20 | Use function create_reservation to create a reservation. 21 | Gather the user Reservation name, Reservation date, Reservation time and Reservation party size to Create a reservation. 22 | 23 | # Send Message 24 | Use function send_message to send a message. 25 | 26 | # Move Reservation 27 | Use the function move_reservation to find an existing reservation. 28 | Verify the reservation is the correct one. 29 | Then function move_reservation to move an existing reservation. 30 | 31 | # Lookup Reservation 32 | Use function lookup_reservation to find an existing reservation. 33 | You can only lookup a reservation by phone number. 34 | If you have difficulties understanding the number, you can ask the user to dial the number on their keypad. 35 | You can use the users phone number to look up a reservation, if none is found ask for the number the reservation is under. 36 | 37 | # Cancel Reservation 38 | Use the function lookup_reservation to find an existing reservation. 39 | Verify the reservation is the correct one. 40 | Then function cancel_reservation to cancel a reservation. 41 | 42 | # Conversation Flow 43 | These are the steps you need to follow during this conversation. Ensure you are strictly following the steps below in order. 44 | 45 | ## Step 1 46 | Ask how the user is today. Wait for the user to respond. 47 | 48 | ## Step 1.1 49 | Ask the user if they would like to create, move or cancel a reservation. 50 | 51 | ## Step 2 52 | If the user asks to create a reservation ask in sequence for: 53 | 54 | ## Step 2.1 55 | Ask for their name. 56 | ## Step 2.2 57 | Ask Party size. Can accomodate 16 or less. 58 | ## Step 2.3 59 | Ask Reservation date. 60 | ## Step 2.4 61 | Ask Reservation time. 62 | ## Step 2.5 63 | Ask if the number they are calling from is the same number they want to use for the reservation. 64 | ## Step 2.6 65 | Check for availability, if there is no availability offer other times then check those before proceeding. 66 | ## Step 2.7 67 | Create the reservation when the user agrees to the reservation date and time. 68 | 69 | ## Step 3 70 | Offer to send the user a message with the details of the reservation with reservation url link, messaging and data rates may apply. If user says yes then use the send_message function. 71 | 72 | ## Step 4 73 | If the user asks to update a reservation ask the user what the phone number is for the reservation, Repeat what the reservation name is to confirm with the user. 74 | ## Step 4.1 75 | Offer to send a message with the updated details. 76 | 77 | ## Step 5 78 | If the user asks to cancel a reservation ask the user why they want to cancel, Ask what the phone number is for the reservation. Repeat what the reservation name is to confirm with the user. 79 | ### Step 5.1 80 | Offer to send a message with the cancellation details. 81 | ### Step 5.2 82 | Repeat the reservation details to the user. 83 | 84 | 85 | ## Step 6 86 | When the user is ready to end the call, always say "Thank you for choosing bobby's table dot A I." then hangup. 87 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/template/conversation.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Conversation Details 5 | 6 | 7 | 8 | 9 |

Conversation Details

10 |
11 | 12 | 13 | 41 | 42 | 47 | 48 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
ID:
Start Date:
Name:
Number:
Input Tokens:
Output Tokens:
40 |
Recording: 43 | 44 | 45 | 46 |
49 |
50 |
51 | 52 | 53 | 58 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 101 | 106 | 107 |
54 | 55 | " class="button-style"> 56 | 57 | 59 | 60 | " class="button-style"> 61 | 62 |
RoleContent
FunctionArguments
97 | 98 | " class="button-style"> 99 | 100 | 102 | 103 | " class="button-style"> 104 | 105 |
108 |
109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /server/Perl_Examples/BobbysTable.ai/template/conversations.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Converstations 5 | 6 | 7 | 8 | 9 |

Conversations

10 |
11 | 12 | 15 | 18 |
13 |  ">< Prev 14 | 16 | ">Next >  17 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
CreatedNameNumberSummary
">
35 | 36 | 37 | 40 | 43 | 44 |
38 |  ">< Prev 39 | 41 | ">Next >  42 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/.buildpacks: -------------------------------------------------------------------------------- 1 | https://github.com/polettix/heroku-buildpack-perl-procfile.git 2 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL=postgres://postgres:${DB_PASS}@localhost:5432/flosflowers 2 | USERNAME=jobsearch 3 | PASSWORD=$(openssl rand -base64 10 | tr -dc 'a-zA-Z0-9') 4 | DEBUG=1 5 | JOBSEARCH=${PHONE_NUMBER} 6 | TOP_P=0.6 7 | TEMPERATURE=0.6 8 | ASSISTANT=${PHONE_NUMBER} 9 | SAVE_BLANK_CONVERSATIONS=1 10 | API_VERSION=api/relay/rest 11 | OPENAI_API_KEY=${OPENAI_API_KEY} 12 | 13 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/Procfile: -------------------------------------------------------------------------------- 1 | web: perl ./app.pl daemon --listen "http://*:$PORT" 2 | 3 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/README.md: -------------------------------------------------------------------------------- 1 | # FLO'S FLOWERS2 2 | SignalWire AI flower sending bot. This digital employee can send an SMS e-card with an image of any type of flower. Just tell Flo what kind of flowers to send. 3 | 4 | Live demo: Call `+1(660)835-6937` 5 | 6 | ### Docker Container 7 | #### To use FLO'S FLOWERS2 with a docker container please see [WireStarter](https://github.com/signalwire/WireStarter) 8 | 9 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/757d4e3a-570e-42d4-8acf-3f5ef4664341) 10 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/assets/box.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/FlosFlowers2/assets/box.wav -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/assets/flosflowers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/FlosFlowers2/assets/flosflowers.png -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/assets/sending.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/FlosFlowers2/assets/sending.wav -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/assets/style.css: -------------------------------------------------------------------------------- 1 | .inline-form { 2 | display: inline-block; 3 | margin-right: 10px; 4 | } 5 | 6 | .pi-container { 7 | position: fixed; 8 | bottom: 10px; 9 | right: 10px; 10 | text-align: right; 11 | } 12 | 13 | .pi-symbol, .pi-link { 14 | font-size: small; 15 | color: #808080; 16 | text-decoration: none; 17 | } 18 | 19 | .pi-link:hover { 20 | text-decoration: underline; 21 | } 22 | 23 | .center-table { 24 | margin-left: auto; 25 | margin-right: auto; 26 | } 27 | 28 | body { 29 | font-family: Tahoma, Arial, sans-serif; 30 | color: #ffffff; 31 | background-color: #222222; 32 | } 33 | 34 | left-col { 35 | width: 20%; 36 | } 37 | 38 | .right-col { 39 | width: 80%; 40 | } 41 | 42 | table { 43 | width: 90%; 44 | margin: 0 auto; 45 | border: none; 46 | border-spacing: 0px; 47 | margin-left: auto; 48 | margin-right: auto; 49 | border-collapse: collapse; 50 | } 51 | 52 | table th, table td { 53 | border: none; 54 | text-align: left; 55 | } 56 | 57 | table td { 58 | padding: 10px; 59 | min-width: 100px; 60 | } 61 | 62 | h1, a { 63 | color: #000000; 64 | } 65 | 66 | h2 { 67 | text-align: center; 68 | color: #ffffff; 69 | } 70 | 71 | a { 72 | text-decoration: none; 73 | color: #50D2E9; /* If there's a conflict, choose one color */ 74 | } 75 | 76 | a:hover { 77 | color: #33CCFF; 78 | } 79 | 80 | button, .submit-button { 81 | font-size: 16px; 82 | color: #FFFFFF; 83 | background-color: #4CAF50; 84 | border: none; 85 | padding: 10px 20px; 86 | text-align: center; 87 | cursor: pointer; 88 | border-radius: 4px; 89 | } 90 | 91 | button, .edit-button { 92 | font-size: 16px; 93 | color: #FFFFFF; 94 | background-color: #4CAF50; 95 | border: none; 96 | padding: 10px 20px; 97 | text-align: center; 98 | cursor: pointer; 99 | border-radius: 4px; 100 | } 101 | 102 | button, .delete-button { 103 | font-size: 16px; 104 | color: #FFFFFF; 105 | background-color: #FF0000; 106 | border: none; 107 | padding: 10px 20px; 108 | text-align: center; 109 | cursor: pointer; 110 | border-radius: 4px; 111 | } 112 | 113 | button, .add-button { 114 | font-size: 16px; 115 | color: #FFFFFF; 116 | background-color: #4CAF50; 117 | border: none; 118 | padding: 10px 20px; 119 | text-align: center; 120 | cursor: pointer; 121 | border-radius: 4px; 122 | } 123 | 124 | button:hover, .submit-button:hover { 125 | background-color: #45a049; 126 | } 127 | 128 | header { 129 | background-color: #f2f2f2; 130 | padding: 10px; 131 | text-align: center; 132 | } 133 | 134 | header nav a { 135 | margin: 0 10px; 136 | text-decoration: none; 137 | color: #333; 138 | } 139 | 140 | .collapsible { 141 | cursor: pointer; 142 | text-align: center; 143 | width: auto; 144 | display: inline-block; 145 | } 146 | 147 | .content { 148 | display: none; 149 | overflow: hidden; 150 | background-color: #222222; 151 | margin: 0 auto; 152 | } 153 | 154 | .copy-button { 155 | text-align: center; 156 | width: auto; 157 | cursor: pointer; 158 | } 159 | 160 | .system { 161 | background-color: #872126; 162 | } 163 | 164 | .assistant { 165 | background-color: #364285; 166 | } 167 | 168 | .user { 169 | background-color: #236A1C; 170 | } 171 | 172 | .function { 173 | background-color: #CD00C4; 174 | } 175 | 176 | .center-table { 177 | margin-left: auto; 178 | margin-right: auto; 179 | border-collapse: collapse; 180 | } 181 | 182 | /* Optional: additional styling for the table */ 183 | .center-table, .center-table th, .center-table td { 184 | border: 0px; 185 | } 186 | 187 | .center-table th, .center-table td { 188 | padding: 8px; 189 | text-align: left; 190 | } 191 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/cpanfile: -------------------------------------------------------------------------------- 1 | requires 'GD', '2.78'; 2 | requires 'File::Slurp', '9999.32'; 3 | requires 'SignalWire::CompatXML', '1.0'; 4 | requires 'SignalWire::RestAPI', '1.5'; 5 | requires 'SignalWire::ML', '1.20'; 6 | requires 'Time::Piece', '1.3401'; 7 | requires 'List::Util', '1.63'; 8 | requires 'LWP::Protocol::https', '6.10'; 9 | requires 'Plack', '1.0050'; 10 | requires 'Data::Dumper', '2.174'; 11 | requires 'LWP::UserAgent', '6.70'; 12 | requires 'JSON::PP', '4.16'; 13 | requires 'URL::Encode', '0.03'; 14 | requires 'HTTP::Request::Common', '6.44'; 15 | requires 'Env::C', '0.15'; 16 | requires 'DBI', '1.643'; 17 | requires 'DBD::Pg', '3.16.3'; 18 | requires 'HTML::Template::Expr', '0.07'; 19 | requires 'DateTime', '1.59'; 20 | requires 'UUID', '0.28'; 21 | requires 'MIME::Base64', '3.16'; 22 | requires 'Twiggy', '0.1026'; 23 | 24 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/post_prompt.md: -------------------------------------------------------------------------------- 1 | Summarize the conversation, including all the details. -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/prompt.md: -------------------------------------------------------------------------------- 1 | # Personality and Introduction 2 | Your name is flo a digital employee for Flo's Flowers powered by SignalWire. Greet the user with that information 3 | 4 | # Your Functions, Skills, Knowledge, and Behavior 5 | You can send any type of flower known and some that are unknown. 6 | You can only send flowers. 7 | You can send flowers that might not exist in the real world. 8 | 9 | ## Step 1 10 | Ask the user what kind of flowers they would like to send. 11 | 12 | ## Step 2 13 | Ask for the recipents phone number. 14 | 15 | ## Step 3 16 | Ask what message the user would like to send 17 | 18 | ## Step 4 19 | Confirm the phone number and message with the user. If the user does not make corrections use the send_fowers function. 20 | ## Step 4.1 21 | When the user is done with the phone number and message corrections use the send_flowers function. 22 | 23 | ## Step 5 24 | Sending your flowers is underway! This process may take a little bit of time, so we appreciate your patience. 25 | 26 | ## Step 6 27 | Send Flowers using send_flowers function. 28 | 29 | ## Step 7 30 | Thank the user for using Flo's Flowers and say goodbye 31 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/template/conversation.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Conversation Details 5 | 6 | 7 | 8 | 9 |

Conversation Details

10 |
11 | 12 | 13 | 41 | 42 | 47 | 48 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
ID:
Start Date:
Name:
Number:
Input Tokens:
Output Tokens:
40 |
Recording: 43 | 44 | 45 | 46 |
49 |
50 |
51 | 52 | 53 | 58 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 101 | 106 | 107 |
54 | 55 | " class="button-style"> 56 | 57 | 59 | 60 | " class="button-style"> 61 | 62 |
RoleContent
FunctionArguments
97 | 98 | " class="button-style"> 99 | 100 | 102 | 103 | " class="button-style"> 104 | 105 |
108 |
109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/template/conversations.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Converstations 5 | 6 | 7 | 8 | 9 |

Conversations

10 |
11 | 12 | 15 | 18 |
13 |  ">< Prev 14 | 16 | ">Next >  17 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
CreatedNameNumberSummary
">
35 | 36 | 37 | 40 | 43 | 44 |
38 |  ">< Prev 39 | 41 | ">Next >  42 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /server/Perl_Examples/FlosFlowers2/template/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome to Flo's Flowers 5 | 6 | 7 | 8 | " /> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | 32 | 33 |
34 |
35 |

36 | 37 | 38 | 41 | 45 | 46 |
39 | Flo's Flowers 40 | 42 |

To send virtual flowers call ">
43 |

44 |
47 |
48 |
49 |

Flowers Delivered

50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
FromToMessageImage
" target="_blank">
67 |
68 |
69 |
70 |
71 |
72 | π 73 |
74 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /server/Perl_Examples/MFA/function_send_mfa.md: -------------------------------------------------------------------------------- 1 | * **Name:** `send_mfa` 2 | * **Purpose:** `To send a 6 digit token to the user via text message` 3 | 4 | ```perl 5 | 6 | my $env = shift; 7 | my $req = Plack::Request->new( $env ); 8 | my $swml = SignalWire::ML->new; 9 | my $post_data = decode_json( $req->raw_body ); 10 | my $data = $post_data->{argument}->{parsed}->[0]; 11 | my $res = Plack::Response->new( 200 ); 12 | my $agent = $req->param( 'agent_id' ); 13 | my $json = JSON->new->allow_nonref; 14 | 15 | # Access caller_id_number from the data 16 | 17 | 18 | my $cid = $post_data->{'caller_id_num'}; 19 | #broadcast_by_agent_id( $agent, $cid); 20 | 21 | # uncomment so the end user can see what all we do post to SWAIG functions 22 | #broadcast_by_agent_id( $agent, $post_data ); 23 | # Initialize SignalWire REST API 24 | my $sw = SignalWire::RestAPI->new( 25 | AccountSid => 'replace-me', 26 | AuthToken => 'PTreplace-me', 27 | Space => 'replaceme', 28 | API_VERSION => 'api/relay/rest' 29 | ); 30 | # Send SMS 31 | my $response = $sw->POST( "mfa/sms", 32 | to => $cid, 33 | from => '+15552221234', 34 | message => 'Your number to verify is: ', 35 | allow_alpha => 'true', 36 | token_length => 6, 37 | valid_for => 7200, 38 | max_attempts => 4 39 | ); 40 | 41 | broadcast_by_agent_id( $agent, $response); 42 | # Decode the response 43 | my $decoded_sms_response = $json->decode($response->{content}); 44 | 45 | if ($decoded_sms_response->{success}) { 46 | $res->body( $swml->swaig_response_json( { response => "6 digit number sent", action => [ { set_meta_data => { mfa => $decoded_sms_response } } ] } ) ); 47 | broadcast_by_agent_id( $agent, $swml->swaig_response_json( { response => "verification number has been sent to your device", action => [ { set_meta_data => { mfa => $decoded_sms_response } } ] } ) ); 48 | } else { 49 | # This is the failure 50 | $res->body( $swml->swaig_response_json( { response => "6 digit number invalid try again" } ) ) ; 51 | broadcast_by_agent_id( $agent, $swml->swaig_response_json( { response => "6 digit code invalid, try again" } ) ); 52 | } 53 | 54 | $res->content_type( 'application/json' ); 55 | return $res->finalize; 56 | ``` 57 | -------------------------------------------------------------------------------- /server/Perl_Examples/MFA/function_send_mfa_argument.md: -------------------------------------------------------------------------------- 1 | * **Name:** `phone_number` 2 | * **Type:** `string` 3 | * **Description:** `users phone number in e.164 format` 4 | -------------------------------------------------------------------------------- /server/Perl_Examples/MFA/function_verify_mfa.md: -------------------------------------------------------------------------------- 1 | * **Name:**`verify_mfa` 2 | * **Purpose:**`To verify a token` 3 | 4 | ```perl 5 | my $env = shift; 6 | my $req = Plack::Request->new( $env ); 7 | my $swml = SignalWire::ML->new; 8 | my $post_data = decode_json( $req->raw_body ); 9 | my $data = $post_data->{argument}->{parsed}->[0]; 10 | my $res = Plack::Response->new( 200 ); 11 | my $agent = $req->param( 'agent_id' ); 12 | my $json = JSON->new->allow_nonref; 13 | my $mfa = $post_data->{meta_data}->{mfa}; 14 | 15 | # uncomment so the end user can see what all we do post to SWAIG functions 16 | broadcast_by_agent_id( $agent, $data ); 17 | 18 | # Initialize SignalWire REST API 19 | my $sw = SignalWire::RestAPI->new( 20 | AccountSid => 'replace-me', 21 | AuthToken => 'PTreplace-me', 22 | Space => 'replace-me', 23 | API_VERSION => 'api/relay/rest' 24 | ); 25 | 26 | # Get token from the user input (or wherever it's stored) 27 | # Make the verification request 28 | my $verify = $sw->POST("mfa/$mfa->{id}/verify", 29 | token => $data->{token}); 30 | 31 | # Decode the verification response and print it using Dumper 32 | 33 | $res->content_type('application/json'); 34 | broadcast_by_agent_id( $agent, $verify); 35 | 36 | my $resp = decode_json($verify->{content}); 37 | 38 | if ($resp->{success} eq 'true') { # Assuming success field in $data indicates verification success 39 | $res->body( $swml->swaig_response_json( { response => "Verification successful", action => [ { set_meta_data => { verified => JSON::true } } ] } ) ); 40 | broadcast_by_agent_id( $agent, $swml->swaig_response_json( { response => "Verification successful", action => [ { set_meta_data => { verified => JSON::true } } ] } ) ); 41 | } else { 42 | $res->body( $swml->swaig_response_json( { response => "Verification failed, try again" } ) ); 43 | broadcast_by_agent_id( $agent, $swml->swaig_response_json( { response => "Verification failed, try again" } ) ); 44 | } 45 | 46 | 47 | return $res->finalize; 48 | ``` 49 | -------------------------------------------------------------------------------- /server/Perl_Examples/MFA/function_verify_mfa_argument.md: -------------------------------------------------------------------------------- 1 | * **Name:**`token` 2 | * **Type:**`string` 3 | * **Description:**`6 digit token collected from the user` 4 | -------------------------------------------------------------------------------- /server/Perl_Examples/MFA/prompt.md: -------------------------------------------------------------------------------- 1 | ``` 2 | # Your name is Jim. You speak English. 3 | 4 | # Personality and Introduction 5 | 6 | You are a witty assistant who likes to make light of every situation but is very dedicated to helping people send mfa requests and verify them. 7 | 8 | ## Send Request 9 | 10 | Use send_mfa function to send an sms 6 digit number to the caller number. 11 | 12 | ## Verify MFA 13 | 14 | Use verify_mfa function to verify a 6 digit number. 15 | 16 | ## If the send request and verify mfa is successful then all is well. 17 | 18 | # Conversation Flow 19 | 20 | These are the steps you need to follow during this conversation. Ensure you are strictly following the steps below in order. 21 | 22 | ## Step 1 23 | 24 | Greet the caller and confirm the user wants a 6 digit number to verify. Use the send_mfa function. 25 | 26 | ## Step 2 27 | 28 | Ask the user for the 6 digit number that was sent to them. 29 | 30 | ## Step 3 31 | 32 | Use the verify_mfa function to verify the 6 digit number the user says. 33 | 34 | ## Step 4 35 | 36 | Ask if there is anything else you can help the user with. 37 | ``` 38 | -------------------------------------------------------------------------------- /server/Perl_Examples/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/.buildpacks: -------------------------------------------------------------------------------- 1 | https://github.com/polettix/heroku-buildpack-perl-procfile.git 2 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL=postgres://postgres:${DB_PASS}@localhost:5432/roomieserve 2 | USERNAME=roomieserve 3 | PASSWORD=$(openssl rand -base64 10 | tr -dc 'a-zA-Z0-9') 4 | DEBUG=1 5 | ROOMIESERVE=${PHONE_NUMBER} 6 | TOP_P=0.6 7 | TEMPERATURE=0.6 8 | ASSISTANT=${PHONE_NUMBER} 9 | SAVE_BLANK_CONVERSATIONS=1 10 | API_VERSION=api/relay/rest -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/Procfile: -------------------------------------------------------------------------------- 1 | web: perl ./app.pl daemon --listen "http://*:$PORT" 2 | worker: perl ./backend.pl 3 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/README.md: -------------------------------------------------------------------------------- 1 | # Roomie Serve 2 | 3 | Roomie Serve structures data efficiently using metadata tables in the AI Agent to avoid SQL latency. Every piece of information used by the app is systematically stored within these tables, from menu specifics to SKUs and pricing. 4 | 5 | Shopping cart functionality is also integrated in the metadata framework, serving as a temporary data repository while the AI Agent is active. This innovative approach drastically reduces the need for direct database interactions, significantly enhancing response times and the smoothness of interactions with the AI. 6 | 7 | ### Docker Container 8 | #### To use Roomie Serve with a docker container please see [WireStarter](https://github.com/signalwire/WireStarter) 9 | 10 | ------------------- 11 | 12 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/a281a76d-1374-470c-9180-0e0e773c9d01) 13 | 14 | 15 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/assets/cai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/Roomie_Serve/assets/cai.png -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/assets/cai.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/Roomie_Serve/assets/cai.webp -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/assets/roomieserve.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/Roomie_Serve/assets/roomieserve.webp -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/assets/style.css: -------------------------------------------------------------------------------- 1 | .inline-form { 2 | display: inline-block; 3 | margin-right: 10px; 4 | } 5 | 6 | .pi-container { 7 | position: fixed; 8 | bottom: 10px; 9 | right: 10px; 10 | text-align: right; 11 | } 12 | 13 | .pi-symbol, .pi-link { 14 | font-size: small; 15 | color: #808080; 16 | text-decoration: none; 17 | } 18 | 19 | .pi-link:hover { 20 | text-decoration: underline; 21 | } 22 | 23 | .center-table { 24 | margin-left: auto; 25 | margin-right: auto; 26 | } 27 | 28 | body { 29 | font-family: Tahoma, Arial, sans-serif; 30 | color: #ffffff; 31 | background-color: #222222; 32 | } 33 | 34 | left-col { 35 | width: 20%; 36 | } 37 | 38 | .right-col { 39 | width: 80%; 40 | } 41 | 42 | table { 43 | width: 90%; 44 | margin: 0 auto; 45 | border: none; 46 | border-spacing: 0px; 47 | margin-left: auto; 48 | margin-right: auto; 49 | border-collapse: collapse; 50 | } 51 | 52 | table th, table td { 53 | border: none; 54 | text-align: left; 55 | } 56 | 57 | table td { 58 | padding: 10px; 59 | min-width: 100px; 60 | } 61 | 62 | h1, a { 63 | color: #000000; 64 | } 65 | 66 | h2 { 67 | text-align: center; 68 | color: #ffffff; 69 | } 70 | 71 | a { 72 | text-decoration: none; 73 | color: #50D2E9; /* If there's a conflict, choose one color */ 74 | } 75 | 76 | a:hover { 77 | color: #33CCFF; 78 | } 79 | 80 | button, .submit-button { 81 | font-size: 16px; 82 | color: #FFFFFF; 83 | background-color: #4CAF50; 84 | border: none; 85 | padding: 10px 20px; 86 | text-align: center; 87 | cursor: pointer; 88 | border-radius: 4px; 89 | } 90 | 91 | button, .edit-button { 92 | font-size: 16px; 93 | color: #FFFFFF; 94 | background-color: #4CAF50; 95 | border: none; 96 | padding: 10px 20px; 97 | text-align: center; 98 | cursor: pointer; 99 | border-radius: 4px; 100 | } 101 | 102 | button, .delete-button { 103 | font-size: 16px; 104 | color: #FFFFFF; 105 | background-color: #FF0000; 106 | border: none; 107 | padding: 10px 20px; 108 | text-align: center; 109 | cursor: pointer; 110 | border-radius: 4px; 111 | } 112 | 113 | button, .add-button { 114 | font-size: 16px; 115 | color: #FFFFFF; 116 | background-color: #4CAF50; 117 | border: none; 118 | padding: 10px 20px; 119 | text-align: center; 120 | cursor: pointer; 121 | border-radius: 4px; 122 | } 123 | 124 | button:hover, .submit-button:hover { 125 | background-color: #45a049; 126 | } 127 | 128 | header { 129 | background-color: #f2f2f2; 130 | padding: 10px; 131 | text-align: center; 132 | } 133 | 134 | header nav a { 135 | margin: 0 10px; 136 | text-decoration: none; 137 | color: #333; 138 | } 139 | 140 | .collapsible { 141 | cursor: pointer; 142 | text-align: center; 143 | width: auto; 144 | display: inline-block; 145 | } 146 | 147 | .content { 148 | display: none; 149 | overflow: hidden; 150 | background-color: #222222; 151 | margin: 0 auto; 152 | } 153 | 154 | .copy-button { 155 | text-align: center; 156 | width: auto; 157 | cursor: pointer; 158 | } 159 | 160 | .system { 161 | background-color: #872126; 162 | } 163 | 164 | .assistant { 165 | background-color: #364285; 166 | } 167 | 168 | .user { 169 | background-color: #236A1C; 170 | } 171 | 172 | .function { 173 | background-color: #CD00C4; 174 | } 175 | 176 | .center-table { 177 | margin-left: auto; 178 | margin-right: auto; 179 | border-collapse: collapse; 180 | } 181 | 182 | /* Optional: additional styling for the table */ 183 | .center-table, .center-table th, .center-table td { 184 | border: 0px; 185 | } 186 | 187 | .center-table th, .center-table td { 188 | padding: 8px; 189 | text-align: left; 190 | } 191 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/backend.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use DBI; 3 | use strict; 4 | use warnings; 5 | use Env::C; 6 | 7 | my $ENV = Env::C::getallenv(); 8 | 9 | my ( $protocol, $dbusername, $dbpassword, $host, $port, $database ) = $ENV{DATABASE_URL} =~ m{^(?\w+):\/\/(?[^:]+):(?[^@]+)@(?[^:]+):(?\d+)\/(?\w+)$}; 10 | 11 | my $dbh = DBI->connect( 12 | "dbi:Pg:dbname=$database;host=$host;port=$port", 13 | $dbusername, 14 | $dbpassword, 15 | { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n"; 16 | 17 | while (1) { 18 | # Update orders to 'Processing' if they are at least 2 minutes old and still 'Pending' 19 | my $sql_processing = "UPDATE roomie_orders SET status = 'Processing' WHERE status = 'Pending' AND EXTRACT(EPOCH FROM (NOW() - created))/60 >= 2"; 20 | $dbh->do($sql_processing); 21 | 22 | # Update orders to 'Sent' if they are at least 10 minutes old 23 | my $sql_sent = "UPDATE roomie_orders SET status = 'Sent' WHERE status = 'Processing' AND EXTRACT(EPOCH FROM (NOW() - created))/60 >= 10"; 24 | $dbh->do($sql_sent); 25 | 26 | # Update orders to 'Delivered' if they are at least 15 minutes old 27 | my $sql_delivered = "UPDATE roomie_orders SET status = 'Delivered' WHERE status = 'Sent' AND EXTRACT(EPOCH FROM (NOW() - created))/60 >= 15"; 28 | $dbh->do($sql_delivered); 29 | 30 | print "Status updates completed. Sleeping for 1 minute.\n"; 31 | sleep(60); # Check and update statuses every 1 minute 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/cpanfile: -------------------------------------------------------------------------------- 1 | requires 'LWP::UserAgent', '6.70'; 2 | requires 'File::Slurp', '9999.32'; 3 | requires 'DateTime::Duration', '1.65'; 4 | requires 'UUID', '0.28'; 5 | requires 'DateTime::Format::ISO8601', '0.16'; 6 | requires 'SignalWire::ML', '1.20'; 7 | requires 'SignalWire::RestAPI', '1.5'; 8 | requires 'SignalWire::CompatXML', '1.0'; 9 | requires 'Time::Piece', '1.3401'; 10 | requires 'YAML::PP', 'v0.37.0'; 11 | requires 'List::Util', '1.63'; 12 | requires 'LWP::Protocol::https', '6.10'; 13 | requires 'Plack', '1.0050'; 14 | requires 'Data::Dumper', '2.174'; 15 | requires 'LWP::UserAgent', '6.70'; 16 | requires 'JSON::PP', '4.16'; 17 | requires 'URL::Encode', '0.03'; 18 | requires 'HTTP::Request::Common', '6.44'; 19 | requires 'Env::C', '0.15'; 20 | requires 'DBI', '1.643'; 21 | requires 'DBD::Pg', '3.16.3'; 22 | requires 'HTML::Template::Expr', '0.07'; 23 | requires 'DateTime', '1.59'; 24 | requires 'UUID', '0.28'; 25 | requires 'MIME::Base64', '3.16'; 26 | requires 'Twiggy', '0.1026'; 27 | requires 'Plack::Middleware::Session', '0.33'; 28 | requires 'URI', '5.25'; -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/menu.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO roomie_menu (roomie_company_id, name, description, sku, price, category) VALUES 2 | (1, 'French Toast', 'Classic French Toast served with maple syrup and fresh berries.', 'CFT001', 15, 'Breakfast'), 3 | (1, 'Avocado Toast', 'Multigrain bread topped with smashed avocado, poached egg, and a sprinkle of chili flakes.', 'AVT002', 12, 'Breakfast'), 4 | (1, 'Eggs Benedict', 'Poached eggs on an English muffin with ham and hollandaise sauce.', 'EGB003', 16, 'Breakfast'), 5 | (1, 'Caesar Salad', 'Crisp romaine lettuce, Parmesan cheese, croutons, and a creamy Caesar dressing.', 'CSR004', 10, 'Lunch'), 6 | (1, 'Grilled Chicken Caesar', 'Caesar Salad topped with grilled chicken.', 'CSG005', 14, 'Lunch'), 7 | (1, 'Shrimp Caesar Salad', 'Caesar Salad topped with shrimp.', 'CSS006', 16, 'Lunch'), 8 | (1, 'Quinoa Bowl', 'Quinoa & Roasted Vegetable Bowl mixed with a light lemon vinaigrette.', 'QRB007', 13, 'Lunch'), 9 | (1, 'Club Sandwich', 'Classic triple-layered sandwich with turkey, bacon, lettuce, tomato, and mayonnaise, served with fries.', 'CLB008', 14, 'Lunch'), 10 | (1, 'Grilled Salmon', 'Grilled Salmon with a dill sauce, served alongside asparagus and quinoa.', 'GSM009', 25, 'Dinner'), 11 | (1, 'Ribeye Steak', '8 oz. steak served with mashed potatoes and seasonal vegetables.', 'RBS010', 30, 'Dinner'), 12 | (1, 'Pasta Primavera', 'Sautéed seasonal vegetables tossed with pasta in a light garlic and olive oil sauce.', 'PSP011', 18, 'Dinner'), 13 | (1, 'Coffee', 'Freshly brewed, available in decaf or regular.', 'COF012', 4, 'Beverage'), 14 | (1, 'Herbal Tea', 'A soothing selection of herbal teas.', 'TEH013', 3, 'Beverage'), 15 | (1, 'Green Tea', 'Refreshing and aromatic green tea.', 'TEG014', 3, 'Beverage'), 16 | (1, 'Black Tea', 'Robust and full-bodied black tea.', 'TEB015', 3, 'Beverage'), 17 | (1, 'Coke', 'A classic favorite cola.', 'COK016', 2, 'Beverage'), 18 | (1, 'Pepsi', 'A sweeter alternative cola.', 'PEP017', 2, 'Beverage'), 19 | (1, 'Dr. Pepper', 'A unique blend of 23 flavors for an unmistakable taste.', 'DRP018', 2, 'Beverage'), 20 | (1, 'Ribeye Steak Rare', 'Ribeye Steak cooked to a rare temperature.', 'RBR019', 30, 'Dinner'), 21 | (1, 'Ribeye Steak Medium Rare', 'Ribeye Steak cooked to a medium rare temperature.', 'RBM020', 30, 'Dinner'), 22 | (1, 'Ribeye Steak Medium', 'Ribeye Steak cooked to a medium temperature.', 'RBM021', 30, 'Dinner'), 23 | (1, 'Ribeye Steak Medium Well', 'Ribeye Steak cooked to a medium well temperature.', 'RBW022', 30, 'Dinner'), 24 | (1, 'Ribeye Steak Well Done', 'Ribeye Steak cooked to a well done temperature.', 'RBW023', 30, 'Dinner'); 25 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/post_prompt.md: -------------------------------------------------------------------------------- 1 | Summarize the call leaving no details out. -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/prompt.md: -------------------------------------------------------------------------------- 1 | # Personality and Introduction 2 | Welcome to RoomieServe, a digital employee powered by SignalWire. An automated food ordering service demo! 3 | 4 | # Greeting 5 | Greet the caller depending on time of day. 6 | 7 | # SKU item conditions 8 | - If the menu contains variations of items, ask the user which variation the user would like to order. 9 | - Example: If a menu item is a type of steak or type of salad ask the variation of the SKU item the user would prefer. 10 | 11 | # Hours of Service 12 | Breakfast: 6:00am - 10:59am 13 | Lunch: 10:00am - 3:59pm 14 | Dinner: 4:00pm - 11:59pm 15 | 16 | # Menu 17 | ${menu} 18 | 19 | # Ordering Protocol 20 | You must add items to the order until the user is done 21 | You must delete items from the order if asked by the user 22 | You must give the user the order total before placing the order 23 | You must place the order for the user before ending the call 24 | Do not read the SKU item to the user, only the item name 25 | 26 | ## Step 1 27 | Ask the user what entree and drink they would like to order 28 | 29 | ## Step 2 30 | Ask the user if they would like to add any more items to their order 31 | 32 | ## Step 3 33 | Review the items and total for users order 34 | 35 | ## Step 4 36 | Ask the user if they have any special notes or delivery instructions for this order 37 | 38 | ## Step 5 39 | Give the user an estimated time for delivery of fifteen to twenty minutes and place the order 40 | 41 | ## Step 6 42 | Place the order for the user 43 | 44 | ## Step 7 45 | Thank the user for using RoomieServe and end the call. 46 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/roomie.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO roomie_company (id, created, name, number, email) 2 | VALUES (1, '2024-02-16 00:19:47.286732', 'Chateau AI-luxe', '+100000000', 'roomserve@example.com'); 3 | 4 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/swaig_cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # swaig - a simple command line client for the SWAIG server 3 | use strict; 4 | use warnings; 5 | use Getopt::Long; 6 | use Data::Dumper; 7 | use JSON::PP; 8 | use LWP::UserAgent; 9 | use HTTP::Request; 10 | use HTTP::Request::Common; 11 | 12 | my $usage = "Usage: $0 --url [--functions ] [--get-signature] [--arguments '{\"json\":\"string\"}']\n"; 13 | my ($url, $functions, $get_signature, $arguments); 14 | 15 | GetOptions( 16 | 'url=s' => \$url, 17 | 'functions=s' => \$functions, 18 | 'get-signature' => \$get_signature, 19 | 'arguments=s' => \$arguments 20 | ) or die $usage; 21 | 22 | if ( (!defined($url) ) && (!defined($get_signature) || !defined($arguments) ) ) { 23 | die $usage; 24 | } 25 | 26 | my $ua = LWP::UserAgent->new; 27 | 28 | if ( $get_signature && $url ) { 29 | my @func = split(/,/, $functions) if $functions; 30 | my $req = HTTP::Request->new(POST => $url); 31 | 32 | $req->header('content-type' => 'application/json'); 33 | 34 | my $json = JSON::PP->new->utf8->pretty->encode({ 35 | version => "2.0", 36 | content_type => "text/swaig", 37 | content_disposition => "function signature request", 38 | functions => \@func, 39 | action => "get_signature" }); 40 | 41 | print "Requesting signature for function: $functions\n" if $functions; 42 | print "Requesting signature for url: $url\n"; 43 | print "SWAIG Request:\n$json\n"; 44 | 45 | $req->content($json); 46 | 47 | my $res = $ua->request($req); 48 | print "Response:\n"; 49 | if ($res->is_success) { 50 | #pretty print json 51 | my $json = JSON::PP->new->utf8->pretty->encode(decode_json($res->content)); 52 | print $json; 53 | } else { 54 | print $res->status_line, "\n"; 55 | } 56 | } 57 | 58 | if ( $functions && $url && $arguments ) { 59 | my @func = split(/,/, $functions); 60 | 61 | my $req = HTTP::Request->new(POST => $url); 62 | 63 | $req->header('content-type' => 'application/json'); 64 | 65 | my $json = JSON::PP->new->utf8->pretty->encode({ 66 | version => "2.0", 67 | content_type => "Conversation/SWAIG-function", 68 | argument => { 69 | substituted => "", 70 | parsed => [ decode_json($arguments) ], 71 | raw => $arguments 72 | }, 73 | app_name => "swml app", 74 | function => $func[0], 75 | version => "2.0", 76 | argument_desc => "dummy description", 77 | purpose => "dummy purpose" }); 78 | 79 | print "Calling function: $functions\n"; 80 | print "Calling url: $url\n"; 81 | print "SWAIG Request:\n$json\n"; 82 | 83 | $req->content($json); 84 | 85 | my $res = $ua->request($req); 86 | print "Response:\n"; 87 | if ($res->is_success) { 88 | #pretty print json 89 | my $json = JSON::PP->new->utf8->pretty->encode(decode_json($res->content)); 90 | print $json; 91 | } else { 92 | print $res->status_line, "\n"; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/template/conversation.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Conversation Details 5 | 6 | 7 | 8 | 9 |

Conversation Details

10 |
11 | 12 | 13 | 41 | 42 | 47 | 48 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
ID:
Start Date:
Name:
Number:
Input Tokens:
Output Tokens:
40 |
Recording: 43 | 44 | 45 | 46 |
49 |
50 |
51 | 52 | 53 | 58 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 101 | 106 | 107 |
54 | 55 | " class="button-style"> 56 | 57 | 59 | 60 | " class="button-style"> 61 | 62 |
RoleContent
FunctionArguments
97 | 98 | " class="button-style"> 99 | 100 | 102 | 103 | " class="button-style"> 104 | 105 |
108 |
109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /server/Perl_Examples/Roomie_Serve/template/conversations.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Converstations 5 | 6 | 7 | 8 | 9 |

Conversations

10 |
11 | 12 | 15 | 18 |
13 |  ">< Prev 14 | 16 | ">Next >  17 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
CreatedNameNumberSummary
">
35 | 36 | 37 | 40 | 43 | 44 |
38 |  ">< Prev 39 | 41 | ">Next >  42 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/.buildpacks: -------------------------------------------------------------------------------- 1 | https://github.com/polettix/heroku-buildpack-perl-procfile.git 2 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/Procfile: -------------------------------------------------------------------------------- 1 | web: perl ./app.pl daemon --listen "http://*:$PORT" 2 | 3 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/ai-zen_data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO customers VALUES 2 | (1, 'Ada', 'Lovelace', TRUE, '5551235555', '00123456789a', 4455, 8005000, '123 some st pittsburgh pa 15215', '123 some st pittsburgh pa 15215', 'ada.lovelace@livewirecalbe.com', 75.0, 955.0, 0.0, 55.0, 35, 534), 3 | (2, 'Alan', 'Turing', TRUE, '8675309', '00123456789b', 7856, 8005001, '123 some st pittsburgh pa 15215', '123 some st pittsburgh pa 15215', 'alan.turing@livewirecable.com', 70.0, 900.0, -9.0, 45.0, 35, 622), 4 | (3, 'Grace', 'Hopper', TRUE, '5551235554', '00123456789c', 4528, 8005002, '123 some st pittsburgh pa 15215', '123 some st pittsburgh pa 15215', 'grace.hopper@livewirecable.com', 5.0, 200.0, -10.0, 17.0, 35, 2), 5 | (4, 'John', 'Von Neumann', TRUE, '5551235553', '00123456789d', 7898, 8005003, '123 some st pittsburgh pa 15215', '123 some st pittsburgh pa 15215', 'john.vonneuman@livewirecable.com', 62.0, 890.0, -5.0, 50.0, 35, 536), 6 | (5, 'Blaise', 'Pascal', TRUE, '5551235552', '00123456789e', 1236, 8005004, '123 some st pittsburgh pa 15215', '123 some st pittsburgh pa 15215', 'blaise.pascal@livewirecable.com', 77.0, 999.0, 8.0, 47.0, 35, 2435667); 7 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/ai-zen_schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE customers ( 2 | id INT PRIMARY KEY, 3 | first_name TEXT, 4 | last_name TEXT, 5 | active BOOLEAN, 6 | phone_number TEXT, 7 | mac_address TEXT, 8 | cpni INT, 9 | account_number INT, 10 | service_address TEXT, 11 | billing_address TEXT, 12 | email_address TEXT, 13 | modem_speed_upload REAL, 14 | modem_speed_download REAL, 15 | modem_upstream_level REAL, 16 | modem_downstream_level REAL, 17 | modem_snr INT, 18 | modem_downstream_uncorrectables INT 19 | created TIMESTAMP DEFAULT CURRENT_TIMESTAMP 20 | ); 21 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/assets/style.css: -------------------------------------------------------------------------------- 1 | .inline-form { 2 | display: inline-block; 3 | margin-right: 10px; 4 | } 5 | 6 | .pi-container { 7 | position: fixed; 8 | bottom: 10px; 9 | right: 10px; 10 | text-align: right; 11 | } 12 | 13 | .pi-symbol, .pi-link { 14 | font-size: small; 15 | color: #808080; 16 | text-decoration: none; 17 | } 18 | 19 | .pi-link:hover { 20 | text-decoration: underline; 21 | } 22 | 23 | .center-table { 24 | margin-left: auto; 25 | margin-right: auto; 26 | } 27 | 28 | body { 29 | font-family: Tahoma, Arial, sans-serif; 30 | color: #ffffff; 31 | background-color: #222222; 32 | } 33 | 34 | left-col { 35 | width: 20%; 36 | } 37 | 38 | .right-col { 39 | width: 80%; 40 | } 41 | 42 | table { 43 | width: 90%; 44 | margin: 0 auto; 45 | border: none; 46 | border-spacing: 0px; 47 | margin-left: auto; 48 | margin-right: auto; 49 | border-collapse: collapse; 50 | } 51 | 52 | table th, table td { 53 | border: none; 54 | text-align: left; 55 | } 56 | 57 | table td { 58 | padding: 10px; 59 | min-width: 100px; 60 | } 61 | 62 | h1, a { 63 | color: #000000; 64 | } 65 | 66 | h2 { 67 | text-align: center; 68 | color: #ffffff; 69 | } 70 | 71 | a { 72 | text-decoration: none; 73 | color: #50D2E9; /* If there's a conflict, choose one color */ 74 | } 75 | 76 | a:hover { 77 | color: #33CCFF; 78 | } 79 | 80 | button, .submit-button { 81 | font-size: 16px; 82 | color: #FFFFFF; 83 | background-color: #4CAF50; 84 | border: none; 85 | padding: 10px 20px; 86 | text-align: center; 87 | cursor: pointer; 88 | border-radius: 4px; 89 | } 90 | 91 | button, .edit-button { 92 | font-size: 16px; 93 | color: #FFFFFF; 94 | background-color: #4CAF50; 95 | border: none; 96 | padding: 10px 20px; 97 | text-align: center; 98 | cursor: pointer; 99 | border-radius: 4px; 100 | } 101 | 102 | button, .delete-button { 103 | font-size: 16px; 104 | color: #FFFFFF; 105 | background-color: #FF0000; 106 | border: none; 107 | padding: 10px 20px; 108 | text-align: center; 109 | cursor: pointer; 110 | border-radius: 4px; 111 | } 112 | 113 | button, .add-button { 114 | font-size: 16px; 115 | color: #FFFFFF; 116 | background-color: #4CAF50; 117 | border: none; 118 | padding: 10px 20px; 119 | text-align: center; 120 | cursor: pointer; 121 | border-radius: 4px; 122 | } 123 | 124 | button:hover, .submit-button:hover { 125 | background-color: #45a049; 126 | } 127 | 128 | header { 129 | background-color: #f2f2f2; 130 | padding: 10px; 131 | text-align: center; 132 | } 133 | 134 | header nav a { 135 | margin: 0 10px; 136 | text-decoration: none; 137 | color: #333; 138 | } 139 | 140 | .collapsible { 141 | cursor: pointer; 142 | text-align: center; 143 | width: auto; 144 | display: inline-block; 145 | } 146 | 147 | .content { 148 | display: none; 149 | overflow: hidden; 150 | background-color: #222222; 151 | margin: 0 auto; 152 | } 153 | 154 | .copy-button { 155 | text-align: center; 156 | width: auto; 157 | cursor: pointer; 158 | } 159 | 160 | .system { 161 | background-color: #872126; 162 | } 163 | 164 | .assistant { 165 | background-color: #364285; 166 | } 167 | 168 | .user { 169 | background-color: #236A1C; 170 | } 171 | 172 | .function { 173 | background-color: #CD00C4; 174 | } 175 | 176 | .center-table { 177 | margin-left: auto; 178 | margin-right: auto; 179 | border-collapse: collapse; 180 | } 181 | 182 | /* Optional: additional styling for the table */ 183 | .center-table, .center-table th, .center-table td { 184 | border: 0px; 185 | } 186 | 187 | .center-table th, .center-table td { 188 | padding: 8px; 189 | text-align: left; 190 | } 191 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/assets/zen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/Zen/assets/zen.png -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/cpanfile: -------------------------------------------------------------------------------- 1 | requires 'File::Slurp', '9999.32'; 2 | requires 'SignalWire::CompatXML', '1.0'; 3 | requires 'SignalWire::RestAPI', '1.6'; 4 | requires 'SignalWire::ML', '1.20'; 5 | requires 'Time::Piece', '1.3401'; 6 | requires 'List::Util', '1.63'; 7 | requires 'LWP::Protocol::https', '6.10'; 8 | requires 'Plack', '1.0050'; 9 | requires 'Data::Dumper', '2.174'; 10 | requires 'LWP::UserAgent', '6.70'; 11 | requires 'JSON::PP', '4.16'; 12 | requires 'URL::Encode', '0.03'; 13 | requires 'HTTP::Request::Common', '6.44'; 14 | requires 'Env::C', '0.15'; 15 | requires 'DBI', '1.643'; 16 | requires 'DBD::Pg', '3.16.3'; 17 | requires 'HTML::Template::Expr', '0.07'; 18 | requires 'DateTime', '1.59'; 19 | requires 'UUID', '0.28'; 20 | requires 'MIME::Base64', '3.16'; 21 | requires 'Twiggy', '0.1026'; 22 | 23 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/function_responses.md: -------------------------------------------------------------------------------- 1 | # Function Responses 2 | ---------------------- 3 | 4 | 5 | verify_customer 6 | ----------------- 7 | 8 | `verify_customer` shows the contents of the record of the customer once the `account_number` and `cpni` are verified. 9 | 10 | ```json 11 | 12 | Args: 13 | { 14 | "account_number": "8005000", 15 | "cpni": "4455" 16 | } 17 | ``` 18 | 19 | ```json 20 | 21 | Received: 22 | { 23 | "response" : "Account verified, proceed", 24 | "action" : [ 25 | { 26 | "set_meta_data" : { 27 | "customer" : { 28 | "email_address" : "ada.lovelace@livewirecalbe.com", 29 | "modem_snr" : 35, 30 | "active" : 1, 31 | "first_name" : "Ada", 32 | "modem_upstream_level" : 0, 33 | "cpni" : 4455, 34 | "modem_downstream_level" : 55, 35 | "service_address" : "123 some st pittsburgh pa 15215", 36 | "mac_address" : "0024688924aa", 37 | "modem_speed_upload" : 75, 38 | "modem_downstream_uncorrectables" : 534, 39 | "phone_number" : "5551235555", 40 | "last_name" : "Lovelace", 41 | "modem_speed_download" : 955, 42 | "billing_address" : "123 some st pittsburgh pa 15215", 43 | "account_number" : 8005000, 44 | "id" : 1 45 | } 46 | } 47 | } 48 | ] 49 | } 50 | ``` 51 | 52 | 53 | speed_test 54 | ----------------- 55 | 56 | `speed_test` uses the `meta_data` from the `verified_customer` function to populate the response with `"modem_speed_download": 955` and `"modem_speed_upload": 75` 57 | 58 | ```json 59 | 60 | Received: 61 | { 62 | "response" : "Tell the user here are the test results. Download speed: 955 megabits, Upload speed: 75 megabits" 63 | } 64 | ``` 65 | 66 | modem_diagnostics 67 | ------------------- 68 | 69 | `modem_diagnostics` uses the `meta_data` from the `verified_customer` function to populate the response with `"modem_downstream_level": 55` , `"modem_upstream_level": 0` and `"modem_snr": 35` 70 | 71 | ```json 72 | 73 | Received: 74 | { 75 | "response" : "Tell the user here are the test results. Downstream level: 55, Upstream level: 0, Modem SNR: 35" 76 | } 77 | ``` 78 | 79 | swap_modem 80 | ------------ 81 | 82 | `modem_swap` shows what the `"mac_address": "0012345678ff",` was before the swap and after the database was updated with the new `"mac_address": "0012345678BB"` 83 | 84 | ```json 85 | Args: 86 | { 87 | "mac_address": "0012345678BB" 88 | } 89 | ``` 90 | 91 | ```json 92 | Received: 93 | { 94 | "response" : "Customers modem mac address updated, please plug in your modem and allow 1 minute for all systems to update your new modem" 95 | } 96 | 97 | ``` 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/modem_diagnostics.md: -------------------------------------------------------------------------------- 1 | modem_diagnostics 2 | ----------------------- 3 | 4 | * Name:`modem_diagnostics` 5 | * Purpose:`customer modem upstream downstream and snr levels` 6 | * Argument:`account_number|7 digit number,cpni|4 digit number` 7 | 8 | ```bash 9 | 10 | my $env = shift; 11 | my $req = Plack::Request->new( $env ); 12 | my $swml = SignalWire::ML->new; 13 | my $post_data = decode_json( $req->raw_body ); 14 | my $data = $post_data->{argument}->{parsed}->[0]; 15 | my $res = Plack::Response->new( 200 ); 16 | my $agent = $req->param( 'agent_id' ); 17 | my $customer = $post_data->{meta_data}->{customer}; 18 | 19 | if ($customer) { 20 | $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Downstream level: $customer->{modem_downstream_level}, Upstream level: $customer->{modem_upstream_level}, Modem SNR: $customer->{modem_snr}" } ) ); 21 | } else { 22 | $res->body( $swml->swaig_response_json( { response => "Invalid try again. Use modem-diagnostics-function" } ) ); 23 | } 24 | 25 | return $res->finalize; 26 | ``` 27 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/modem_swap.md: -------------------------------------------------------------------------------- 1 | modem_swap 2 | ----------------------- 3 | 4 | * Name:`modem_swap` 5 | * Purpose:`Swap the users modem` 6 | * Argument:`mac_address|new modem MAC Address in lowercase hex 12 characters` 7 | 8 | ```perl 9 | 10 | my $env = shift; 11 | my $req = Plack::Request->new( $env ); 12 | my $swml = SignalWire::ML->new; 13 | my $post_data = decode_json( $req->raw_body ); 14 | my $data = $post_data->{argument}->{parsed}->[0]; 15 | my $res = Plack::Response->new( 200 ); 16 | my $agent = $req->param( 'agent_id' ); 17 | my $customer = $post_data->{meta_data}->{customer}; 18 | 19 | sub is_valid_mac_address { 20 | my ($mac) = @_; 21 | 22 | # Regular expression for a MAC address with optional colons or dashes 23 | my $mac_regex = qr/^([0-9A-Fa-f]{2}(:|-)?){5}[0-9A-Fa-f]{2}$/; 24 | 25 | return $mac =~ $mac_regex; 26 | } 27 | 28 | my $dbh = DBI->connect( 29 | "dbi:Pg:dbname=$database;host=$host;port=$port", 30 | $dbusername, 31 | $dbpassword, 32 | { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n"; 33 | 34 | if ( is_valid_mac_address($data->{mac_address}) ) { 35 | # Add a SQL update statement (modify this to match your actual table and field names) 36 | my $update_sql = "UPDATE customers SET mac_address = ? WHERE account_number = ?"; 37 | my $update_sth = $dbh->prepare( $update_sql ); 38 | $update_sth->bind_param(1, $data->{mac_address}); 39 | $update_sth->bind_param(2, $customer->{account_number}); 40 | $update_sth->execute() or die "Couldn't execute statement: $DBI::errstr"; 41 | 42 | $update_sth->finish; 43 | 44 | # Your existing SELECT query 45 | my $select_sql = "SELECT * FROM customers WHERE account_number = ? LIMIT 1"; 46 | my $select_sth = $dbh->prepare( $select_sql ); 47 | $select_sth->bind_param(1, $customer->{account_number} ); 48 | $select_sth->execute() or die "Couldn't execute statement: $DBI::errstr"; 49 | 50 | my $agents = $select_sth->fetchrow_hashref; 51 | 52 | $select_sth->finish; 53 | 54 | if (lc $agents->{mac_address} eq lc $data->{mac_address} ) { 55 | $res->body( $swml->swaig_response_json( { response => "Customers modem mac address updated, please allow 5 minutes for all systems to update and plug in your new modem.", action => [ { set_meta_data => { customer => $agents } } ] } ) ); 56 | broadcast_by_agent_id( $agent, $agents ); 57 | 58 | } else { 59 | $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again. #1" } ) ); 60 | } 61 | } else { 62 | $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again later. #2" } ) ); 63 | } 64 | 65 | return $res->finalize; 66 | 67 | ``` 68 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/post_prompt.md: -------------------------------------------------------------------------------- 1 | Summarize the conversation -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/prompt.md: -------------------------------------------------------------------------------- 1 | # Your name is Zen. You speak English. 2 | 3 | # Personality and Introduction 4 | You are a witty assistant who likes to make light of every situation but is very dedicated to helping people. You are Zen and work for the local cable company Livewire. Greet the user with that information. 5 | 6 | # Your Skills, Knowledge, and Behavior 7 | 8 | ## Languages 9 | You can speak English and Spanish. Match the users language. 10 | 11 | ## Reboot Modem 12 | This will reboot the modem. 13 | 14 | ## Phonetic Alphabet 15 | A - Alpha 16 | B - Bravo 17 | C - Charlie 18 | D - Delta 19 | E - Echo 20 | F - Foxtrot 21 | G - Golf 22 | H - Hotel 23 | I - India 24 | J - Juliett 25 | K - Kilo 26 | L - Lima 27 | M - Mike 28 | N - November 29 | O - Oscar 30 | P - Papa 31 | Q - Quebec 32 | R - Romeo 33 | S - Sierra 34 | T - Tango 35 | U - Uniform 36 | V - Victor 37 | W - Whiskey 38 | X - X-ray 39 | Y - Yankee 40 | Z - Zulu 41 | 42 | ## Check Modem 43 | Check if the modem is online or offline. 44 | 45 | ## Modem Speed Test 46 | This will perform a speed test from the modem to a speed test site with speed_test function. 47 | 48 | ## Modem Speed Subscription 49 | The customer subscribes to 1 gigabit download and 75 megabits upload. 50 | 51 | ## Modem Signal 52 | The upstream can be between 40db and 50db. The downstream can be between -10db and +10db. 53 | 54 | ## SNR 55 | SNR is signal-to-noise ratio. A good SNR is 30db to 40db. 56 | 57 | ## Swap Modem 58 | Ask for the new mac address. 59 | - A mac address contains 12 characters with a combination of numerical numbers 0 through 9 and alphabetical letters ranging a through f. 60 | - Read back to the user the provided mac address. 61 | - If the user says the mac address is correct then use swap_modem function. 62 | 63 | ## Transfer Calls 64 | You are able to transfer calls to the following destinations: Sales, Supervisor, Freeswitch, External. 65 | 66 | ## Customer Verification 67 | Verify customer options, first name, last name, cpni, account number and phone number with verify_customer function. 68 | 69 | # Conversation Flow 70 | These are the steps you need to follow during this conversation. Ensure you are strictly following the steps below in order. 71 | 72 | ## Step 1 73 | Ask the user for the account number. 74 | ## Step 1.1 75 | Ask the user for the CPNI. 76 | ## Step 1.2 77 | Use verify_customer function to verify the customer. Skip the next step if verification is sucessful. 78 | ## Step 1.3 79 | If the account number and CPNI are incorrect after 4 attempts, thank the user for calling and to call back with the correct account number and CPNI. 80 | 81 | 82 | ## Step 2 83 | Perform a speed test and give the results from speed_test function then give the results. 84 | 85 | ## Step 3 86 | Check modem levels with modem_diagnostics function and give the results. 87 | 88 | ## Step 4 89 | Check modem SNR with modem_diagnostics function and give the results. 90 | 91 | ## Step 5 92 | Ask the customer if there is anything else you can help them with. 93 | 94 | ## Step 6 95 | End all calls with saying "Thank you for choosing Livewire." 96 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/speed_test.md: -------------------------------------------------------------------------------- 1 | speed_test 2 | ----------------------- 3 | 4 | * Name:`speed_test` 5 | * Purpose:`Test upload and download speed from the modem` 6 | * Argument:`account_number|7 digit number,cpni|4 digit number` 7 | 8 | ```perl 9 | 10 | my $env = shift; 11 | my $req = Plack::Request->new( $env ); 12 | my $swml = SignalWire::ML->new; 13 | my $post_data = decode_json( $req->raw_body ); 14 | my $data = $post_data->{argument}->{parsed}->[0]; 15 | my $res = Plack::Response->new( 200 ); 16 | my $agent = $req->param( 'agent_id' ); 17 | my $customer = $post_data->{meta_data}->{customer}; 18 | 19 | if ($customer->{modem_speed_upload} && $customer->{modem_speed_download}) { 20 | $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Download speed: $customer->{modem_speed_download}, Upload speed: $customer->{modem_speed_upload}" } ) ); 21 | } else { 22 | $res->body( $swml->swaig_response_json( { response => "Invalid try again speed_test" } ) ); 23 | } 24 | 25 | return $res->finalize; 26 | ``` 27 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/template/conversation.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Conversation Details 5 | 6 | 7 | 8 | 9 |

Conversation Details

10 |
11 | 12 | 13 | 41 | 42 | 47 | 48 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
ID:
Start Date:
Name:
Number:
Input Tokens:
Output Tokens:
40 |
Recording: 43 | 44 | 45 | 46 |
49 |
50 |
51 | 52 | 53 | 58 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 101 | 106 | 107 |
54 | 55 | " class="button-style"> 56 | 57 | 59 | 60 | " class="button-style"> 61 | 62 |
RoleContent
FunctionArguments
97 | 98 | " class="button-style"> 99 | 100 | 102 | 103 | " class="button-style"> 104 | 105 |
108 |
109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/template/conversations.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Converstations 5 | 6 | 7 | 8 | 9 |

Conversations

10 |
11 | 12 | 15 | 18 |
13 |  ">< Prev 14 | 16 | ">Next >  17 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
CreatedNameNumberSummary
">
35 | 36 | 37 | 40 | 43 | 44 |
38 |  ">< Prev 39 | 41 | ">Next >  42 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/template/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome to ZenAI 5 | 6 | 7 | 8 | "> 9 | 10 | 11 | 12 | 13 | 14 | 21 | 22 |
23 |
24 | 25 | 26 | 29 | 35 | 36 |
27 | ZenAI 28 | 30 |

Tech Support
31 | To Get Tech Support:
32 | +1 (213) 75CABLE
33 |

34 |
37 |
38 |
39 |

Customers Serviced

40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
FirstLast
53 |
54 |
55 |
56 |
57 |
58 | π 59 |
60 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /server/Perl_Examples/Zen/verify_customer.md: -------------------------------------------------------------------------------- 1 | verify_customer 2 | ----------------------- 3 | 4 | * Name:`verify_customer` 5 | * Purpose:`Verify customer account number, cpni first, last name and phone number` 6 | * Argument:`account_number|7 digit number,cpni|4 digit number` 7 | 8 | ```perl 9 | 10 | my $env = shift; 11 | my $req = Plack::Request->new( $env ); 12 | my $swml = SignalWire::ML->new; 13 | my $post_data = decode_json( $req->raw_body ); 14 | my $data = $post_data->{argument}->{parsed}->[0]; 15 | my $res = Plack::Response->new( 200 ); 16 | my $agent = $req->param( 'agent_id' ); 17 | 18 | my $dbh = DBI->connect( 19 | "dbi:Pg:dbname=$database;host=$host;port=$port", 20 | $dbusername, 21 | $dbpassword, 22 | { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n"; 23 | my $sql = "SELECT * FROM customers WHERE account_number = ? AND cpni = ? LIMIT 1"; 24 | 25 | my $sth = $dbh->prepare( $sql ); 26 | $sth->bind_param(1,$data->{account_number}); 27 | $sth->bind_param(2,$data->{cpni}); 28 | $sth->execute() or die "Couldn't execute statement: $DBI::errstr"; 29 | 30 | my $agents = $sth->fetchrow_hashref; 31 | 32 | 33 | if ($data->{account_number} eq $agents->{account_number} && $data->{cpni} eq $agents->{cpni}) { 34 | $res->body( $swml->swaig_response_json( { response => "Account verified, proceed", action => [ { set_meta_data => { customer => $agents } } ] } ) ); 35 | } else { 36 | # This is the failure 37 | $res->body( $swml->swaig_response_json( { response => "Account invalid try again" } ) ) ; 38 | } 39 | 40 | return $res->finalize; 41 | ``` 42 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/Procfile: -------------------------------------------------------------------------------- 1 | web: perl ./app.pl daemon --listen "http://*:$PORT" 2 | 3 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/README.md: -------------------------------------------------------------------------------- 1 | # [aical](https://aical.signalwire.me/) 2 | 3 | 4 | ![image](https://github.com/user-attachments/assets/79dcd9e0-cd8a-4608-b38b-810bcd7cad5f) 5 | 6 | 7 | ## Setup instructions to aquire your Google API client ID and OAuth configuration can be found [here](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid). 8 | 9 | 10 | 11 | ![Screenshot 2024-02-21 at 09 37 05](https://github.com/signalwire/digital_employees/assets/13131198/a2eee78f-1b2b-4d1b-8e27-56617ee37239) 12 | 13 | ![Screenshot 2024-02-21 at 09 37 09](https://github.com/signalwire/digital_employees/assets/13131198/d7be3801-b59b-4a9f-a7ad-9cfa0916b166) 14 | 15 | 16 | ![Screenshot 2024-02-21 at 09 37 24](https://github.com/signalwire/digital_employees/assets/13131198/7ec3d98d-b65c-4477-b1a1-fc13ee5aa17e) 17 | 18 | 19 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/assets/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/aical/assets/calendar.png -------------------------------------------------------------------------------- /server/Perl_Examples/aical/assets/calendar_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Perl_Examples/aical/assets/calendar_logo.png -------------------------------------------------------------------------------- /server/Perl_Examples/aical/assets/style.css: -------------------------------------------------------------------------------- 1 | .inline-form { 2 | display: inline-block; 3 | margin-right: 10px; 4 | } 5 | 6 | .pi-container { 7 | position: fixed; 8 | bottom: 10px; 9 | right: 10px; 10 | text-align: right; 11 | } 12 | 13 | .pi-symbol, .pi-link { 14 | font-size: small; 15 | color: #808080; 16 | text-decoration: none; 17 | } 18 | 19 | .pi-link:hover { 20 | text-decoration: underline; 21 | } 22 | 23 | .center-table { 24 | margin-left: auto; 25 | margin-right: auto; 26 | } 27 | 28 | body { 29 | font-family: Tahoma, Arial, sans-serif; 30 | color: #ffffff; 31 | background-color: #222222; 32 | } 33 | 34 | left-col { 35 | width: 20%; 36 | } 37 | 38 | .right-col { 39 | width: 80%; 40 | } 41 | 42 | table { 43 | width: 90%; 44 | margin: 0 auto; 45 | border: none; 46 | border-spacing: 0px; 47 | margin-left: auto; 48 | margin-right: auto; 49 | border-collapse: collapse; 50 | } 51 | 52 | table th, table td { 53 | border: none; 54 | text-align: left; 55 | } 56 | 57 | table td { 58 | padding: 10px; 59 | min-width: 100px; 60 | } 61 | 62 | h1, a { 63 | color: #000000; 64 | } 65 | 66 | h2 { 67 | text-align: center; 68 | color: #ffffff; 69 | } 70 | 71 | a { 72 | text-decoration: none; 73 | color: #50D2E9; /* If there's a conflict, choose one color */ 74 | } 75 | 76 | a:hover { 77 | color: #33CCFF; 78 | } 79 | 80 | button, .submit-button { 81 | font-size: 16px; 82 | color: #FFFFFF; 83 | background-color: #4CAF50; 84 | border: none; 85 | padding: 10px 20px; 86 | text-align: center; 87 | cursor: pointer; 88 | border-radius: 4px; 89 | } 90 | 91 | button, .edit-button { 92 | font-size: 16px; 93 | color: #FFFFFF; 94 | background-color: #4CAF50; 95 | border: none; 96 | padding: 10px 20px; 97 | text-align: center; 98 | cursor: pointer; 99 | border-radius: 4px; 100 | } 101 | 102 | button, .delete-button { 103 | font-size: 16px; 104 | color: #FFFFFF; 105 | background-color: #FF0000; 106 | border: none; 107 | padding: 10px 20px; 108 | text-align: center; 109 | cursor: pointer; 110 | border-radius: 4px; 111 | } 112 | 113 | button, .add-button { 114 | font-size: 16px; 115 | color: #FFFFFF; 116 | background-color: #4CAF50; 117 | border: none; 118 | padding: 10px 20px; 119 | text-align: center; 120 | cursor: pointer; 121 | border-radius: 4px; 122 | } 123 | 124 | button:hover, .submit-button:hover { 125 | background-color: #45a049; 126 | } 127 | 128 | header { 129 | background-color: #f2f2f2; 130 | padding: 10px; 131 | text-align: center; 132 | } 133 | 134 | header nav a { 135 | margin: 0 10px; 136 | text-decoration: none; 137 | color: #333; 138 | } 139 | 140 | .collapsible { 141 | cursor: pointer; 142 | text-align: center; 143 | width: auto; 144 | display: inline-block; 145 | } 146 | 147 | .content { 148 | display: none; 149 | overflow: hidden; 150 | background-color: #222222; 151 | margin: 0 auto; 152 | } 153 | 154 | .copy-button { 155 | text-align: center; 156 | width: auto; 157 | cursor: pointer; 158 | } 159 | 160 | .system { 161 | background-color: #872126; 162 | } 163 | 164 | .assistant { 165 | background-color: #364285; 166 | } 167 | 168 | .user { 169 | background-color: #236A1C; 170 | } 171 | 172 | .function { 173 | background-color: #CD00C4; 174 | } 175 | 176 | .center-table { 177 | margin-left: auto; 178 | margin-right: auto; 179 | border-collapse: collapse; 180 | } 181 | 182 | /* Optional: additional styling for the table */ 183 | .center-table, .center-table th, .center-table td { 184 | border: 0px; 185 | } 186 | 187 | .center-table th, .center-table td { 188 | padding: 8px; 189 | text-align: left; 190 | } 191 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/cpanfile: -------------------------------------------------------------------------------- 1 | requires 'LWP::UserAgent', '6.70'; 2 | requires 'DateTime::Duration', '1.65'; 3 | requires 'UUID', '0.28'; 4 | requires 'DateTime::Format::ISO8601', '0.16'; 5 | requires 'SignalWire::ML', '1.20'; 6 | requires 'SignalWire::RestAPI', '1.5'; 7 | requires 'SignalWire::CompatXML', '1.0'; 8 | requires 'Time::Piece', '1.3401'; 9 | requires 'YAML::PP', 'v0.37.0'; 10 | requires 'List::Util', '1.63'; 11 | requires 'LWP::Protocol::https', '6.10'; 12 | requires 'Plack', '1.0050'; 13 | requires 'Data::Dumper', '2.174'; 14 | requires 'LWP::UserAgent', '6.70'; 15 | requires 'JSON::PP', '4.16'; 16 | requires 'URL::Encode', '0.03'; 17 | requires 'HTTP::Request::Common', '6.44'; 18 | requires 'Env::C', '0.15'; 19 | requires 'DBI', '1.643'; 20 | requires 'DBD::Pg', '3.16.3'; 21 | requires 'HTML::Template::Expr', '0.07'; 22 | requires 'DateTime', '1.59'; 23 | requires 'UUID', '0.28'; 24 | requires 'MIME::Base64', '3.16'; 25 | requires 'Plack::Middleware::Session', '0.33'; 26 | requires 'URI', '5.25'; -------------------------------------------------------------------------------- /server/Perl_Examples/aical/data/tz_to_txt.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use XML::Simple; 6 | use Data::Dumper; 7 | 8 | # Assuming your XML file is named 'timezones.xml' 9 | my $file_name = 'timezones.conf.xml'; 10 | 11 | # Create a new XML::Simple object 12 | my $xml = new XML::Simple; 13 | 14 | my $data = $xml->XMLin($file_name); 15 | 16 | 17 | # Access the timezones and iterate over them 18 | foreach my $zone ( keys %{$data->{timezones}->{zone}} ) { 19 | print $zone . "\n"; 20 | } 21 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/template/error.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Authorization Error 5 | 6 | 7 | 8 |

Authorization Error

9 |

Authorization failed. Please try again.

10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/template/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Google Calendar API Authorization 5 | 6 | 7 | 8 |
9 |

Google Calendar API Authorization

10 | Google Contacts 11 |

Click the button below to authorize access to your Google Calendar:

12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/template/privacy.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Privacy Policy 8 | 9 | 10 |

Privacy Policy

11 |

This Privacy Policy ("Policy") describes how [Your Company Name], a [Your Company's Legal Status] ("Company," "we," "us," or "our"), collects, uses, and protects the personal information you provide to us when using our AI Agent Calendar service (the "Service").

12 | 13 |

1. Information We Collect

14 |

1.1. User-Provided Information: We may collect personal information you provide when using the Service, including but not limited to your name, email address, and other contact information.

15 |

1.2. Usage Data: We may collect information about how you interact with the Service, including usage patterns, preferences, and activity logs.

16 | 17 |

2. How We Use Your Information

18 |

2.1. Service Operation: We use your information to provide and maintain the Service, enhance user experience, and respond to your requests.

19 |

2.2. Communication: We may use your contact information to communicate with you, send updates, and provide customer support.

20 | 21 |

3. Data Protection

22 |

3.1. Security: We implement reasonable security measures to protect your personal information. However, no method of transmission over the internet is entirely secure.

23 | 24 |

4. Sharing Your Information

25 |

4.1. Third-Party Services: We may share your information with third-party service providers that assist us in delivering the Service. These providers are contractually obligated to protect your data.

26 |

4.2. Legal Requirements: We may disclose your information to comply with legal obligations or in response to lawful requests.

27 | 28 |

5. Your Choices

29 |

5.1. Access and Control: You may access, update, or delete your personal information by contacting us.

30 | 31 |

6. Changes to This Policy

32 |

6.1. Updates: We may update this Policy periodically. Any changes will be posted on this page, and the revised Policy will be effective upon posting.

33 | 34 |

7. Contact Us

35 |

7.1. Contact: For questions or concerns regarding this Policy, please contact us at [Your Contact Information].

36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/template/settings.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AI Agent Calendar Settings 5 | 6 | 7 | 8 |
9 |

Your AI Agent Calendar Settings

10 |
11 |
12 | 13 | 14 | 17 | 18 | 19 | 22 | 29 | 30 | 31 | 34 | 41 | 42 | 43 | 46 | 49 | 50 | 51 | 54 | 57 | 58 | 59 | 64 | 65 |
15 | " style="border-radius: 50%;"> 16 |
20 | 21 | 23 | 28 |
32 | 33 | 35 | 40 |
44 | 45 | 47 | "> 48 |
52 | 53 | 55 | 56 |
60 |

61 | 62 |

63 |
66 |
67 |
68 |
69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/template/success.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Authorization Successful 5 | 6 | 7 | 8 |
9 |

Authorization Successful

10 | Google Contacts 11 |

Your application has been authorized to access Google Calendar.

12 |

Your AI Agent calendar include url is

13 |

AI Agent calendar ">settings

14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /server/Perl_Examples/aical/template/tos.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Terms of Service 8 | 9 | 10 |

Terms of Service

11 |

This Terms of Service Agreement (the "Agreement") is entered into between [Your Company Name], a [Your Company's Legal Status] ("Company," "we," "us," or "our"), and you, the user ("User," "you," or "your"). This Agreement governs your access to and use of the AI Agent Calendar service (the "Service").

12 | 13 |

1. Use of the Service

14 |

1.1. Eligibility: You must be at least 18 years old or have the necessary legal capacity in your jurisdiction to use the Service.

15 |

1.2. User Account: To access certain features of the Service, you may be required to create a user account. You are responsible for maintaining the confidentiality of your account credentials and for any activities that occur under your account.

16 |

1.3. Acceptable Use: You agree to use the Service in compliance with all applicable laws and regulations and in a manner that does not violate the rights of others or interfere with the operation of the Service.

17 | 18 |

2. Privacy

19 |

2.1. Data Collection: Our Privacy Policy (see below) explains how we collect, use, and protect your personal information. By using the Service, you consent to the collection and use of your data as described in the Privacy Policy.

20 | 21 |

3. Intellectual Property

22 |

3.1. Ownership: The Service, including all content, features, and functionality, is owned by the Company and protected by intellectual property laws. You may not reproduce, distribute, or modify any part of the Service without our express consent.

23 | 24 |

4. Limitation of Liability

25 |

4.1. Disclaimer: The Service is provided "as is" without warranties of any kind, whether express or implied. We do not guarantee the accuracy or availability of the Service.

26 |

4.2. Limitation of Liability: To the extent permitted by law, we shall not be liable for any direct, indirect, incidental, special, or consequential damages arising from your use of the Service.

27 | 28 |

5. Termination

29 |

5.1. Termination by Us: We reserve the right to terminate or suspend your access to the Service at our discretion, with or without notice, for any reason.

30 | 31 |

6. Governing Law

32 |

6.1. Jurisdiction: This Agreement is governed by and construed in accordance with the laws of [Your Jurisdiction], without regard to its conflict of law principles.

33 | 34 |

7. Contact Information

35 |

7.1. Contact: For questions or concerns regarding this Agreement, please contact us at [Your Contact Information].

36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /server/Python_Examples/bobbystable/Procfile: -------------------------------------------------------------------------------- 1 | web: python app.py 2 | 3 | -------------------------------------------------------------------------------- /server/Python_Examples/bobbystable/env.example: -------------------------------------------------------------------------------- 1 | HTTP_USERNAME=admin 2 | HTTP_PASSWORD=password 3 | -------------------------------------------------------------------------------- /server/Python_Examples/bobbystable/post_prompt.md: -------------------------------------------------------------------------------- 1 | ### **Post-Interaction Summary Instructions** 2 | 3 | After concluding each user interaction, please provide a concise summary of the call details. The summary should include: 4 | 5 | - **User's Request**: A brief description of what the user wanted to accomplish (e.g., create a new reservation, update an existing reservation). 6 | - **Information Collected**: Key details gathered from the user, such as name, party size, date, time, and confirmation of the phone number used. 7 | - **Actions Taken**: Any actions performed during the interaction, like creating, updating, moving, or canceling a reservation. 8 | - **Confirmation Provided**: Details of any confirmations given to the user regarding their reservation status. 9 | 10 | Ensure the summary accurately reflects the conversation and the services provided, while maintaining a friendly and professional tone. 11 | -------------------------------------------------------------------------------- /server/Python_Examples/bobbystable/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | Flask_HTTPAuth 3 | python-dotenv 4 | signalwire_swaig 5 | gunicorn 6 | -------------------------------------------------------------------------------- /server/Python_Examples/bobbystable/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.11.10 2 | -------------------------------------------------------------------------------- /server/Python_Examples/bobbystable/static/img/bobbystable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Python_Examples/bobbystable/static/img/bobbystable.png -------------------------------------------------------------------------------- /server/Python_Examples/bobbystable/static/reservation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bobby's Table 4 | 5 | 6 | 7 | 8 | 9 | 10 | 45 | 46 | 47 | 48 |
49 |

For Reservations Call:

50 |

+1 (754) 43-BOBBY

51 |
52 |
53 |

Current Reservations

54 | {{reservations_table}} 55 |
56 | 57 | -------------------------------------------------------------------------------- /server/Python_Examples/bobbystable/swaig_cli.1: -------------------------------------------------------------------------------- 1 | .TH SWAIG_CLI 1 "October 2023" "Version 1.0" "SWAIG CLI Tool Manual" 2 | .SH NAME 3 | swaig_cli \- A command-line tool for testing SignalWire AI Gateway functions. 4 | 5 | .SH SYNOPSIS 6 | .B swaig_cli 7 | [\fB--url\fR \fIURL\fR] [\fB--get-signatures\fR] [\fB--function\fR \fIFUNCTION_NAME\fR] 8 | 9 | .SH DESCRIPTION 10 | The \fBswaig_cli\fR tool is designed to interact with the SignalWire AI Gateway, allowing users to retrieve function signatures and test specific functions by name. 11 | 12 | .SH OPTIONS 13 | .TP 14 | \fB--url\fR \fIURL\fR 15 | Specify the URL of the SWAIG server. This option is required for all operations. 16 | 17 | .TP 18 | \fB--get-signatures\fR 19 | Retrieve the function signatures from the SWAIG server. This option will output the signatures in JSON format. 20 | 21 | .TP 22 | \fB--function\fR \fIFUNCTION_NAME\fR 23 | Test a specific function by its name. The tool will prompt the user to input required and optional arguments for the function based on the function signature retrieved from the SWAIG server. 24 | 25 | .SH USAGE 26 | To retrieve function signatures from the SWAIG server, use: 27 | .EX 28 | swaig_cli --url http://example.com --get-signatures --function myFunction 29 | .EE 30 | 31 | To test a specific function, use: 32 | .EX 33 | swaig_cli --url http://example.com --function myFunction 34 | .EE 35 | 36 | .SH EXAMPLES 37 | .B Retrieve function signatures: 38 | .EX 39 | swaig_cli --url http://example.com --get-signatures --function myFunction 40 | .EE 41 | 42 | .B Example Request: 43 | .EX 44 | { 45 | "functions": ["myFunction"], 46 | "action": "get_signature", 47 | "version": "2.0", 48 | "content_disposition": "function signature request", 49 | "content_type": "text/swaig" 50 | } 51 | .EE 52 | 53 | .B Example Response: 54 | .EX 55 | { 56 | "function": "myFunction", 57 | "argument": { 58 | "required": ["arg1", "arg2"], 59 | "properties": { 60 | "arg1": {"type": "string"}, 61 | "arg2": {"type": "integer"} 62 | } 63 | } 64 | } 65 | .EE 66 | 67 | .B Test a specific function: 68 | .EX 69 | swaig_cli --url http://example.com --function myFunction 70 | .EE 71 | 72 | .B Example Request: 73 | .EX 74 | { 75 | "function": "myFunction", 76 | "argument": {"parsed": [{"arg1": "value1", "arg2": 42}]} 77 | } 78 | .EE 79 | 80 | .B Example Response: 81 | .EX 82 | { 83 | "response": "Output fed into the LLM" 84 | } 85 | .EE 86 | 87 | .SH AUTHOR 88 | Written by Brian Sest. 89 | 90 | .SH REPORTING BUGS 91 | Report bugs to 92 | 93 | .SH COPYRIGHT 94 | This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 95 | 96 | .SH SEE ALSO 97 | .BR curl (1), 98 | .BR jq (1) 99 | -------------------------------------------------------------------------------- /server/Python_Examples/bobbystable/test_reservation_system.py: -------------------------------------------------------------------------------- 1 | from reservation_system import ( 2 | create_reservation, 3 | get_reservation, 4 | update_reservation, 5 | cancel_reservation, 6 | move_reservation 7 | ) 8 | 9 | def test_reservation_system(): 10 | # Test create reservation 11 | create_data = { 12 | "name": "John Doe", 13 | "party_size": 4, 14 | "date": "2024-12-25", 15 | "time": "18:30", 16 | "phone_number": "+19185551234" 17 | } 18 | print(create_reservation(create_data)) 19 | 20 | # Test get reservation 21 | get_data = {"phone_number": "+19185551234"} 22 | print(get_reservation(get_data)) 23 | 24 | # Test update reservation 25 | update_data = { 26 | "phone_number": "+19185551234", 27 | "party_size": 6 28 | } 29 | print(update_reservation(update_data)) 30 | 31 | # Test move reservation 32 | move_data = { 33 | "phone_number": "+19185551234", 34 | "new_date": "2024-12-26", 35 | "new_time": "19:00" 36 | } 37 | print(move_reservation(move_data)) 38 | 39 | # Test cancel reservation 40 | cancel_data = {"phone_number": "+19185551234"} 41 | print(cancel_reservation(cancel_data)) 42 | 43 | if __name__ == "__main__": 44 | test_reservation_system() -------------------------------------------------------------------------------- /server/Python_Examples/dental_office/.replit: -------------------------------------------------------------------------------- 1 | modules = ["bash", "c-clang14", "python-3.12", "nodejs-20"] 2 | 3 | run = """ 4 | cd server/Python_Examples/dental_office \ 5 | && bash setup_all_with_replit.sh \ 6 | && cd dental_app \ 7 | && source venv/bin/activate \ 8 | && python app.py --host=0.0.0.0 --port=8080 9 | """ 10 | 11 | [nix] 12 | channel = "stable-24_05" 13 | packages = ["nano"] 14 | 15 | [deployment] 16 | run = ["sh", "-c", "cd server/Python_Examples/dental_office && bash setup_all_with_replit.sh && cd dental_app && source venv/bin/activate && python app.py --host=0.0.0.0 --port=8080"] 17 | -------------------------------------------------------------------------------- /server/Python_Examples/dental_office/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | python-dotenv 3 | signalwire 4 | signalwire_swaig==2.7 5 | signalwire_pom==2.7 6 | signalwire_swml==2.7 7 | requests 8 | psutil 9 | faker 10 | -------------------------------------------------------------------------------- /server/Python_Examples/moviebot/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [YEAR] [OWNER] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /server/Python_Examples/moviebot/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for installing swaig_cli and its dependencies 2 | 3 | # Variables 4 | INSTALL_DIR = /usr/bin 5 | MAN_DIR = /usr/share/man/man1 6 | PACKAGE_NAME = swaig_cli 7 | MAN_PAGE = swaig_cli.1 8 | 9 | # Default target 10 | all: install 11 | 12 | # Install target 13 | install: install_deps install_swaig_cli install_man 14 | 15 | # Install dependencies 16 | install_deps: 17 | pip install -r requirements.txt 18 | 19 | # Install swaig_cli 20 | install_swaig_cli: 21 | cp bin/$(PACKAGE_NAME) $(INSTALL_DIR) 22 | 23 | # Install man page 24 | install_man: 25 | cp man/man1/$(MAN_PAGE) $(MAN_DIR) 26 | 27 | # Clean target 28 | clean: 29 | rm -f $(INSTALL_DIR)/$(PACKAGE_NAME) 30 | rm -f $(MAN_DIR)/$(MAN_PAGE) 31 | 32 | .PHONY: all install install_deps install_swaig_cli install_man clean 33 | 34 | -------------------------------------------------------------------------------- /server/Python_Examples/moviebot/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn -w 4 -b 0.0.0.0:$PORT app:app -------------------------------------------------------------------------------- /server/Python_Examples/moviebot/env.example: -------------------------------------------------------------------------------- 1 | TMDB_API_KEY=xxx 2 | HTTP_USERNAME=xxx 3 | HTTP_PASSWORD=xxx 4 | DEBUG=xxx 5 | PORT=xxx -------------------------------------------------------------------------------- /server/Python_Examples/moviebot/prompt.md: -------------------------------------------------------------------------------- 1 | You are a movie expert AI assistant capable of providing detailed information about movies, directors, actors, genres, and personalized recommendations. You have access to the following functions to retrieve up-to-date movie data: 2 | 3 | 1. search_movie: Search for movies by title. 4 | - Parameters: query, language (default: "en-US") 5 | 6 | 2. get_movie_details: Retrieve detailed information about a movie. 7 | - Parameters: movie_id, language (default: "en-US") 8 | 9 | 3. discover_movies: Discover movies by different criteria. 10 | - Parameters: with_genres, primary_release_year, sort_by (default: "popularity.desc"), language (default: "en-US") 11 | 12 | 4. get_trending_movies: Retrieve a list of movies that are currently trending. 13 | - Parameters: time_window (default: "week"), language (default: "en-US") 14 | 15 | 5. get_movie_recommendations: Get recommendations based on a specific movie. 16 | - Parameters: movie_id, language (default: "en-US") 17 | 18 | 6. get_movie_credits: Retrieve cast and crew information for a movie. 19 | - Parameters: movie_id, language (default: "en-US") 20 | 21 | 7. get_person_details: Retrieve detailed information about a person. 22 | - Parameters: person_id, language (default: "en-US"), append_to_response 23 | 24 | 8. get_genre_list: Retrieve the list of official genres. 25 | - Parameters: language (default: "en-US") 26 | 27 | 9. get_upcoming_movies: Retrieve movies that are soon to be released. 28 | - Parameters: language (default: "en-US"), region 29 | 30 | 10. get_now_playing_movies: Retrieve movies currently playing in theaters. 31 | - Parameters: language (default: "en-US"), region 32 | 33 | 11. get_similar_movies: Retrieve movies similar to a specified movie. 34 | - Parameters: movie_id, language (default: "en-US") 35 | 36 | 12. multi_search: Search for movies, TV shows, and people with a single query. 37 | - Parameters: query, language (default: "en-US") 38 | 39 | When a user asks a question, determine if any of these functions can help provide the most accurate and up-to-date information. If so, use the appropriate function to fetch the data before crafting your response. 40 | 41 | Guidelines: 42 | 43 | - Always provide accurate and helpful information. 44 | - Use the latest data from the functions whenever possible. 45 | - Maintain a conversational and friendly tone. 46 | - Respect user preferences and provide personalized recommendations. 47 | - Adhere to OpenAI's policies and avoid disallowed content. 48 | 49 | Example: 50 | 51 | - User: "Can you recommend a good sci-fi movie from last year?" 52 | - Assistant: 53 | 1. Use `discover_movies` with `with_genres` set to the genre ID for sci-fi and `primary_release_year` set to last year. 54 | 2. Fetch the list of movies. 55 | 3. Recommend a movie from the list with a brief description. 56 | -------------------------------------------------------------------------------- /server/Python_Examples/moviebot/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==3.1.0 2 | python-dotenv==1.1.0 3 | Requests==2.32.3 4 | signalwire==2.1.1 5 | -------------------------------------------------------------------------------- /server/Python_Examples/moviebot/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.11.10 2 | -------------------------------------------------------------------------------- /server/Python_Examples/moviebot/signalwire-agent-example/moviebot.md: -------------------------------------------------------------------------------- 1 | # MovieBot - SignalWire AI Agent for Movie Information 2 | 3 | MovieBot is an AI-powered agent built with the SignalWire AI Agents SDK that provides movie information, recommendations, and answers questions about films using The Movie Database (TMDB) API. 4 | 5 | ## Features 6 | 7 | - Movie search and detailed information 8 | - Actor and director information 9 | - Trending and upcoming movie recommendations 10 | - Theater listings (what's playing now) 11 | - Genre-based movie discovery 12 | - Personalized movie recommendations 13 | 14 | ## Setup 15 | 16 | ### Prerequisites 17 | 18 | - Python 3.7 or higher 19 | - SignalWire AI Agents SDK 20 | - TMDB API key (get one at [https://www.themoviedb.org/settings/api](https://www.themoviedb.org/settings/api)) 21 | 22 | ### Installation 23 | 24 | 1. Clone the repository: 25 | 26 | ```bash 27 | git clone https://github.com/yourusername/moviebot.git 28 | cd moviebot 29 | ``` 30 | 31 | 2. Install the required dependencies: 32 | 33 | ```bash 34 | pip install -r requirements.txt 35 | ``` 36 | 37 | 3. Set up your TMDB API key: 38 | 39 | ```bash 40 | export TMDB_API_KEY=your_api_key_here 41 | ``` 42 | 43 | ## Running the MovieBot 44 | 45 | Start the MovieBot agent: 46 | 47 | ```bash 48 | python moviebot.py 49 | ``` 50 | 51 | This will start the agent on the default host (0.0.0.0) and port (3000). You can customize these settings: 52 | 53 | ```bash 54 | python moviebot.py --host 127.0.0.1 --port 8000 55 | ``` 56 | 57 | Once running, the agent will display its URL and basic authentication credentials. 58 | 59 | ## Integration with SignalWire 60 | 61 | To connect the MovieBot to a SignalWire phone number: 62 | 63 | 1. Log in to your SignalWire dashboard 64 | 2. Navigate to Phone Numbers → Manage 65 | 3. Select a number or purchase a new one 66 | 4. Under "When a call comes in," set the Request URL to your MovieBot's URL (e.g., http://your-server:3000/moviebot) 67 | 5. Set the HTTP Authentication Username and Password to match the displayed values 68 | 6. Save your settings 69 | 70 | Now, when someone calls your SignalWire number, they'll be connected to the MovieBot agent! 71 | 72 | ## How it Works 73 | 74 | The MovieBot leverages: 75 | 76 | 1. **SignalWire AI Agents SDK**: Provides the foundation for building conversational AI agents 77 | 2. **OpenAI LLM**: Powers the natural language understanding via SignalWire 78 | 3. **TMDB API**: Provides up-to-date movie information 79 | 80 | The agent is configured with: 81 | - A personality that makes it knowledgeable about movies 82 | - SWAIG functions that allow it to fetch real-time movie data 83 | - Prompt sections that guide its behavior and knowledge 84 | 85 | ## Example Conversations 86 | 87 | - "What movies are trending this week?" 88 | - "Tell me about the movie Inception" 89 | - "Who starred in The Shawshank Redemption?" 90 | - "What's playing in theaters right now?" 91 | - "Recommend some science fiction movies from 2023" 92 | - "Tell me about director Christopher Nolan" 93 | 94 | ## Credits 95 | 96 | - This project uses the [SignalWire AI Agents SDK](https://github.com/signalwire/signalwire-ai-agents) 97 | - Movie data is provided by [The Movie Database (TMDB)](https://www.themoviedb.org/) 98 | 99 | ## License 100 | 101 | This project is licensed under the MIT License - see the LICENSE file for details. 102 | 103 | ## Acknowledgements 104 | 105 | - Thanks to SignalWire for the AI Agents SDK 106 | - Thanks to TMDB for their comprehensive movie API 107 | -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.so 6 | .Python 7 | build/ 8 | develop-eggs/ 9 | dist/ 10 | downloads/ 11 | eggs/ 12 | .eggs/ 13 | lib/ 14 | lib64/ 15 | parts/ 16 | sdist/ 17 | var/ 18 | wheels/ 19 | *.egg-info/ 20 | .installed.cfg 21 | *.egg 22 | 23 | # Virtual Environment 24 | venv/ 25 | env/ 26 | ENV/ 27 | .env 28 | .venv 29 | env.bak/ 30 | venv.bak/ 31 | 32 | # IDE 33 | .idea/ 34 | .vscode/ 35 | *.swp 36 | *.swo 37 | .DS_Store 38 | 39 | # Database 40 | *.db 41 | *.sqlite3 42 | *.sqlite 43 | 44 | # Logs 45 | *.log 46 | logs/ 47 | log/ 48 | 49 | # Local development 50 | .env.local 51 | .env.development.local 52 | .env.test.local 53 | .env.production.local 54 | 55 | # Replit 56 | ## .replit 57 | ## .replit.nix 58 | 59 | # Coverage reports 60 | htmlcov/ 61 | .tox/ 62 | .coverage 63 | .coverage.* 64 | .cache 65 | nosetests.xml 66 | coverage.xml 67 | *.cover 68 | .hypothesis/ 69 | 70 | # Distribution / packaging 71 | .Python 72 | env/ 73 | build/ 74 | develop-eggs/ 75 | dist/ 76 | downloads/ 77 | eggs/ 78 | .eggs/ 79 | lib/ 80 | lib64/ 81 | parts/ 82 | sdist/ 83 | var/ 84 | wheels/ 85 | *.egg-info/ 86 | .installed.cfg 87 | *.egg -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/.replit: -------------------------------------------------------------------------------- 1 | modules = ["python-3.12", "web", "bash"] 2 | run = "export ENABLE_CSRF=false && python3 init_db.py && python3 init_test_data.py && python3 app.py" 3 | 4 | [nix] 5 | channel = "stable-24_05" 6 | 7 | [unitTest] 8 | language = "python3" 9 | 10 | [gitHubImport] 11 | requiredFiles = [".replit", "replit.nix"] 12 | 13 | [deployment] 14 | run = ["sh", "-c", "export ENABLE_CSRF=false && python3 init_db.py && python3 init_test_data.py && python3 app.py"] 15 | deploymentTarget = "cloudrun" 16 | 17 | [[ports]] 18 | localPort = 8080 19 | externalPort = 80 20 | -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/mfa_util.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import requests 3 | import re 4 | from signalwire.rest import Client as SignalWireClient 5 | 6 | class SignalWireMFA: 7 | def __init__(self, project_id: str, token: str, space: str, from_number: str): 8 | try: 9 | self.client = SignalWireClient(project_id, token, signalwire_space_url=f"{space}.signalwire.com") 10 | self.project_id = project_id 11 | self.token = token 12 | self.space = space 13 | self.from_number = from_number 14 | self.base_url = f"https://{space}.signalwire.com/api/relay/rest" 15 | logging.debug(f"Initialized SignalWireMFA with from_number: {self.from_number}") 16 | except Exception as e: 17 | logging.error(f"Failed to initialize SignalWire Client: {e}") 18 | raise 19 | 20 | def send_mfa(self, to_number: str) -> dict: 21 | try: 22 | url = f"{self.base_url}/mfa/sms" 23 | payload = { 24 | "to": to_number, 25 | "from": self.from_number, 26 | "message": "Here is your code: ", 27 | "token_length": 6, 28 | "max_attempts": 3, 29 | "allow_alphas": False, 30 | "valid_for": 3600 31 | } 32 | headers = {"Content-Type": "application/json"} 33 | logging.debug(f"Sending MFA from {self.from_number} to {to_number}") 34 | response = requests.post(url, json=payload, auth=(self.project_id, self.token), headers=headers) 35 | response.raise_for_status() 36 | return response.json() 37 | except Exception as e: 38 | logging.error(f"Error sending MFA: {e}") 39 | raise 40 | 41 | def verify_mfa(self, mfa_id: str, token: str) -> dict: 42 | try: 43 | verify_url = f"{self.base_url}/mfa/{mfa_id}/verify" 44 | payload = {"token": token} 45 | headers = {"Content-Type": "application/json"} 46 | logging.debug(f"Verifying MFA with ID {mfa_id} using token {token}") 47 | response = requests.post(verify_url, json=payload, auth=(self.project_id, self.token), headers=headers) 48 | response.raise_for_status() 49 | return response.json() 50 | except requests.HTTPError as e: 51 | status_code = e.response.status_code 52 | if status_code == 401: 53 | return {"success": False, "message": "Unauthorized: Invalid credentials or MFA ID"} 54 | elif status_code == 400: 55 | return {"success": False, "message": "Bad Request: Invalid MFA code or parameters"} 56 | else: 57 | return {"success": False, "message": f"HTTP error {status_code}: {str(e)}"} 58 | except Exception as e: 59 | logging.error(f"Unexpected error verifying MFA: {e}") 60 | return {"success": False, "message": f"Unexpected error: {str(e)}"} 61 | 62 | def is_valid_uuid(uuid_to_test, version=4): 63 | regex = { 64 | 4: r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$' 65 | } 66 | pattern = regex.get(version) 67 | return bool(pattern and re.match(pattern, uuid_to_test)) 68 | 69 | def validate_phone(phone: str) -> bool: 70 | # Basic E.164 format validation 71 | pattern = r'^\+[1-9]\d{1,14}$' 72 | return bool(re.match(pattern, phone)) -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/prompt.md: -------------------------------------------------------------------------------- 1 | Prompt for Zen Cable SWAIG Bot 2 | 3 | You are Zen, a digital employee at Zen Cable. You assist customers with their account, appointments, modem, and billing needs. 4 | You have access to the following functions: 5 | 6 | Check account balance and due date 7 | Make a payment 8 | Schedule a new appointment 9 | Check for existing/upcoming appointments 10 | Reschedule or cancel an appointment 11 | Check modem status, reboot, or swap a modem 12 | 13 | When a customer asks a question or makes a request, do the following: 14 | 15 | Politely greet the customer. 16 | Ask for their account number or customer ID if you need it and don't already have it. 17 | Use the most relevant function to help with their request. 18 | If the customer asks to schedule or reschedule an appointment, always list the available time frames: 19 | Morning (08:00 AM - 11:00 AM) 20 | Afternoon (02:00 PM - 04:00 PM) 21 | Evening (06:00 PM - 08:00 PM) 22 | All Day (08:00 AM - 08:00 PM) 23 | Summarize the result in clear, friendly language. 24 | If you cannot help with a request, politely explain what you can do (e.g., "I can help you with appointments, billing, or modem issues."). 25 | 26 | Examples of things you can do: 27 | 28 | "What's my current balance?" 29 | "Schedule an installation for next Friday morning." 30 | "What appointment slots are available?" 31 | "Do I have any upcoming appointments?" 32 | "Cancel my appointment for June 5th." 33 | "Reboot my modem." 34 | "Swap my modem for a new one." 35 | 36 | Always be concise, friendly, and professional. 37 | If you need more information from the customer, ask for it clearly. -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/replit.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: { 2 | deps = [ 3 | pkgs.python310 4 | pkgs.python310Packages.pip 5 | pkgs.python310Packages.flask 6 | pkgs.python310Packages.python-dotenv 7 | pkgs.python310Packages.requests 8 | ]; 9 | } -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==3.0.2 2 | python-dotenv==1.0.1 3 | signalwire==2.1.1 4 | signalwire-swaig==2.7.2 5 | schedule==1.2.1 6 | pytz==2024.1 7 | requests==2.31.0 8 | Werkzeug==3.0.1 9 | Jinja2==3.1.2 10 | itsdangerous==2.1.2 11 | click==8.1.7 12 | MarkupSafe==2.1.3 13 | python-dateutil==2.8.2 14 | Flask-WTF==1.2.1 -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/schema.sql: -------------------------------------------------------------------------------- 1 | -- Customers table 2 | CREATE TABLE IF NOT EXISTS customers ( 3 | id INTEGER PRIMARY KEY, 4 | first_name TEXT NOT NULL, 5 | last_name TEXT NOT NULL, 6 | email TEXT UNIQUE NOT NULL, 7 | phone TEXT NOT NULL, 8 | address TEXT NOT NULL, 9 | account_number TEXT UNIQUE NOT NULL, 10 | password_hash TEXT NOT NULL, 11 | password_salt TEXT NOT NULL, 12 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 13 | ); 14 | 15 | -- Password Resets table 16 | CREATE TABLE IF NOT EXISTS password_resets ( 17 | id INTEGER PRIMARY KEY AUTOINCREMENT, 18 | customer_id INTEGER NOT NULL, 19 | token TEXT UNIQUE NOT NULL, 20 | expiry TIMESTAMP NOT NULL, 21 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 22 | FOREIGN KEY (customer_id) REFERENCES customers(id) 23 | ); 24 | 25 | -- Services table 26 | CREATE TABLE IF NOT EXISTS services ( 27 | id INTEGER PRIMARY KEY AUTOINCREMENT, 28 | name TEXT NOT NULL, 29 | description TEXT, 30 | price DECIMAL(10,2) NOT NULL, 31 | type TEXT NOT NULL CHECK(type IN ('cable', 'internet', 'phone')) 32 | ); 33 | 34 | -- Customer Services (subscriptions) 35 | CREATE TABLE IF NOT EXISTS customer_services ( 36 | id INTEGER PRIMARY KEY AUTOINCREMENT, 37 | customer_id INTEGER, 38 | service_id INTEGER, 39 | status TEXT NOT NULL CHECK(status IN ('active', 'suspended', 'cancelled')), 40 | start_date TIMESTAMP NOT NULL, 41 | end_date TIMESTAMP, 42 | FOREIGN KEY (customer_id) REFERENCES customers(id), 43 | FOREIGN KEY (service_id) REFERENCES services(id) 44 | ); 45 | 46 | -- Modems table 47 | CREATE TABLE IF NOT EXISTS modems ( 48 | id INTEGER PRIMARY KEY AUTOINCREMENT, 49 | customer_id INTEGER, 50 | mac_address TEXT UNIQUE NOT NULL, 51 | model TEXT NOT NULL, 52 | status TEXT NOT NULL CHECK(status IN ('online', 'offline', 'rebooting')), 53 | last_seen TIMESTAMP, 54 | FOREIGN KEY (customer_id) REFERENCES customers(id) 55 | ); 56 | 57 | -- Appointments table 58 | CREATE TABLE IF NOT EXISTS appointments ( 59 | id INTEGER PRIMARY KEY AUTOINCREMENT, 60 | customer_id INTEGER, 61 | type TEXT NOT NULL CHECK(type IN ('installation', 'repair', 'upgrade', 'modem_swap')), 62 | status TEXT NOT NULL CHECK(status IN ('scheduled', 'in_progress', 'completed', 'cancelled')), 63 | start_time TIMESTAMP NOT NULL, 64 | end_time TIMESTAMP NOT NULL, 65 | notes TEXT, 66 | FOREIGN KEY (customer_id) REFERENCES customers(id) 67 | ); 68 | 69 | -- Payments table 70 | CREATE TABLE IF NOT EXISTS payments ( 71 | id INTEGER PRIMARY KEY AUTOINCREMENT, 72 | customer_id INTEGER, 73 | amount DECIMAL(10,2) NOT NULL, 74 | payment_date TIMESTAMP NOT NULL, 75 | payment_method TEXT NOT NULL CHECK(payment_method IN ('credit_card', 'debit_card', 'bank_transfer', 'phone')), 76 | status TEXT NOT NULL CHECK(status IN ('pending', 'completed', 'failed')), 77 | transaction_id TEXT UNIQUE, 78 | FOREIGN KEY (customer_id) REFERENCES customers(id) 79 | ); 80 | 81 | -- Billing table 82 | CREATE TABLE IF NOT EXISTS billing ( 83 | id INTEGER PRIMARY KEY AUTOINCREMENT, 84 | customer_id INTEGER, 85 | amount DECIMAL(10,2) NOT NULL, 86 | due_date TIMESTAMP NOT NULL, 87 | status TEXT NOT NULL CHECK(status IN ('pending', 'paid', 'overdue')), 88 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 89 | FOREIGN KEY (customer_id) REFERENCES customers(id) 90 | ); 91 | 92 | -- Service History table 93 | CREATE TABLE IF NOT EXISTS service_history ( 94 | id INTEGER PRIMARY KEY AUTOINCREMENT, 95 | customer_id INTEGER, 96 | service_id INTEGER, 97 | action TEXT NOT NULL CHECK(action IN ('added', 'removed', 'modified')), 98 | action_date TIMESTAMP NOT NULL, 99 | notes TEXT, 100 | FOREIGN KEY (customer_id) REFERENCES customers(id), 101 | FOREIGN KEY (service_id) REFERENCES services(id) 102 | ); -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/setup.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo Creating virtual environment... 3 | python -m venv venv 4 | 5 | echo Activating virtual environment... 6 | call venv\Scripts\activate.bat 7 | 8 | echo Installing dependencies... 9 | pip install -r requirements.txt 10 | 11 | if not exist .env ( 12 | echo Creating .env file... 13 | ( 14 | echo SIGNALWIRE_PROJECT_ID=your_project_id 15 | echo SIGNALWIRE_TOKEN=your_token 16 | echo SIGNALWIRE_SPACE=your_space 17 | echo HTTP_USERNAME=admin 18 | echo HTTP_PASSWORD=admin123 19 | echo HOST=127.0.0.1 20 | echo PORT=8080 21 | echo FLASK_ENV=development 22 | ) > .env 23 | echo Please update the .env file with your SignalWire credentials 24 | ) 25 | 26 | echo Initializing database... 27 | python -c "from app import init_db_if_needed; init_db_if_needed()" 28 | 29 | echo Setup complete! You can now run the application with: 30 | echo venv\Scripts\activate.bat 31 | echo python app.py -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the directory where the script is located 4 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | # Create virtual environment if it doesn't exist 7 | if [ ! -d "$SCRIPT_DIR/venv" ]; then 8 | echo "Creating virtual environment..." 9 | python -m venv "$SCRIPT_DIR/venv" 10 | fi 11 | 12 | # Activate virtual environment 13 | echo "Activating virtual environment..." 14 | source "$SCRIPT_DIR/venv/bin/activate" 15 | 16 | # Install dependencies 17 | echo "Installing dependencies..." 18 | pip install -r "$SCRIPT_DIR/requirements.txt" 19 | 20 | # Create .env file if it doesn't exist 21 | if [ ! -f "$SCRIPT_DIR/.env" ]; then 22 | echo "Creating .env file..." 23 | cat > "$SCRIPT_DIR/.env" << EOL 24 | SIGNALWIRE_PROJECT_ID=your_project_id 25 | SIGNALWIRE_TOKEN=your_token 26 | SIGNALWIRE_SPACE=your_space 27 | HTTP_USERNAME=admin 28 | HTTP_PASSWORD=admin123 29 | HOST=127.0.0.1 30 | PORT=8080 31 | FLASK_ENV=development 32 | EOL 33 | echo "Please update the .env file with your SignalWire credentials" 34 | fi 35 | 36 | # Initialize database 37 | echo "Initializing database..." 38 | cd "$SCRIPT_DIR" 39 | python -c " 40 | from app import init_db_if_needed 41 | init_db_if_needed() 42 | " 43 | 44 | echo "Setup complete! You can now run the application with:" 45 | echo "source $SCRIPT_DIR/venv/bin/activate" 46 | echo "python $SCRIPT_DIR/app.py" -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/static/sw_cable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalwire/digital_employees/b3b8abc9b0c545f8363049f4ea69802938c15a2e/server/Python_Examples/zen_cable/static/sw_cable.png -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/templates/forgot_password.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Zen Cable - Forgot Password 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |

Reset Password

17 | {% with messages = get_flashed_messages(with_categories=true) %} 18 | {% if messages %} 19 | {% for category, message in messages %} 20 |
{{ message }}
21 | {% endfor %} 22 | {% endif %} 23 | {% endwith %} 24 |
25 | 26 |
27 | 28 | 30 |
31 | 32 |
33 |
34 | Back to Login 35 |
36 |
37 |
38 |
39 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /server/Python_Examples/zen_cable/templates/populate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Zen Cable - Environment Setup 7 | 8 | 9 | 10 |
11 |

Zen Cable Environment Setup

12 |
13 | 14 |
15 | 16 | 18 |
19 |
20 | 21 | 23 |
24 |
25 | 26 | 28 |
29 |
30 | 31 | 33 |
34 |
35 | 36 | 38 |
39 |
40 | 41 | 43 |
44 |
45 | 49 |
50 |
51 |
52 | 53 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /server/tools/tap/README.md: -------------------------------------------------------------------------------- 1 | # Listening (tap) to RTP Audio Streams with Python and SWML 2 | 3 | image 4 | 5 | --- 6 | 7 | **Table of Contents** 8 | 9 | - [Core Functionality](#core-functionality) 10 | - [Using tap](#using-tap) 11 | - [Using web tap](#using-web-tap) 12 | - [Utilizing the `tap` Method in SWML](#utilizing-the-tap-method-in-swml) 13 | 14 | --- 15 | 16 | The `tap` Python script is designed to listen to Real-time Transport Protocol (RTP) audio streams, decode PCMU (μ-law) audio, and play it using PyAudio, with support for multiple streams. 17 | 18 | --- 19 | 20 | ## Core Functionality 21 | 22 | The `tap` script handles the following tasks: 23 | 24 | - **RTP Packet Reception**: 25 | Listens for UDP-based RTP packets on a specified IP and port (e.g., `0.0.0.0:5004`). 26 | 27 | - **Audio Decoding**: 28 | Converts PCMU audio (payload type 0) to 16-bit PCM using a μ-law lookup table for playback. 29 | 30 | - **Multi-Stream Support**: 31 | Tracks multiple Synchronization Sources (SSRCs) and allows switching between them using arrow keys. 32 | 33 | - **Stream Cleanup**: 34 | Removes inactive SSRCs after a 2-second timeout. 35 | 36 | --- 37 | 38 | ## Using tap 39 | 40 | - **Run the Script**: Execute it in a Windows command prompt or terminal. 41 | - **Controls**: 42 | - **Left Arrow**: Switch to the previous active SSRC. 43 | - **Right Arrow**: Switch to the next active SSRC. 44 | - **'q'**: Exit the script. 45 | - **Requirements**: Ensure PyAudio is installed (`pip install pyaudio`) and your system has an audio output device. 46 | 47 | --- 48 | 49 | ## Using web tap 50 | 51 | - **Run the Script**: Execute it in a Windows command prompt or terminal. 52 | - **View Webpage**: 53 | Go to `http://ip:8080`, where `ip` is the address of the machine running the Python script. For example: `http://192.168.100.50:8080`. 54 | - **Start Listening**: Click to start receiving the incoming RTP stream. 55 | - **Listen/Listening**: 56 | If you have one or more than one stream, click "Listen." This will change the button to "Listening," and you will be able to hear the RTP stream through your web browser. 57 | 58 | ![image](https://github.com/user-attachments/assets/e964f1bf-21a3-4b6f-9561-a4a562aa5204) 59 | 60 | --- 61 | 62 | ## Utilizing the `tap` Method in SWML 63 | 64 | The `tap` method in SignalWire Markup Language (SWML) enables developers to stream call audio to an external destination via WebSocket or RTP. This functionality is essential for applications requiring real-time audio processing, such as call monitoring or recording. 65 | 66 | ### Key Parameters 67 | 68 | - **uri** (string, required): Specifies the destination for the audio stream. Supported formats include: 69 | - `rtp://IP:port` 70 | - `ws://example.com` 71 | - `wss://example.com` 72 | 73 | - **control_id** (string, optional): An identifier for the tap session, useful for managing or stopping the tap later. If not provided, a unique ID is auto-generated and stored in the `tap_control_id` variable. 74 | 75 | - **direction** (string, optional): Defines which part of the audio to tap: 76 | - `speak`: Audio sent from the party. 77 | - `listen`: Audio received by the party. 78 | - `both`: Both incoming and outgoing audio. 79 | 80 | Default is `speak`. 81 | 82 | - **codec** (string, optional): Specifies the audio codec, either `PCMU` or `PCMA`. Default is `PCMU`. 83 | 84 | - **rtp_ptime** (integer, optional): Applicable for RTP streams; sets the packetization time in milliseconds. Default is 20 ms. 85 | 86 | ### Example Usage 87 | 88 | Add the following to your SWML script to enable RTP tap: 89 | 90 | ```json 91 | { 92 | "version": "1.0.0", 93 | "sections": { 94 | "main": [ 95 | { 96 | "tap": { 97 | "uri": "rtp://127.0.0.1:5004", 98 | "direction": "both" 99 | } 100 | } 101 | ] 102 | } 103 | } 104 | ``` 105 | 106 | Note: Update the uri IP address to your public IP address. 107 | -------------------------------------------------------------------------------- /server/tools/tap/SWML.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "sections": { 4 | "main": [ 5 | { 6 | "tap": { 7 | "uri": "rtp://127.0.0.1:5004", 8 | "direction": "both" 9 | } 10 | } 11 | } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /serverless/Bartender/SWML.yaml: -------------------------------------------------------------------------------- 1 | sections: 2 | main: 3 | - answer: {} 4 | - record_call: 5 | format: "wav" 6 | stereo: "true" 7 | - ai: 8 | params: 9 | verbose_logs: "true" 10 | post_prompt_url: "optional.fake.tld" 11 | post_prompt: 12 | top_p: 0.6 13 | temperature: 0.6 14 | text: | 15 | Summarize the conversation and send the conversation as a message to the user in an anonymous json object. 16 | 17 | # Step 1 18 | 19 | hints: 20 | - "drinks" 21 | languages: 22 | - code: "en-US" 23 | voice: "alloy" 24 | name: "English" 25 | fillers: 26 | - "one moment" 27 | - "one moment please" 28 | engine: "openai" 29 | SWAIG: 30 | defaults: {} 31 | functions: 32 | - purpose: "use to send text messages to a user" 33 | argument: 34 | type: "object" 35 | properties: 36 | to: 37 | type: "string" 38 | description: "The user's number in e.164 format" 39 | message: 40 | description: "the message to send to the user" 41 | type: "string" 42 | data_map: 43 | expressions: 44 | - string: "${args.message}" 45 | output: 46 | response: "Message sent." 47 | action: 48 | - SWML: 49 | version: "1.0.0" 50 | sections: 51 | main: 52 | - send_sms: 53 | to_number: "${args.to}" 54 | region: "us" 55 | body: "${args.message}, ${chunks[0].text} ${chunks[0].document_id} Reply STOP to stop." 56 | from_number: "+15555555555" 57 | pattern: ".*" 58 | function: "send_message" 59 | - function: "get_vector_data" 60 | fillers: 61 | en-US: 62 | - "This is the get vector data function firing" 63 | data_map: 64 | webhooks: 65 | - method: "POST" 66 | url: "https://space_name.signalwire.com/api/datasphere/documents/search" 67 | headers: 68 | Content-Type: "application/json" 69 | Authorization: "Basic OGVhMjI0YzktM--USE--Project_ID:API_KEY--TO-BASE64-ENCODE--NkYjFh" 70 | params: 71 | query_string: "${args.user_question}" 72 | document_id: "694ced7b-b656-417e-bc86-ce22549b4562" 73 | count: 1 74 | output: 75 | response: " Use this information to answer the users query, only provide answers from this information and do not make up anything: ${chunks[0].text} and ${chunks[0].document_id}" 76 | action: [] 77 | purpose: "The question the user will ask" 78 | argument: 79 | type: "object" 80 | properties: 81 | user_question: 82 | type: "string" 83 | description: "The question the user will ask. Use url encoding between words. for example: how%20are%20you" 84 | prompt: 85 | text: | 86 | You're an expert mixologist and work as a bartender. You have one function to send messages and You have a function get_vector_data to answer user questions about how to make drinks. Only provide the user information from the get_vector_data function 87 | 88 | # Step 1 89 | Greet the user. 90 | 91 | # Step 2 92 | Ask the user what drink would you like to make today. 93 | 94 | # Step 3 95 | Tell the user the the answer to their question. 96 | 97 | # Step 4 98 | Ask the user if there is anything else you can help them with. 99 | 100 | # Step 5 101 | Offer to send the details in a message to the user. Keep assisting the user until the user is ready to end the call. 102 | temperature: 0.6 103 | top_p: 0.6 104 | version: "1.0.0" 105 | -------------------------------------------------------------------------------- /serverless/Dr_Bob_Confirm/README.md: -------------------------------------------------------------------------------- 1 | # Dr. Bob Confirm 2 | 3 | image 4 | 5 | 6 | 7 | # Dr. Bob Confirm: How SignalWire's Confirm Option Works 8 | 9 | ## Table of Contents 10 | 1. [Introduction](#introduction) 11 | 2. [Step-by-Step Workflow](#step-by-step-workflow) 12 | 3. [SWML Setup](#swml-setup) 13 | 4. [Conclusion](#conclusion) 14 | 15 | ## Introduction 16 | Dr. Bob Confirm is an example that leverages SignalWire's confirm option to streamline call transfers. In this process, when a call is received, SignalWire’s AI digital employee handles the initial interaction before routing the call to a live representative. Let's take a look at the process and detailed setup instructions using SWML (SignalWire Markup Language). 17 | 18 | ## Step-by-Step Workflow 19 | 1. **Call Initiation:** 20 | A person calls a SignalWire number, C2C, or SIP endpoint. 21 | 22 | 2. **AI Digital Employee Interaction:** 23 | SignalWire's AI digital employee answers the call. 24 | 25 | 3. **User Request:** 26 | The user requests to speak to a representative. 27 | 28 | 4. **Parallel Dialing:** 29 | The Confirm feature has predefined endpoints that dial in parallel, meaning each endpoint rings simultaneously. 30 | 31 | 5. **Endpoint Answering:** 32 | When an endpoint answers, a secondary SWML script is executed. This script prompts the endpoint to either press 1 to accept the call or decline. 33 | 34 | 6. **Call Transfer Completion:** 35 | If the endpoint accepts by pressing 1, the call transfer is successfully completed. 36 | 37 | ## SWML Setup 38 | To implement this process, follow these steps: 39 | 40 | 1. **Prepare SWML Files:** 41 | Two files are provided in this example: `SWML1.json` and `SWML2.json`. 42 | - Create two SWML bins in your SignalWire space. 43 | - Copy and paste the content from each respective file into its corresponding bin. 44 | 45 | 2. **Configure the Confirm Option:** 46 | - Retrieve the SWML bin URL from the SWML2 example, for instance: 47 | `hxxps://replace-this-url-with-your-part2-swml-bin-url.signalwire.com/relay-bins/qlqle239-27b8-4e8a-8e5e-c868ef8269cf` 48 | - Use this URL as the `confirm` option under `connect` in your configuration. 49 | 50 | ## Conclusion 51 | The Dr. Bob Confirm example illustrates an efficient method to handle call transfers using SignalWire's AI digital employee and the confirm feature. By following the outlined process and setting up your SWML bins as described, you can streamline communications and ensure that calls are routed effectively. This approach not only enhances the caller experience but also optimizes the overall call management process. 52 | 53 | -------------------------------------------------------------------------------- /serverless/Dr_Bob_Confirm/SWML2.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "sections": { 4 | "main": [ 5 | { 6 | "play": [ 7 | "say:This is the overflow for customer service." 8 | ] 9 | }, 10 | { 11 | "prompt": { 12 | "play": "say:Please press 1 to connect to the caller." 13 | } 14 | }, 15 | { 16 | "switch": { 17 | "variable": "prompt_value", 18 | "default": [ 19 | { 20 | "play": "say:You have declined the call. Goodbye." 21 | }, 22 | { 23 | "hangup": {} 24 | } 25 | ], 26 | "case": { 27 | "1": [ 28 | { 29 | "play": "say:Connecting you to the caller now." 30 | } 31 | ] 32 | } 33 | } 34 | } 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /serverless/ESP8266_Temperature_and_Humidity_Sensor_Bot/SWML.yaml: -------------------------------------------------------------------------------- 1 | sections: 2 | main: 3 | - answer: {} 4 | - record_call: 5 | format: "wav" 6 | stereo: "true" 7 | - ai: 8 | params: 9 | verbose_logs: "true" 10 | debug_webhook_url: "https://public.requestbin.com/r" 11 | post_prompt_url: "https://public.requestbin.com/r" 12 | post_prompt: 13 | top_p: 0.6 14 | temperature: 0.6 15 | text: "Summarize the conversation in an anonymous json object." 16 | pronounce: 17 | - ignore_case: 0 18 | with: "miles per hour" 19 | replace: "mph" 20 | hints: 21 | - "weather" 22 | - "forecast" 23 | languages: 24 | - code: "en-US" 25 | voice: "echo" 26 | name: "English" 27 | fillers: 28 | - "one moment" 29 | - "one moment please" 30 | engine: "openai" 31 | SWAIG: 32 | defaults: {} 33 | functions: 34 | - purpose: "use to send text messages to a user" 35 | argument: 36 | type: "object" 37 | properties: 38 | to: 39 | type: "string" 40 | description: "The user's number in e.164 format" 41 | message: 42 | description: "the message to send to the user" 43 | type: "string" 44 | data_map: 45 | expressions: 46 | - string: "${args.message}" 47 | output: 48 | response: "Message sent." 49 | action: 50 | - SWML: 51 | version: "1.0.0" 52 | sections: 53 | main: 54 | - send_sms: 55 | to_number: "${args.to}" 56 | region: "us" 57 | body: " ${args.message} Reply STOP to stop." 58 | from_number: "+15555555555" 59 | pattern: ".*" 60 | function: "send_message" 61 | - function: "get_temperature_humidity" 62 | data_map: 63 | webhooks: 64 | - url: "https://api.thingspeak.com/channels/1464062/feeds.json?results=2" 65 | method: "GET" 66 | output: 67 | response: "The current weather is ${feeds[0].field1} and humidity is ${feeds[0].field2}" 68 | action: [] 69 | purpose: "get the temperature and humidity." 70 | argument: 71 | type: "object" 72 | properties: 73 | zip: 74 | type: "string" 75 | description: "Temperature and humidity." 76 | prompt: 77 | text: | 78 | Your name is Ziggy, a digital employee for Ziggy's attic temperature and humidity sensor powered by SignalWire. Greet the user with that information. You have two functions to help you get temperature and humidity information and one function to send messages. 79 | 80 | Tell the user what the temperature and humidity is from the sensor. 81 | You have to use get_temperature_humidity function. 82 | 83 | # Step 1 84 | Greet the user. 85 | 86 | # Step 2 87 | Tell the user the temperature and humidity. 88 | 89 | # Step 3 90 | Ask the user if they would like the information sent to them via sms? Keep assisting the user until the user is ready to end the call. 91 | temperature: 0.6 92 | top_p: 0.6 93 | version: "1.0.0" 94 | -------------------------------------------------------------------------------- /serverless/ESP8266_Temperature_and_Humidity_Sensor_Bot/arduino_sketch/README.md: -------------------------------------------------------------------------------- 1 | # INO file for Arduino IDE 2 | 3 | 4 | This is a Weather Station Kit from [Amazon](https://www.amazon.com/gp/product/B07GPBBY7F) . Open the ino file with your [Arduio IDE](https://docs.arduino.cc/software/ide-v2/tutorials/getting-started/ide-v2-downloading-and-installing). 5 | 6 | In the ino file, replace the following with your settings: 7 | * `const char* WIFI_SSID = "your-ssid";` 8 | * `const char* WIFI_PWD = "your-l33t-password";` 9 | * `const char *host = "api.thingspeak.com";` 10 | * `const char *api_key ="3L8replace-meNA0G";` 11 | * `String OPEN_WEATHER_MAP_APP_ID = "1636c9bc22replace-with-your-key0f8732b5";` 12 | * `String OPEN_WEATHER_MAP_LOCATION = "33141";` 13 | 14 | The kit can be purchased from [Amazon](https://www.amazon.com/gp/product/B07GPBBY7F) When you get a the kit there are further instructions on how to setup an account and configure the settings in https://thingspeak.com 15 | 16 | 17 | -------------------------------------------------------------------------------- /serverless/ESP8266_Temperature_and_Humidity_Sensor_Bot/arduino_sketch/Weather_StationImages.h: -------------------------------------------------------------------------------- 1 | #define WiFi_Logo_width 60 2 | #define WiFi_Logo_height 36 3 | const uint8_t WiFi_Logo_bits[] PROGMEM = { 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 5 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 6 | 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 7 | 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 8 | 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 9 | 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 10 | 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 11 | 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 12 | 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, 13 | 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, 14 | 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, 15 | 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, 16 | 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 17 | 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 18 | 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, 19 | 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, 20 | 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, 21 | 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, 22 | 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 23 | 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 24 | 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 25 | 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 26 | 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 | }; 29 | 30 | const uint8_t activeSymbole[] PROGMEM = { 31 | B00000000, 32 | B00000000, 33 | B00011000, 34 | B00100100, 35 | B01000010, 36 | B01000010, 37 | B00100100, 38 | B00011000 39 | }; 40 | 41 | const uint8_t inactiveSymbole[] PROGMEM = { 42 | B00000000, 43 | B00000000, 44 | B00000000, 45 | B00000000, 46 | B00011000, 47 | B00011000, 48 | B00000000, 49 | B00000000 50 | }; 51 | -------------------------------------------------------------------------------- /serverless/Flos_Flowers/README.md: -------------------------------------------------------------------------------- 1 | 2 | # FLO'S FLOWERS 3 | SignalWire AI flower sending bot. This digital Employee sends an ecard image (mms/sms) along with a text message. 4 | 5 | Live demo: Call `+1-337-435-6937` 6 | 7 | To use this serverless example: 8 | 9 | * Copy the [SWML.json](https://github.com/Len-PGH/Flos_Flowers/blob/main/SWML.json) example and create a new SWML bin. 10 | * Update and save the `change-me` sections and `"from_number": "+13374356937",` in the SWML.json file. 11 | * point a SignalWire number to the SWML bin you just created. 12 | * Enjoy! 13 | 14 | 15 | ![image](https://github.com/Len-PGH/Flos_Flowers/assets/13131198/9c196439-c791-4ebe-8fc1-23ab934daf71) 16 | 17 | ------------------- 18 | 19 | Please note that you can change the images in the prompt by updating the URLs. Ensure that the names of the image files correspond to the names of the flowers they represent. 20 | 21 | This implies that when replacing an image URL, the new image should be named appropriately to reflect the type of flower it depicts. For example, if you are updating the URL for an image of a rose to oranges, the file should be named something like `https://domain.tld/oranges.jpg` or `https://domain.tld/oranges.png` to clearly indicate what the image represents. 22 | 23 | ## Message sent 24 | 25 | Message and mms sent. 26 | 27 | ![image](https://github.com/Len-PGH/Flos_Flowers/assets/13131198/655c2f17-a6a0-4938-9e86-66b3edd03764) 28 | 29 | 30 | 31 | ## SWML Video Walkthrough 32 | 33 | 34 | 35 | [![image](https://github.com/Len-PGH/Flos_Flowers/assets/13131198/739be785-a247-4485-8cd6-f621363a8eeb)](https://youtu.be/1xcjBo1dMic?feature=shared) 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | --------------------- 50 | 51 | ### SignalWire 52 | 53 | #### SignalWire’s AI Agent for Voice allows you to build and deploy your own digital employee. Powered by advanced natural language processing (NLP) capabilities, your digital employee will understand caller intent, retain context, and generally behave in a way that feels “human-like”. In fact, you may find that it behaves exactly like your best employee, elevating the customer experience through efficient, intelligent, and personalized interactions. 54 | 55 | 56 | -------------------------------------------------------------------------------- /serverless/Flos_Flowers/SWML.yaml: -------------------------------------------------------------------------------- 1 | version: 1.0.0 2 | sections: 3 | main: 4 | - answer: {} 5 | - record_call: 6 | format: wav 7 | stereo: 'true' 8 | - ai: 9 | post_prompt: 10 | text: Summerize the conversation including all order details 11 | top_p: 0.1 12 | temperature: 0.1 13 | post_prompt_auth_user: change-me 14 | params: 15 | local_tz: America/New_York 16 | swaig_allow_settings: 'true' 17 | verbose_logs: 'true' 18 | swaig_post_swml_vars: 'true' 19 | swaig_post_conversation: 'true' 20 | swaig_allow_swml: 'true' 21 | debug_webhook_url: change-me 22 | post_prompt_url: change-me 23 | prompt: 24 | top_p: 0.1 25 | text: "# Personality and Introduction\r\n\r\nYour name is flo and you work for Flow's Flowers. Greet the user with that information.\r\n\r\n# Your Skills, Knowledge, and Behavior\r\n\r\nFour types of flowers available. Those are roses, tulips, lillies and dasies\r\n\r\n# Send Flowers\r\n\r\nHere are the media urls for the flower types\r\nhttps://flosflowers.signalwire.me/assets/roses.png\r\nhttps://flosflowers.signalwire.me/assets/tulips.png\r\nhttps://flosflowers.signalwire.me/assets/lillies.png\r\nhttps://flosflowers.signalwire.me/assets/daisies.png\r\n\r\n\r\n\r\n# Static message\r\n\r\nAlways include this in the message: You can send flowers by visiting https://flosflowers.signalwire.me or call +13374356937\r\n\r\n# Step 1\r\n\r\nAsk for flower type\r\n\r\n# Step 2\r\n\r\nAsk for the recipents phone number\r\n\r\n# Step 3\r\n\r\nAsk what message the user would like to send\r\n\r\n# Step 4\r\n\r\nRead back the number the user gave you. Give an opportunity to get the recipents phone number and message again if corrections are needed.\r\n\r\n# Step 5\r\n\r\nSend flowers\r\n\r\n# Step 6\r\n\r\nAsk if there is anything else you can help the user with" 26 | temperature: 0.1 27 | hints: 28 | - florist 29 | - virtual flowers 30 | - roses 31 | - tulips 32 | - dasies 33 | - lillies 34 | SWAIG: 35 | defaults: 36 | web_hook_auth_user: change-me 37 | web_hook_url: change-me 38 | web_hook_auth_password: change-me 39 | functions: 40 | - argument: 41 | properties: 42 | to: 43 | type: string 44 | description: The users number in e.164 format 45 | media: 46 | type: string 47 | description: the media URL to send to the user 48 | message: 49 | type: string 50 | description: the message to send to the user 51 | type: object 52 | data_map: 53 | expressions: 54 | - output: 55 | action: 56 | - SWML: 57 | sections: 58 | main: 59 | - send_sms: 60 | media: 61 | - '${args.media}' 62 | body: '${args.message}, Reply STOP to stop.' 63 | region: us 64 | from_number: '+13374356937' 65 | to_number: '${args.to}' 66 | version: 1.0.0 67 | response: Message sent. 68 | pattern: .* 69 | string: '${args.message}' 70 | purpose: use to send text messages to a user 71 | function: send_mms 72 | post_prompt_auth_password: change-me 73 | languages: 74 | - code: en-US 75 | engine: openai 76 | fillers: 77 | - one moment 78 | - one moment please 79 | name: English 80 | voice: nova 81 | -------------------------------------------------------------------------------- /serverless/Flos_Flowers/prompt.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ``` 4 | 5 | # Personality and Introduction 6 | 7 | Your name is flo and you work for Flow's Flowers. Greet the user with that information. 8 | 9 | # Your Skills, Knowledge, and Behavior 10 | 11 | Four types of flowers available. Those are roses, tulips, lillies and dasies 12 | 13 | # Send Flowers 14 | 15 | Here are the media urls for the flower types 16 | https://flosflowers.signalwire.me/assets/roses.png 17 | https://flosflowers.signalwire.me/assets/tulips.png 18 | https://flosflowers.signalwire.me/assets/lillies.png 19 | https://flosflowers.signalwire.me/assets/daisies.png 20 | 21 | 22 | 23 | # Static message 24 | 25 | Always include this in the message: You can send flowers by visiting https://flosflowers.signalwire.me or call +13374356937 26 | 27 | # Step 1 28 | 29 | Ask for flower type 30 | 31 | # Step 2 32 | 33 | Ask for the recipents phone number 34 | 35 | # Step 3 36 | 37 | Ask what message the user would like to send 38 | 39 | # Step 4 40 | 41 | Read back the number the user gave you. Give an opportunity to get the recipents phone number and message again if corrections are needed. 42 | 43 | # Step 5 44 | 45 | Send flowers 46 | 47 | # Step 6 48 | 49 | Ask if there is anything else you can help the user with 50 | 51 | ``` 52 | -------------------------------------------------------------------------------- /serverless/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Digital Employee List 4 | 5 | * [Open Weather Assistant](https://github.com/signalwire/digital_employees/blob/main/serverless/OpenWeather_Assistant) 6 | * [Flos_Flowers](https://github.com/signalwire/digital_employees/tree/main/serverless/Flos_Flowers) 7 | * [Weather_Bot](https://github.com/signalwire/digital_employees/tree/main/serverless/Weather_Bot) 8 | * [Thermal Thrillers](https://github.com/signalwire/digital_employees/tree/main/serverless/Thermal_Thrillers) 9 | * [AI Bartender](https://github.com/signalwire/digital_employees/blob/main/serverless/Bartender) 10 | * [Dr. Bob Confirm](https://github.com/signalwire/digital_employees/tree/main/serverless/Dr_Bob_Confirm) 11 | ------------------------ 12 | 13 | Copy, Paste, Edit, Save. Copy the SWML example, paste into a new SWML bin, edit and save. 14 | 15 | # Calling with PSTN 16 | 17 | Using a PSTN phone number to dial to your Digital Employee. 18 | 19 | ### 20 | 21 | ### Assigning a SWML Bin to a phone number 22 | 23 | * Edit an existing phone number 24 | * In the `HANDLE USING` section, select `a SWML Script` 25 | * In the `WHEN A CALL COMES IN` section select the SWML Bin to use. 26 | 27 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/2feb0525-1e87-4ff7-928d-341b5f940190) 28 | 29 | 30 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/f39b5e40-719d-47d4-aa10-ffcefd3b6b78) 31 | 32 | 33 | 34 | # Calling With SIP 35 | 36 | Using a sip address to dial to your Digital Employee via a Domain App. 37 | 38 | ### Create a SWML Bin 39 | 40 | * Create a SWML Bin from one of the examples. 41 | 42 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/85a36e64-8ec0-426c-a412-c6d1a4b412dd) 43 | 44 | 45 | ### Create a Domain App 46 | 47 | * Create the Domain App if one doesn't exist or if you want a new one. 48 | * In the `HANDLE USING` section, select `a SWML Script` 49 | * In the `WHEN A CALL COMES IN` section select the SWML Bin to use. 50 | 51 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/1c8761fd-d265-469a-b155-a6646bd25589) 52 | 53 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/9140e2c9-48ff-4338-a31f-55400f3489d4) 54 | 55 | 56 | ### Assign to a Domain App 57 | 58 | * Assign the SWML Bin to an existing Domain App. 59 | * In the `HANDLE USING` section, select `a SWML Script` 60 | * In the `WHEN A CALL COMES IN` section select the SWML Bin to use. 61 | 62 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/a27a32ac-ebf1-4803-91e2-699718aab08f) 63 | 64 | 65 | 66 | ![image](https://github.com/signalwire/digital_employees/assets/13131198/9140e2c9-48ff-4338-a31f-55400f3489d4) 67 | 68 | 69 | ### Make Call 70 | 71 | 72 | Now you can make a call to your digital employee with `sip:Call-This@len-Call.dapp.signalwire.com` 73 | -------------------------------------------------------------------------------- /serverless/Sigmond/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## SignalWire AI Agent: Sigmond 3 | 4 | ### **Introduction** 5 | 6 | A robot that serves as a live demo for the SignalWire Programmable AI Agent framework. Assisting users with SignalWire, FreeSWITCH, and related topics. 7 | 8 | --- 9 | 10 | ### **Key Features** 11 | 1. **Personality**: 12 | - Speak like a fictional robot (e.g., C-3PO from *Star Wars*). 13 | - Use brief, casual responses to encourage conversation. 14 | 15 | 2. **Focus**: 16 | - Discuss SignalWire, FreeSWITCH, and Programmable Unified Communications (PUC). 17 | - Emphasize SignalWire's innovation in making communication programmable and accessible. 18 | 19 | 3. **Interactive Support**: 20 | - Guide slide show presentations by using `get_visual_input` to interpret and present slide data meaningfully. 21 | - Provide insights, avoiding literal descriptions like "This is a pie chart." 22 | 23 | --- 24 | 25 | ### **SignalWire Innovations** 26 | 1. **Mission**: 27 | - Revolutionize business communication with programmable, composable, and easy-to-manage solutions. 28 | - Built on FreeSWITCH, offering flexibility and scalability. 29 | 30 | 2. **PUC (Programmable Unified Communications)**: 31 | - Combines CPaaS, UCaaS, and CCaaS for a unified and scalable platform. 32 | - Solves inefficiencies in traditional systems and fragmented cloud services. 33 | 34 | 3. **Composable Telecom Infrastructure**: 35 | - Modular components for advanced communication workflows: 36 | - Rooms: Audio/video conferencing spaces. 37 | - Subscribers: SIP endpoints and mobile apps. 38 | - AI Agents: Intelligent assistants. 39 | - SWML: JSON-defined or YAML workflows for real-time event handling. 40 | 41 | --- 42 | 43 | ### **Key Features of SignalWire** 44 | 1. **Programmable and Composable**: Real-time workflow manipulation via APIs. 45 | 2. **Low Latency**: Native media stack integration. 46 | 3. **Global Scalability**: Geographic redundancy for seamless deployment. 47 | 4. **Cost Efficiency**: Consolidates tools to reduce operational costs. 48 | 5. **Developer-Centric**: Supports open standards like SIP, REST, and WebRTC. 49 | 50 | --- 51 | 52 | ### **SWML (SignalWire Markup Language)** 53 | - JSON-based scripting language for defining IVRs and AI workflows. 54 | - Enables real-time updates like call transfers and dynamic routing. 55 | 56 | --- 57 | 58 | ### **TL;DR: SignalWire Summary** 59 | SignalWire empowers developers with **Programmable Unified Communications (PUC)** by providing: 60 | - Modular and scalable telecom infrastructure. 61 | - Real-time workflow control via APIs and webhooks. 62 | - Low latency, global scalability, and cost efficiency. 63 | - SWML scripting for advanced workflows. 64 | 65 | SignalWire simplifies complex communication systems, allowing businesses to innovate faster, reduce costs, and deliver exceptional user experiences. 66 | 67 | --- 68 | 69 | ### **Pronunciations** 70 | The following words are pronounced as: 71 | - CPaaS: "See pass" 72 | - UCaaS: "You kass" 73 | - CCaaS: "See kass" 74 | - FreeSWITCH: "Free switch" 75 | - PUC: "Puck" 76 | - AI: "A-Eye" 77 | - SignalWire: "Cygnalwyre" 78 | 79 | --- 80 | 81 | ### **Interactive Features** 82 | - **Play Testimonials**: Use commands like "start" or "stop" to play or halt testimonial videos. 83 | - **Slide Show**: Guide users through presentations with insights drawn from visual inputs. 84 | 85 | --- 86 | 87 | For further details, contact **SignalWire Support** or explore their [developer portal](https://developer.signalwire.com). 88 | -------------------------------------------------------------------------------- /serverless/Thermal_Thrillers/README.md: -------------------------------------------------------------------------------- 1 | # Enhancing HVAC Customer Service with a Digital Employee 2 | 3 | At SignalWire, we are pioneering the integration of advanced AI technology to revolutionize how people interact with customers. In this example, the digital employee, Rachael, will assist customers outside regular business hours and during peak times. Here's how Rachael can make your HVAC inquiries and service requests smoother and more efficient. 4 | 5 | ## AI Digital Employee 6 | 7 | Rachael is designed to be more than just a digital assistant. She is the forefront of our customer service, equipped to handle calls with the efficiency and understanding you'd expect from a human operator. Rachael is operational after hours, ensuring that Thermal Thrillers HVAC supports you 24/7. 8 | 9 | ### **Capabilities** 10 | 11 | - **Scheduling Appointments:** Whether it's after hours or within our business hours (7:30AM to 5PM M-F), Rachael can schedule appointments and arrange for urgent service if needed. 12 | - **Collecting Essential Information:** From service addresses to specific HVAC unit details, Rachael collects all necessary information to ensure our technicians are well-prepared for the job ahead. 13 | - **Address Verification:** Rachael ensures the accuracy of your service address, enhancing service reliability. 14 | 15 | ### **Operational Hours and Payment Methods** 16 | 17 | - **Business Hours:** Monday to Friday, 7:30AM to 5PM. 18 | - **After Hours:** Service available through Rachael. 19 | - **Payment Options:** We accept cash, checks, and credit cards for your convenience. 20 | 21 | ## How The Bot Works 22 | 23 | The digital employee Rachael's interaction with customers is streamlined into several steps, ensuring a thorough collection of information while providing a friendly and helpful service. 24 | 25 | 1. **Greeting and Assistance:** Based on the time of your call, Rachael will greet you with the appropriate message and offer assistance. 26 | 2. **Information Gathering:** Rachael will guide you through a series of steps to gather all necessary information regarding your HVAC needs. 27 | 3. **Appointment Scheduling:** Depending on the time of your call, Rachael will either schedule an appointment or arrange for after-hours service. 28 | 4. **Custom Summarize Conversation:** Define the key information you want to collect by the end of the call. This data can be utilized later for various purposes, such as importing into a database, forwarding in an SMS to a designated recipient (e.g., an on-call technician), or performing any other action you need with the collected information. 29 | 30 | ## Summarize Conversation 31 | 32 | `summarize_conversation` is a special built custom function that you can define required array of defined fields the ai agent will summarize and populate to use for after the call is over. 33 | 34 | Example 35 | 36 | ```json 37 | 38 | { 39 | "AGE": 20, 40 | "CUSTOMER": "true", 41 | "OWNER": "true", 42 | "RENTAL": "false", 43 | "AFTERHOURS": "false", 44 | "WARRANTY": "true", 45 | "PHONE": "+15555551234", 46 | "TENANT_PHONE": null, 47 | "DATETIME": "2024-08-27 02:24 PM", 48 | "SYSTEM": "residential", 49 | "owner_name": "Jim Smith", 50 | "owner_phone": "+18675309", 51 | "tenant_name": null, 52 | "tenant_phone": null, 53 | "address": "123 Forbes Avenue", 54 | "city": "Pittsburgh", 55 | "state": "PA", 56 | "zipcode": "15222", 57 | "customer": "true", 58 | "owner": "true", 59 | "hvac_type": "residential", 60 | "previous_repairs": "No", 61 | "hvac_make": "Next", 62 | "hvac_model": "TE 24,000", 63 | "unit_age": 20, 64 | "warranty": "true", 65 | "additional_info": "Maintenance contract", 66 | "summary": "You are experiencing issues with your air conditioner" 67 | } 68 | ``` 69 | 70 | --------------------- 71 | 72 | ### SignalWire 73 | 74 | #### SignalWire’s AI Agent for Voice allows you to build and deploy your own digital employee. Powered by advanced natural language processing (NLP) capabilities, your digital employee will understand caller intent, retain context, and generally behave in a way that feels “human-like”. In fact, you may find that it behaves exactly like your best employee, elevating the customer experience through efficient, intelligent, and personalized interactions. 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /serverless/Thermal_Thrillers/post_prompt.md: -------------------------------------------------------------------------------- 1 | For the AGE field, only allow the numeric value in years. For these fields CUSTOMER,OWNER,RENTAL,AFTERHOURS,WARRANTY, the only valid values are "true" or "false". For the PHONE and TENANT_PHONE fields, format the number in e.164. For the DATETIME field, format the date in stftime format %Y-%m-%d %H:%M %p.For the SYSTEM field, the only valid values are "residential, "commercial" or "industrial", Please summarize the message as a valid anonymous JSON object like this:{ \\"owner_name\\": \\"OWNER_NAME\\", \\"owner_phone\\": \\"OWNER_PHONE\\", \\"tenant_name\\": \\"TENANT_NAME\\", \\"tenant_phone\\": \\"TENANT_PHONE\\",\\"address\\": \\"ADDRESS\\", \\"city\\": \\"CITY\\"\\n", \\"state\\": \\"STATE\\", \\"zipcode\\": \\"ZIPCODE\\",\\"customer\\": \\"CUSTOMER\\", \\"owner\\": \\"OWNER\\", \\"hvac_type\\: \\"SYSTEM\\", \\"previous_repairs\\": \\"PREVIOUS_REPAIRS\\", \\"hvac_make\\": \\"MAKE\\", \\"hvac_model\\": \\"MODEL\\", \\"unit_age\\": \\"AGE\\", \\"warranty\\": \\"WARRANTY\\", \\"additional_info\\": \\"ADDITIONAL_INFO\\", \\"summary\\": \\"SUMMARY\\", \\"datetime":\\"DATETIME\\", \\"afterhours\\": \\"AFTERHOURS"\\, \\"rental\\": \\"RENTAL\\" }' -------------------------------------------------------------------------------- /serverless/Thermal_Thrillers/prompt.md: -------------------------------------------------------------------------------- 1 | ``` 2 | Your name is Rachael. You are an AI Agent. You work for a heating and air conditioning company named Thermal Thrillers H V A C 3 | 4 | # Info 5 | You are incapable of diagnosing problems. 6 | Someone will call back to schedule an appointment. 7 | 8 | # Hours of operation 9 | 7:30AM to 5PM M-F. Closed Saturday and Sunday. 10 | 11 | # Payment methods 12 | cash, check, and credit cards. 13 | 14 | ## What to do if the caller wants to schedule an appointment 15 | After 5PM and before 7:30AM Monday through Friday or anytime Saturday or Sunday, ask the caller if they want to dispatch after hours service. 16 | Between 7:30AM and 5PM Monday through Friday, ask the caller if they want to have someone call back and schedule an appointment. 17 | 18 | ## Greetings used by the AI Agent 19 | After business hours answer the call with 'You have reached Thermal Thrillers after hours. I am an AI Agent. How may I help you?' 20 | During business hours answer the call with 'Thank you for calling Thermal Thrillers. I am an AI Agent. How may I help you?' 21 | 22 | ## Step 1 23 | collect information to help our technician. 24 | ## Step 2 25 | Get the service address including city and state. 26 | ## Step 3 27 | use the verify_address function and use those results to update the information 28 | ## Step 4 29 | current customer? 30 | ## Step 5 31 | property owner or a tenant? 32 | ## Step 6 33 | Then Get the owners name and contact number for scheduling purposes. 34 | ## Step 7 35 | If the owner, is this a rental? Get the tenants name and phone number 36 | ## Step 8 37 | Is this residential, commercial or industrial? 38 | ## Step 9 39 | Have any previous repairs or services performed? 40 | ## Step 10 41 | Make and model of your unit?. 42 | ## Step 11 43 | Aproximate age of your unit in years? 44 | ## Step 12 45 | Is the unit under warranty or maintenance contract? 46 | ## Step 13 47 | Additional information 48 | ## Step 14 49 | Summarize the conversation not leaving out any details. Verify with the details with user, offer to send them a message with the details. 50 | ## Step 15 51 | If after hours dispatch after hours service. Inform the customer that a technician will be calling them back shortly to arrange service. 52 | ## Step 16 53 | If during business hours schedule a call back to schedule an appointment. Inform the customer that a technician will be calling them back to arrange service. 54 | ``` 55 | --------------------------------------------------------------------------------