File size: 4,525 Bytes
1a63d97
 
 
35d97c8
 
552fb1e
35d97c8
 
552fb1e
35d97c8
 
1a63d97
 
35d97c8
1a63d97
 
 
35d97c8
1a63d97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54d4a4a
 
1a63d97
 
 
 
 
 
 
35d97c8
1a63d97
 
 
 
 
 
 
 
 
 
 
361f9d4
87dcd10
1a63d97
156e5b3
 
 
 
 
 
361f9d4
 
156e5b3
 
 
 
c490c32
156e5b3
 
 
 
 
87dcd10
156e5b3
 
 
149eeaf
 
 
156e5b3
c490c32
156e5b3
 
149eeaf
1a63d97
156e5b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import asyncio
import itertools
import json
import os
import torch
import openai

class ChatService:
    def __init__(self, api="openai", model_id = "gpt-3.5-turbo"):
        self._api = api
        self._device = "cuda:0" if torch.cuda.is_available() else "cpu"
        openai.api_key = os.getenv("OPENAI_API_KEY")
        self._model_id = model_id

    def _should_we_send_to_voice(self, sentence):
        sentence_termination_characters = [".", "?", "!"]
        close_brackets = ['"', ')', ']']

        temination_charicter_present = any(c in sentence for c in sentence_termination_characters)
 
        # early exit if we don't have a termination character
        if not temination_charicter_present:
            return None

        # early exit the last char is a termination character
        if sentence[-1] in sentence_termination_characters:
            return None
        
        # early exit the last char is a close bracket
        if sentence[-1] in close_brackets:
            return None
        
        termination_indices = [sentence.rfind(char) for char in sentence_termination_characters]
        # Filter out termination indices that are not followed by whitespace or end of string
        termination_indices = [i for i in termination_indices if sentence[i+1].isspace()]
        last_termination_index = max(termination_indices)
        # handle case of close bracket
        while last_termination_index+1 < len(sentence) and sentence[last_termination_index+1] in close_brackets:
            last_termination_index += 1

        text_to_speak = sentence[:last_termination_index+1]
        return text_to_speak
    
    def ignore_sentence(self, text_to_speak):
        # exit if empty, white space or an single breaket
        if text_to_speak.isspace():
            return True
        # exit if not letters or numbers
        has_letters = any(char.isalpha() for char in text_to_speak)
        has_numbers = any(char.isdigit() for char in text_to_speak)
        if not has_letters and not has_numbers:
            return True
        return False

    async def get_responses_as_sentances_async(self, messages, cancel_event=None):
        llm_response = ""
        current_sentence = ""
        delay = 0.1

        while True:
            try:
                response = await openai.ChatCompletion.acreate(
                    model=self._model_id,
                    messages=messages,
                    temperature=1.0,  # use 0 for debugging/more deterministic results
                    stream=True
                )

                async for chunk in response:
                    if cancel_event is not None and cancel_event.is_set():
                        return
                    chunk_message = chunk['choices'][0]['delta']
                    if 'content' in chunk_message:
                        chunk_text = chunk_message['content']
                        current_sentence += chunk_text
                        llm_response += chunk_text
                        text_to_speak = self._should_we_send_to_voice(current_sentence)
                        if text_to_speak:
                            current_sentence = current_sentence[len(text_to_speak):]
                            yield text_to_speak, True
                        else:
                            yield current_sentence, False

                if cancel_event is not None and cancel_event.is_set():
                    return
                if len(current_sentence) > 0:
                    yield current_sentence, True
                return

            except openai.error.APIError as e:
                print(f"OpenAI API returned an API Error: {e}")
                print(f"Retrying in {delay} seconds...")
                await asyncio.sleep(delay)
                delay *= 2

            except openai.error.APIConnectionError as e:
                print(f"Failed to connect to OpenAI API: {e}")
                print(f"Retrying in {delay} seconds...")
                await asyncio.sleep(delay)
                delay *= 2

            except openai.error.RateLimitError as e:
                print(f"OpenAI API request exceeded rate limit: {e}")
                print(f"Retrying in {delay} seconds...")
                await asyncio.sleep(delay)
                delay *= 2

            except Exception as e:
                print(f"OpenAI API unknown error: {e}")
                print(f"Retrying in {delay} seconds...")
                await asyncio.sleep(delay)
                delay *= 2