├── .gitattributes ├── AsyncClient.py ├── Client.py ├── ElevateAI.py ├── LICENSE ├── README.md ├── example_code_with_elevatepy.py └── requirements.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | # Explicitly declare text files you want to always be normalized and converted 7 | # to native line endings on checkout. 8 | *.py text 9 | 10 | -------------------------------------------------------------------------------- /AsyncClient.py: -------------------------------------------------------------------------------- 1 | """Async version of SDK.""" 2 | 3 | import aiohttp 4 | import json 5 | 6 | 7 | class AsyncClient: 8 | """Base class.""" 9 | 10 | BOUNDARY = "_____123456789_____" 11 | 12 | def __init__(self, token, url="https://api.elevateai.com/v1"): 13 | """Initialize.""" 14 | self.url = url 15 | self.api_token = token 16 | self.declareUri = url + "/interactions" 17 | self.uploadUri = url + "/interactions/%s/upload" 18 | self.statusUri = url + "/interactions/%s/status" 19 | self.transcriptsUri = url + "/interactions/%s/transcripts" 20 | self.transcriptsUri2 = url + "/interactions/%s/transcripts/punctuated" 21 | self.aiUri = url + "/interactions/%s/ai" 22 | self.uploadHeader = { 23 | "Content-Type": "multipart/form-data;boundary=%s" % self.BOUNDARY, 24 | "X-API-TOKEN": token, 25 | } 26 | self.jsonHeader = { 27 | "Content-Type": "application/json; charset=utf-8", 28 | "X-API-TOKEN": token, 29 | } 30 | 31 | async def declare( 32 | self, 33 | languageTag="en-us", 34 | vertical="default", 35 | transcriptionMode="highAccuracy", 36 | mediafile=None, 37 | url=None, 38 | originalFilename=None, 39 | externalIdentifier=None, 40 | ): 41 | """First step is to declare the interaction.""" 42 | data = { 43 | "type": "audio", 44 | "downloadUri": url, 45 | "languageTag": languageTag, 46 | "vertical": vertical, 47 | "audioTranscriptionMode": transcriptionMode, 48 | "includeAiResults": True, 49 | } 50 | if originalFilename: 51 | data["originalFilename"] = originalFilename 52 | if externalIdentifier: 53 | data["externalIdentifier"] = externalIdentifier 54 | 55 | async with aiohttp.ClientSession() as asess: 56 | async with asess.post( 57 | self.declareUri, headers=self.jsonHeader, json=data 58 | ) as rsp: 59 | if rsp.status == 401: # If status code is Unauthorized 60 | print("Declare:Received 401, check if token is correct.") 61 | 62 | raw_response = await rsp.text() 63 | 64 | if raw_response: 65 | try: 66 | i = json.loads(raw_response) 67 | except json.JSONDecodeError: 68 | print("Declare: Failed to parse response as JSON.") 69 | return None 70 | else: 71 | print("Declare: Empty response received.") 72 | return None 73 | 74 | """If a filepath was passed in, go ahead and upload the file.""" 75 | if mediafile: 76 | await self.upload(i, mediafile) 77 | i["status"] = await self.status(i) 78 | return i 79 | 80 | async def upload(self, i, f): 81 | """Second step is to upload the file.""" 82 | if type(i) == dict: 83 | i = i["interactionIdentifier"] 84 | async with aiohttp.ClientSession() as asess: 85 | with aiohttp.MultipartWriter("form-data", 86 | boundary=self.BOUNDARY) as dw: 87 | fp = dw.append( 88 | open(f, "rb"), 89 | headers={"Content-Type": "application/octet-stream"} 90 | ) 91 | fp.set_content_disposition("form-data", filename=f) 92 | rsp = await asess.post( 93 | self.uploadUri % i, headers=self.uploadHeader, data=dw 94 | ) 95 | return rsp.ok 96 | 97 | async def status(self, interaction): 98 | """Check status of interaction.""" 99 | if type(interaction) == dict: 100 | interaction = interaction["interactionIdentifier"] 101 | 102 | async with aiohttp.ClientSession() as asess: 103 | async with asess.get( 104 | self.statusUri % interaction, headers=self.jsonHeader 105 | ) as rsp: 106 | raw_response = await rsp.text() 107 | 108 | if raw_response: 109 | try: 110 | j = json.loads(raw_response) 111 | return j["status"] 112 | except json.JSONDecodeError: 113 | print("Status: Failed to parse response as JSON.") 114 | return None 115 | else: 116 | print("Status: Empty status response received.") 117 | return None 118 | 119 | async def transcripts(self, interaction, punctuated=True): 120 | """Get the transcriptions.""" 121 | if type(interaction) == dict: 122 | interaction = interaction["interactionIdentifier"] 123 | url = self.transcriptsUri2 if punctuated else self.transcriptsUri 124 | async with aiohttp.ClientSession() as asess: 125 | rsp = await asess.get(url % interaction, headers=self.jsonHeader) 126 | return await rsp.json() 127 | 128 | async def ai(self, interaction): 129 | """Get the JSON AI results.""" 130 | if type(interaction) == dict: 131 | interaction = interaction["interactionIdentifier"] 132 | async with aiohttp.ClientSession() as asess: 133 | rsp = await asess.get(self.aiUri % interaction, 134 | headers=self.jsonHeader) 135 | return await rsp.json() 136 | -------------------------------------------------------------------------------- /Client.py: -------------------------------------------------------------------------------- 1 | """Class to use when interacting with ElevateAI.""" 2 | 3 | import requests 4 | import json 5 | 6 | 7 | class Client: 8 | """Base class.""" 9 | 10 | def __init__(self, url="https://api.elevateai.com/v1", token=None): 11 | """Initialize.""" 12 | self.url = url 13 | self.api_token = token 14 | self.declareUri = url + "/interactions" 15 | self.uploadUri = url + "/interactions/%s/upload" 16 | self.statusUri = url + "/interactions/%s/status" 17 | self.transcriptsUri = url + "/interactions/%s/transcripts" 18 | self.transcriptsUri2 = url + "/interactions/%s/transcripts/punctuated" 19 | self.aiUri = url + "/interactions/%s/ai" 20 | self.uploadHeader = {"X-API-TOKEN": token} 21 | self.jsonHeader = { 22 | "Content-Type": "application/json; charset=utf-8", 23 | "X-API-TOKEN": token, 24 | } 25 | self.session = requests.session() 26 | self.session.headers.update(self.jsonHeader) 27 | 28 | def declare( 29 | self, 30 | languageTag="en-us", 31 | vertical="default", 32 | transcriptionMode="highAccuracy", 33 | mediafile=None, 34 | url=None, 35 | ): 36 | """First step is to declare the interaction.""" 37 | data = { 38 | "type": "audio", 39 | "downloadUrl": url, 40 | "languageTag": languageTag, 41 | "vertical": vertical, 42 | "audioTranscriptionMode": transcriptionMode, 43 | "includeAiResults": True, 44 | } 45 | rsp = self.session.post(self.declareUri, data=json.dumps(data)) 46 | i = rsp.json() 47 | if mediafile: 48 | self.upload(i, mediafile) 49 | i["status"] = self.status(i) 50 | return i 51 | 52 | def upload(self, i, f): 53 | """Second step is to upload the file.""" 54 | if type(i) == dict: 55 | i = i["interactionIdentifier"] 56 | files = [("", (f, open(f, "rb"), "application/octet-stream"))] 57 | rsp = requests.post(self.uploadUri % i, 58 | headers=self.uploadHeader, files=files) 59 | return rsp 60 | 61 | def status(self, interaction): 62 | """Check the status of the interaction.""" 63 | if type(interaction) == dict: 64 | interaction = interaction["interactionIdentifier"] 65 | rsp = self.session.get(self.statusUri % interaction) 66 | return rsp.json()["status"] 67 | 68 | def transcripts(self, interaction, punctuated=True): 69 | """Get the transcriptions.""" 70 | if type(interaction) == dict: 71 | interaction = interaction["interactionIdentifier"] 72 | url = self.transcriptsUri2 if punctuated else self.transcriptsUri 73 | rsp = self.session.get(url % interaction) 74 | return rsp.json() 75 | 76 | def ai(self, interaction): 77 | """Get the JSON AI results.""" 78 | if type(interaction) == dict: 79 | interaction = interaction["interactionIdentifier"] 80 | rsp = self.session.get(self.aiUri % interaction) 81 | return rsp.json() 82 | 83 | 84 | # if __name__ == "__main__": 85 | # import time 86 | # from pathlib import Path 87 | 88 | # # files = list(Path('d:/dev/elevateai-cli/sample-media').glob('*.wav')) 89 | # files = list(Path("c:/tmp").glob("*.wav")) 90 | 91 | # cli = Client("http://api.elevateAI.com/v1", 92 | # "") 93 | # tab = [] 94 | # for f in files * 4: 95 | # fn = str(f) 96 | # print("declaring interaction on %s ..." % fn) 97 | # entry = cli.declare( 98 | # languageTag="en-us", transcriptionMode="highAccuracy", mediafile=fn 99 | # ) 100 | # tab.append(entry) 101 | 102 | # while True: 103 | # for e in tab: 104 | # s = cli.status(e) 105 | # if s != e["status"]: 106 | # e["status"] = s 107 | # if s == "processed": 108 | # tx = cli.transcripts(e) 109 | # aiResult = cli.ai(e) 110 | # print("Results[%s]:" % e["interactionIdentifier"], tx, aiResult) 111 | 112 | # processed = len([i for i in tab if i["status"] == "processed"]) 113 | # if processed == len(tab): 114 | # print("DONE!") 115 | # break 116 | # else: 117 | # print("......") 118 | # time.sleep(10) 119 | -------------------------------------------------------------------------------- /ElevateAI.py: -------------------------------------------------------------------------------- 1 | """ElevateAI functions to get transcriptions.""" 2 | 3 | import requests 4 | import json 5 | 6 | 7 | def DeclareAudioInteraction( 8 | language, 9 | verticle, 10 | downloadUri, 11 | token, 12 | audioTranscriptionMode, 13 | includeAiResults: bool, 14 | originalFileName=None, 15 | externalIdentifier=None, 16 | ): 17 | """1st step in processing an interaction is to declare the interaction.""" 18 | url = "https://api.elevateai.com/v1/interactions/" 19 | 20 | payload = json.dumps( 21 | { 22 | "type": "audio", 23 | "languageTag": language, 24 | "vertical": verticle, 25 | "audioTranscriptionMode": audioTranscriptionMode, 26 | "downloadUri": downloadUri, 27 | "includeAiResults": includeAiResults, 28 | "originalfilename": originalFileName, 29 | "externalidentifier": externalIdentifier, 30 | } 31 | ) 32 | 33 | if downloadUri is None: 34 | payload = json.dumps( 35 | { 36 | "type": "audio", 37 | "languageTag": language, 38 | "vertical": verticle, 39 | "audioTranscriptionMode": audioTranscriptionMode, 40 | "includeAiResults": includeAiResults, 41 | "originalfilename": originalFileName, 42 | "externalidentifier": externalIdentifier, 43 | } 44 | ) 45 | 46 | headers = {"X-API-TOKEN": token, "Content-Type": "application/json"} 47 | 48 | response = requests.request("POST", url, headers=headers, data=payload) 49 | return response 50 | 51 | 52 | def GetInteractionStatus(interactionId, token): 53 | """Check if interaction has been processed.""" 54 | url = "https://api.elevateai.com/v1/interactions/%s/status" % interactionId 55 | 56 | headers = {"X-API-TOKEN": token, "Content-Type": "application/json"} 57 | 58 | response = requests.request("GET", url, headers=headers) 59 | 60 | return response 61 | 62 | 63 | def UploadInteraction( 64 | interactionId, token, localFilePath, fileName, originalFileName=None 65 | ): 66 | """Upload file to ElevateAI.""" 67 | url = "https://api.elevateai.com/v1/interactions/%s/upload" % interactionId 68 | 69 | payload = {} 70 | 71 | files = [ 72 | (fileName, (fileName, open(localFilePath, "rb"), "application/octet-stream")) 73 | ] 74 | headers = {"X-API-Token": token} 75 | 76 | response = requests.request("POST", url, headers=headers, data=payload, files=files) 77 | 78 | return response 79 | 80 | 81 | def GetWordByWordTranscript(interactionId, token): 82 | """Get the word by word transcription of the interaction.""" 83 | url = "https://api.elevateai.com/v1/interactions/%s/transcript" % interactionId 84 | 85 | headers = {"X-API-TOKEN": token, "Content-Type": "application/json"} 86 | 87 | response = requests.request("GET", url, headers=headers) 88 | 89 | return response 90 | 91 | 92 | def GetPuncutatedTranscript(interactionId, token): 93 | """Get the punctuated version of the transcription.""" 94 | url = ( 95 | "https://api.elevateai.com/v1/interactions/%s/transcripts/punctuated" 96 | % interactionId 97 | ) 98 | 99 | headers = {"X-API-TOKEN": token, "Content-Type": "application/json"} 100 | 101 | response = requests.request("GET", url, headers=headers) 102 | 103 | return response 104 | 105 | 106 | def GetAIResults(interactionId, token): 107 | """Get JSON with AI results.""" 108 | url = "https://api.elevateai.com/v1/interactions/%s/ai" % interactionId 109 | 110 | headers = {"X-API-TOKEN": token, "Content-Type": "application/json"} 111 | 112 | response = requests.request("GET", url, headers=headers) 113 | 114 | return response 115 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 ElevateAI 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |
6 |

7 | 8 | Website 9 | 10 | | 11 | 12 | Documentation 13 | 14 | | 15 | 16 | Blog 17 | 18 |

19 |
20 | 21 | # ElevateAI Python SDK 22 | 23 | [ElevateAI](https://www.elevateai.com) provides an API for Speech-to-text (ASR), behavioral analysis and sentiment analysis of voice interactions. 24 | 25 | There are three implementations available: 26 | 27 | - AsyncClient.py :: defines class to be instantiated when needing concurrency. 28 | - Client.py :: defines class is to be instantiated 29 | - ElevateAI.py :: defines functions that can be called. 30 | 31 | 32 | ## Example 33 | This examples use ElevateAI.py. 34 | 35 | 1. [Signup](https://app.elevateai.com) and retrieve API token from ElevateAI. 36 | 1. Declare an interaction. Provide a URI if you want ElevateAI to download the interaction via a Public URI. 37 | 2. Retrieve Interaction ID from JSON response and store. 38 | 3. Upload a file. 39 | 4. Check status every 30 seconds using Interaction ID until status returns 'processed' or an [error status](https://docs.elevateai.com/tutorials/check-the-processing-status). 40 | 5. Retrieve results - [phrase-by-phrase transcript](https://docs.elevateai.com/tutorials/get-phrase-by-phrase-transcript), [punctuated transcript](https://docs.elevateai.com/tutorials/get-punctuated-transcript), and [AI results](https://docs.elevateai.com/tutorials/get-cx-ai). 41 | 42 | 43 | ```python 44 | import ElevateAI 45 | import time 46 | 47 | #Step 1 48 | token = "API-TOKEN" 49 | langaugeTag = "en-us" 50 | vert = "default" 51 | transcriptionMode = "highAccuracy" 52 | localFilePath = "A:\\05212005-255.wav" 53 | #extension needed for codec parsing 54 | fileName = "05212005-255.wav" 55 | 56 | #Step 2 57 | declareResp = ElevateAI.DeclareAudioInteraction(langaugeTag, vert, None, token, transcriptionMode, False) 58 | declareJson = declareResp.json() 59 | interactionId = declareJson["interactionIdentifier"] 60 | 61 | #Step 3 62 | uploadInteractionResponse = ElevateAI.UploadInteraction(interactionId, token, localFilePath, fileName) 63 | 64 | #Step 4 65 | #Loop over status until processed 66 | while True: 67 | getInteractionStatusResponse = ElevateAI.GetInteractionStatus(interactionId,token) 68 | getInteractionStatusResponseJson = getInteractionStatusResponse.json() 69 | if getInteractionStatusResponseJson["status"] == "processed" or getInteractionStatusResponseJson["status"] == "fileUploadFailed" or getInteractionStatusResponseJson["status"] == "fileDownloadFailed" or getInteractionStatusResponseJson["status"] == "processingFailed" : 70 | break 71 | time.sleep(30) 72 | 73 | #Step 5 74 | #get results after file is processed 75 | getWordByWordTranscriptResponse = ElevateAI.GetWordByWordTranscript(interactionId, token) 76 | getPuncutatedTranscriptResponse = ElevateAI.GetPuncutatedTranscript(interactionId, token) 77 | getAIResultsResponse = ElevateAI.GetAIResults(interactionId, token) 78 | 79 | ``` 80 | -------------------------------------------------------------------------------- /example_code_with_elevatepy.py: -------------------------------------------------------------------------------- 1 | """Example code for interacting with ElevateAI API.""" 2 | 3 | import ElevateAI 4 | import time 5 | 6 | # Prereq - make sure you create a free account at 7 | # https://app.elevateai.com and create a token. 8 | token = "d7011e44-2266-4a64-bf12-5f2af2aeb84b" 9 | langaugeTag = "en-us" 10 | vert = "default" 11 | transcriptionMode = "highAccuracy" 12 | localFilePath = "/Users/nali/WIP/ElevateAIPythonSDK/sample.wav" 13 | fileName = "sample1.wav" 14 | originalFileName = "sample1-originalfilename.wav" 15 | externalIdentifier = "My own id" 16 | 17 | # Step 1,2 18 | declareResp = ElevateAI.DeclareAudioInteraction( 19 | langaugeTag, 20 | vert, 21 | None, 22 | token, 23 | transcriptionMode, 24 | False, 25 | originalFileName, 26 | externalIdentifier, 27 | ) 28 | 29 | declareJson = declareResp.json() 30 | 31 | interactionId = declareJson["interactionIdentifier"] 32 | 33 | # Step 3 34 | uploadInteractionResponse = ElevateAI.UploadInteraction( 35 | interactionId, token, localFilePath, fileName 36 | ) 37 | # Step 4 38 | # Loop over status until processed 39 | while True: 40 | getInteractionStatusResponse = ElevateAI.GetInteractionStatus( 41 | interactionId, token) 42 | getInteractionStatusResponseJson = getInteractionStatusResponse.json() 43 | if ( 44 | getInteractionStatusResponseJson["status"] == "processed" 45 | or getInteractionStatusResponseJson["status"] == "fileUploadFailed" 46 | or getInteractionStatusResponseJson["status"] == "fileDownloadFailed" 47 | or getInteractionStatusResponseJson["status"] == "processingFailed" 48 | ): 49 | break 50 | time.sleep(30) 51 | 52 | 53 | # Step 6 54 | # get results after file is processed 55 | getWordByWordTranscriptResponse = ElevateAI.GetWordByWordTranscript( 56 | interactionId, token 57 | ) 58 | getPuncutatedTranscriptResponse = ElevateAI.GetPuncutatedTranscript( 59 | interactionId, token 60 | ) 61 | getAIResultsResponse = ElevateAI.GetAIResults(interactionId, token) 62 | 63 | input() 64 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.8.4 2 | Requests==2.31.0 3 | textual==0.38.1 4 | --------------------------------------------------------------------------------