voice input [test mode]: init

This commit is contained in:
if 2023-04-07 23:10:58 +03:00
parent 05a85cee1e
commit 40b05641fe
9 changed files with 160 additions and 44 deletions

View File

@ -27,7 +27,6 @@ repos:
- id: check-added-large-files - id: check-added-large-files
- id: detect-private-key - id: detect-private-key
- id: requirements-txt-fixer - id: requirements-txt-fixer
- id: debug-statements
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort
rev: 5.12.0 rev: 5.12.0
@ -52,10 +51,3 @@ repos:
- id: python-check-blanket-noqa - id: python-check-blanket-noqa
- id: python-use-type-annotations - id: python-use-type-annotations
- id: text-unicode-replacement-char - id: text-unicode-replacement-char
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
hooks:
- id: mypy
additional_dependencies: [ types-PyYAML==6.0.10, types_requests==2.28.8, types-ujson==5.5.0 ]
args: [ --ignore-missing-imports, --warn-no-return, --warn-redundant-casts, --disallow-incomplete-defs ]

View File

@ -1,3 +1,6 @@
[global]
showWarningOnDirectExecution = false
[theme] [theme]
primaryColor = "#0E8388" primaryColor = "#0E8388"
backgroundColor = "#2C3333" backgroundColor = "#2C3333"

BIN
assets/icons/rec_on.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

56
chat.py
View File

@ -10,17 +10,19 @@ from src.utils.conversation import get_user_input, show_chat_buttons, show_conve
import streamlit as st import streamlit as st
# --- PATH SETTINGS --- # --- PATH SETTINGS ---
current_dir = Path(__file__).parent if "__file__" in locals() else Path.cwd() current_dir: Path = Path(__file__).parent if "__file__" in locals() else Path.cwd()
css_file = current_dir / "src/styles/.css" css_file: Path = current_dir / "src/styles/.css"
assets_dir = current_dir / "assets" assets_dir: Path = current_dir / "assets"
icons_dir = assets_dir / "icons" icons_dir: Path = assets_dir / "icons"
img_dir = assets_dir / "img" img_dir: Path = assets_dir / "img"
tg_svg = icons_dir / "tg.svg" tg_svg: Path = icons_dir / "tg.svg"
# --- GENERAL SETTINGS --- # --- GENERAL SETTINGS ---
PAGE_TITLE = "AI Talks" PAGE_TITLE: str = "AI Talks"
PAGE_ICON = "🤖" PAGE_ICON: str = "🤖"
AI_MODEL_OPTIONS = [ LANG_EN: str = "En"
LANG_RU: str = "Ru"
AI_MODEL_OPTIONS: list[str] = [
"gpt-3.5-turbo", "gpt-3.5-turbo",
"gpt-4", "gpt-4",
"gpt-4-32k", "gpt-4-32k",
@ -35,7 +37,7 @@ with open(css_file) as f:
selected_lang = option_menu( selected_lang = option_menu(
menu_title=None, menu_title=None,
options=["En", "Ru", ], options=[LANG_EN, LANG_RU, ],
icons=["globe2", "globe"], icons=["globe2", "globe"],
menu_icon="cast", menu_icon="cast",
default_index=0, default_index=0,
@ -44,36 +46,44 @@ selected_lang = option_menu(
) )
# Storing The Context # Storing The Context
if "locale" not in st.session_state:
st.session_state.locale = en
if "generated" not in st.session_state: if "generated" not in st.session_state:
st.session_state["generated"] = [] st.session_state.generated = []
if "past" not in st.session_state: if "past" not in st.session_state:
st.session_state["past"] = [] st.session_state.past = []
if "messages" not in st.session_state: if "messages" not in st.session_state:
st.session_state["messages"] = [] st.session_state.messages = []
if "user_text" not in st.session_state: if "user_text" not in st.session_state:
st.session_state["user_text"] = "" st.session_state.user_text = ""
if "input_kind" not in st.session_state:
st.session_state.input_kind = st.session_state.locale.input_kind_1
def main() -> None: def main() -> None:
if st.session_state.user_text: c1, c2 = st.columns(2)
show_conversation()
st.session_state.user_text = ""
c1, c2, c3 = st.columns(3)
with c1, c2: with c1, c2:
c1.selectbox(label=st.session_state.locale.select_placeholder1, key="model", options=AI_MODEL_OPTIONS) c1.selectbox(label=st.session_state.locale.select_placeholder1, key="model", options=AI_MODEL_OPTIONS)
role_kind = c2.radio( st.session_state.input_kind = c2.radio(
label=st.session_state.locale.input_kind,
options=(st.session_state.locale.input_kind_1, st.session_state.locale.input_kind_2),
horizontal=True,
)
role_kind = c1.radio(
label=st.session_state.locale.radio_placeholder, label=st.session_state.locale.radio_placeholder,
options=(st.session_state.locale.radio_text1, st.session_state.locale.radio_text2), options=(st.session_state.locale.radio_text1, st.session_state.locale.radio_text2),
horizontal=True, horizontal=True,
) )
match role_kind: match role_kind:
case st.session_state.locale.radio_text1: case st.session_state.locale.radio_text1:
c3.selectbox(label=st.session_state.locale.select_placeholder2, key="role", c2.selectbox(label=st.session_state.locale.select_placeholder2, key="role",
options=st.session_state.locale.ai_role_options) options=st.session_state.locale.ai_role_options)
case st.session_state.locale.radio_text2: case st.session_state.locale.radio_text2:
c3.text_input(label=st.session_state.locale.select_placeholder3, key="role") c2.text_input(label=st.session_state.locale.select_placeholder3, key="role")
if st.session_state.user_text:
show_conversation()
st.session_state.user_text = ""
get_user_input() get_user_input()
show_chat_buttons() show_chat_buttons()
@ -85,7 +95,7 @@ if __name__ == "__main__":
case "Ru": case "Ru":
st.session_state.locale = ru st.session_state.locale = ru
case _: case _:
locale = en st.session_state.locale = en
st.markdown(f"<h1 style='text-align: center;'>{st.session_state.locale.title}</h1>", unsafe_allow_html=True) st.markdown(f"<h1 style='text-align: center;'>{st.session_state.locale.title}</h1>", unsafe_allow_html=True)
st.markdown("---") st.markdown("---")
main() main()

View File

@ -1,8 +1,10 @@
streamlit==1.20.0 streamlit==1.21.0
streamlit-chat==0.0.2.2 streamlit-chat==0.0.2.2
streamlit_option_menu==0.3.2 streamlit_option_menu==0.3.2
openai==0.27.2 openai==0.27.4
gtts==2.3.1 gtts==2.3.1
pip==23.0.1 pip==23.0.1
bokeh==2.4.2
streamlit-bokeh-events==0.1.2
watchdog==3.0.0 watchdog==3.0.0

View File

@ -12,7 +12,7 @@ import streamlit as st
US_AG = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36" US_AG = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"
class Chatbot: class BardChat:
""" """
A class to interact with Google Bard. A class to interact with Google Bard.
Parameters Parameters

View File

@ -3,20 +3,34 @@ from openai.error import InvalidRequestError, OpenAIError
from requests.exceptions import TooManyRedirects from requests.exceptions import TooManyRedirects
from streamlit_chat import message from streamlit_chat import message
from src.utils.agi.bard import Chatbot from src.utils.agi.bard import BardChat
from src.utils.agi.chat_gpt import chat_gpt_request from src.utils.agi.chat_gpt import chat_gpt_request
from src.utils.stt import show_voice_input
from src.utils.tts import show_player from src.utils.tts import show_player
def clear_chat() -> None: def clear_chat() -> None:
st.session_state["generated"] = [] st.session_state.generated = []
st.session_state["past"] = [] st.session_state.past = []
st.session_state["messages"] = [] st.session_state.messages = []
st.session_state["user_text"] = "" st.session_state.user_text = ""
def show_text_input() -> None:
st.text_area(label=st.session_state.locale.chat_placeholder, value=st.session_state.user_text, key="user_text")
def get_user_input(): def get_user_input():
st.text_area(label=st.session_state.locale.chat_placeholder, value=st.session_state.user_text, key="user_text") match st.session_state.input_kind:
case st.session_state.locale.input_kind_1:
clear_chat()
show_text_input()
case st.session_state.locale.input_kind_2:
clear_chat()
show_voice_input()
show_text_input()
case _:
show_text_input()
def show_chat_buttons() -> None: def show_chat_buttons() -> None:
@ -39,7 +53,7 @@ def show_chat(ai_content: str, user_text: str) -> None:
st.session_state.generated.append(ai_content) st.session_state.generated.append(ai_content)
if st.session_state.generated: if st.session_state.generated:
for i in range(len(st.session_state.generated)): for i in range(len(st.session_state.generated)):
message(st.session_state["past"][i], is_user=True, key=str(i) + "_user", avatar_style="micah") message(st.session_state.past[i], is_user=True, key=str(i) + "_user", avatar_style="micah")
message("", key=str(i)) message("", key=str(i))
st.markdown(st.session_state.generated[i]) st.markdown(st.session_state.generated[i])
@ -67,7 +81,7 @@ def chat_gpt_conversation() -> None:
def bard_conversation() -> None: def bard_conversation() -> None:
try: try:
bard = Chatbot(st.secrets.api_credentials.bard_session) bard = BardChat(st.secrets.api_credentials.bard_session)
ai_content = bard.ask(st.session_state.user_text) ai_content = bard.ask(st.session_state.user_text)
st.warning(ai_content.get("content")) st.warning(ai_content.get("content"))
except (TooManyRedirects, AttributeError) as err: except (TooManyRedirects, AttributeError) as err:

View File

@ -17,6 +17,10 @@ class Locale:
chat_run_btn: str chat_run_btn: str
chat_clear_btn: str chat_clear_btn: str
chat_save_btn: str chat_save_btn: str
speak_btn: str
input_kind: str
input_kind_1: str
input_kind_2: str
select_placeholder1: str select_placeholder1: str
select_placeholder2: str select_placeholder2: str
select_placeholder3: str select_placeholder3: str
@ -44,9 +48,13 @@ class EnLocale(Locale):
donates1: str = "Russia" donates1: str = "Russia"
donates2: str = "World" donates2: str = "World"
chat_placeholder: str = "Start Your Conversation With AI:" chat_placeholder: str = "Start Your Conversation With AI:"
chat_run_btn: str = "Run" chat_run_btn: str = "Ask"
chat_clear_btn: str = "Clear" chat_clear_btn: str = "Clear"
chat_save_btn: str = "Save" chat_save_btn: str = "Save"
speak_btn: str = "Push to Speak"
input_kind: str = "Input Kind"
input_kind_1: str = "Text"
input_kind_2: str = "Voice [test mode]"
select_placeholder1: str = "Select Model" select_placeholder1: str = "Select Model"
select_placeholder2: str = "Select Role" select_placeholder2: str = "Select Role"
select_placeholder3: str = "Create Role" select_placeholder3: str = "Create Role"
@ -83,9 +91,13 @@ class RuLocale(Locale):
donates1: str = "Россия" donates1: str = "Россия"
donates2: str = "Остальной Мир" donates2: str = "Остальной Мир"
chat_placeholder: str = "Начните Вашу Беседу с ИИ:" chat_placeholder: str = "Начните Вашу Беседу с ИИ:"
chat_run_btn: str = "Запустить" chat_run_btn: str = "Спросить"
chat_clear_btn: str = "Очистить" chat_clear_btn: str = "Очистить"
chat_save_btn: str = "Сохранить" chat_save_btn: str = "Сохранить"
speak_btn: str = "Нажмите и Говорите"
input_kind: str = "Вид ввода"
input_kind_1: str = "Текст"
input_kind_2: str = "Голос [тестовый режим]"
select_placeholder1: str = "Выберите Модель" select_placeholder1: str = "Выберите Модель"
select_placeholder2: str = "Выберите Роль" select_placeholder2: str = "Выберите Роль"
select_placeholder3: str = "Создайте Роль" select_placeholder3: str = "Создайте Роль"

View File

@ -0,0 +1,83 @@
import streamlit as st
from bokeh.models import CustomJS
from bokeh.models.widgets import Button
from streamlit_bokeh_events import streamlit_bokeh_events
REC_GIF = "assets/icons/rec_on.gif"
def get_js_code(lang: str) -> str:
return """
var value = "";
var rand = 0;
var recognition = new webkitSpeechRecognition();
recognition.continuous = false;
recognition.interimResults = true;
""" + f"recognition.lang = '{lang}';" + """
document.dispatchEvent(new CustomEvent("GET_ONREC", {detail: 'start'}));
recognition.onspeechstart = function () {
document.dispatchEvent(new CustomEvent("GET_ONREC", {detail: 'running'}));
}
recognition.onsoundend = function () {
document.dispatchEvent(new CustomEvent("GET_ONREC", {detail: 'stop'}));
}
recognition.onresult = function (e) {
var value2 = "";
for (var i = e.resultIndex; i < e.results.length; ++i) {
if (e.results[i].isFinal) {
value += e.results[i][0].transcript;
rand = Math.random();
} else {
value2 += e.results[i][0].transcript;
}
}
document.dispatchEvent(new CustomEvent("GET_TEXT", {detail: {t:value, s:rand}}));
document.dispatchEvent(new CustomEvent("GET_INTRM", {detail: value2}));
}
recognition.onerror = function(e) {
document.dispatchEvent(new CustomEvent("GET_ONREC", {detail: 'stop'}));
}
recognition.start();
"""
def show_speak_btn() -> Button:
stt_button = Button(label=st.session_state.locale.speak_btn, button_type="success", width=100)
stt_button.js_on_event("button_click", CustomJS(code=get_js_code(st.session_state.locale.lang_code)))
return stt_button
def get_bokeh_result() -> dict:
stt_button = show_speak_btn()
return streamlit_bokeh_events(
bokeh_plot=stt_button,
events="GET_TEXT,GET_ONREC,GET_INTRM",
key="listen",
refresh_on_update=False,
override_height=75,
debounce_time=0,
)
def show_voice_input() -> None:
if "input" not in st.session_state:
st.session_state.input = {"text": "", "session": 0}
result = get_bokeh_result()
if result:
if "GET_TEXT" in result:
if result.get("GET_TEXT")["t"] != "" and result.get("GET_TEXT")["s"] != st.session_state.input["session"]:
st.session_state.input["text"] = result.get("GET_TEXT")["t"]
st.session_state.input["session"] = result.get("GET_TEXT")["s"]
if "GET_ONREC" in result:
placeholder = st.container()
if result.get("GET_ONREC") == "start":
placeholder.image(REC_GIF)
st.session_state.input["text"] = ""
elif result.get("GET_ONREC") == "running":
placeholder.image(REC_GIF)
elif result.get("GET_ONREC") == "stop":
if st.session_state.input["text"] != "":
st.session_state.user_text = st.session_state.input["text"]