1
0
mirror of https://github.com/janvarev/Irene-Voice-Assistant.git synced 2025-12-05 23:08:23 +02:00
Files
Irene-Voice-Assistant/runva_webapi.py

242 lines
7.5 KiB
Python

# ----------
from fastapi import FastAPI, HTTPException
import uvicorn
from multiprocessing import Process
from termcolor import cprint
import json
from starlette.websockets import WebSocket
#from pydantic import BaseModel
from vacore import VACore
import time
# ------------------- main loop ------------------
webapi_options = None
core = None
model = None # vosk model
#rec = None # vosk recognizer
try:
with open('options/webapi.json', 'r', encoding="utf-8") as f:
s = f.read(1000000)
f.close()
webapi_options = json.loads(s)
use_ssl = webapi_options["use_ssl"]
except Exception as e:
core = VACore()
core.init_with_plugins()
core.init_plugin("webapi")
cprint("Настройки созданы; пожалуйста, перезапустите этот файл", "red")
exit(-1)
"""
returnFormat Варианты:
- "none" (TTS реакции будут на сервере) (звук на сервере)
- "saytxt" (сервер вернет текст, TTS будет на клиенте) (звук на клиенте)
- "saywav" (TTS на сервере, сервер отрендерит WAV и вернет клиенту, клиент его проиграет) (звук на клиенте) **наиболее универсальный для клиента**
"""
def runCmd(cmd:str,returnFormat:str):
if core.logPolicy == "cmd" or core.logPolicy == "all":
print("Running cmd: ",cmd)
tmpformat = core.remoteTTS
core.remoteTTS = returnFormat
core.remoteTTSResult = ""
core.lastSay = ""
core.execute_next(cmd,core.context)
core.remoteTTS = tmpformat
app = FastAPI()
is_running = True
from starlette.routing import Mount
from starlette.staticfiles import StaticFiles
app.mount("/webapi_client", StaticFiles(directory="webapi_client", html = True), name="webapi_client")
app.mount("/mic_client", StaticFiles(directory="mic_client", html = True), name="mic_client")
@app.websocket("/wsmic")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
from vosk import KaldiRecognizer
rec = KaldiRecognizer(model, 48000)
print("New WebSocket microphone recognition")
while True:
data = await websocket.receive_bytes()
r = process_chunk(rec,data)
await websocket.send_text(r)
def process_chunk(rec,message):
# with open('temp/asr_server_test.wav', 'wb') as the_file:
# the_file.write(message)
if message == '{"eof" : 1}':
return rec.FinalResult()
elif rec.AcceptWaveform(message):
res2 = "{}"
res = rec.Result()
#print("Result:",res)
resj = json.loads(res)
if "text" in resj:
voice_input_str = resj["text"]
#print(restext)
import requests
if voice_input_str != "" and voice_input_str != None:
print(voice_input_str)
#ttsFormatList = ["saytxt"]
res2 = sendRawTxtOrig(voice_input_str,"none,saytxt")
# saywav not supported due to bytes serialization???
if res2 != "NO_VA_NAME":
res2 = json.dumps(res2)
else:
res2 = "{}"
else:
#print("2",rec.PartialResult())
pass
return res2
else:
res = rec.PartialResult()
#print("Part Result:",res)
return rec.PartialResult()
@app.on_event("startup")
async def startup_event():
global core
core = VACore()
core.init_with_plugins()
core.init_plugin("webapi")
print("WEB api for VoiceAssistantCore (remote control)")
url = ""
if webapi_options["use_ssl"]:
url = "https://{0}:{1}/".format("localhost",webapi_options["port"])
else:
url = "http://{0}:{1}/".format("localhost",webapi_options["port"])
print("Web client URL (VOSK in browser): ", url+"webapi_client/")
print("Mic client URL (experimental, sends WAV bytes to server): ", url+"mic_client/")
from vosk import Model, SpkModel, KaldiRecognizer
global model
model = Model("model")
# рендерит текст в wav
@app.get("/ttsWav")
async def ttsWav(text:str):
#runCmd(cmd,returnFormat)
tmpformat = core.remoteTTS
core.remoteTTS = "saywav"
core.play_voice_assistant_speech(text)
core.remoteTTS = tmpformat
return core.remoteTTSResult
# выполняет команду Ирины
# Например: привет, погода.
@app.get("/sendTxtCmd")
async def sendSimpleTxtCmd(cmd:str,returnFormat:str = "none"):
runCmd(cmd,returnFormat)
return core.remoteTTSResult
# Посылает распознанный текстовый ввод. Если в нем есть имя помощника, выполняется команда.
# Пример: ирина погода, раз два
@app.get("/sendRawTxt")
async def sendRawTxt(rawtxt:str,returnFormat:str = "none"):
return sendRawTxtOrig(rawtxt,returnFormat)
def sendRawTxtOrig(rawtxt:str,returnFormat:str = "none"):
tmpformat = core.remoteTTS
core.remoteTTS = returnFormat
core.remoteTTSResult = ""
core.lastSay = ""
isFound = core.run_input_str(rawtxt)
core.remoteTTS = tmpformat
if isFound:
return core.remoteTTSResult
else:
return "NO_VA_NAME"
# Обновляет контекст на то же самое время
@app.get("/reinitContext")
async def reinitContext():
if core.contextTimer != None:
core.context_set(core.context,core.contextTimerLastDuration)
return ""
# Запускает внутреннюю процедуру проверки таймеров. Должна запускаться периодически
@app.get("/updTimers")
async def updTimers():
#core.say("аа")
#print("upd timers")
core._update_timers()
return ""
# Сообщает серверу, что клиент воспроизвёл ответ и можно начать отсчёт таймера контекста
@app.get("/replyWasGiven")
async def replyWasGiven():
if core.contextRemoteWaitForCall:
if core.contextTimer != None:
core.contextTimer.start()
#print("debug - run context after webapi call")
def core_update_timers_http(runReq=True):
time.sleep(5) # small sleep before start
while is_running:
try:
import requests
if webapi_options["use_ssl"]:
reqstr = "https://{0}:{1}/updTimers".format(webapi_options["host"],webapi_options["port"])
else:
reqstr = "http://{0}:{1}/updTimers".format(webapi_options["host"],webapi_options["port"])
#print(reqstr)
r = requests.get(reqstr,verify=False)
except Exception:
pass
try:
time.sleep(2)
except:
return
return
@app.on_event("shutdown")
def app_shutdown():
global is_running
cprint("Ctrl-C pressed, exiting Irene.", "yellow")
is_running = False
if __name__ == "__main__":
p = Process(target=core_update_timers_http, args=(False,))
p.start()
if webapi_options["use_ssl"]:
uvicorn.run("runva_webapi:app",
host=webapi_options["host"], port=webapi_options["port"],
ssl_keyfile="localhost.key",
ssl_certfile="localhost.crt",
log_level=webapi_options["log_level"])
else:
uvicorn.run("runva_webapi:app",
host=webapi_options["host"], port=webapi_options["port"],
log_level=webapi_options["log_level"])