95 lines
2.9 KiB
Python
95 lines
2.9 KiB
Python
import os, time, logging, requests
|
|
import speech_recognition as sr
|
|
from ctypes import *
|
|
|
|
# ALSA 에러 억제
|
|
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
|
|
def py_error_handler(filename, line, function, err, fmt): pass
|
|
ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
|
|
c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
|
|
try:
|
|
asound = cdll.LoadLibrary('libasound.so.2')
|
|
asound.snd_lib_error_set_handler(c_error_handler)
|
|
except: pass
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# ===== 설정 =====
|
|
OPENWEBUI_URL = "http://192.168.0.149:18789"
|
|
API_KEY = "your-openwebui-api-key" # OpenWebUI → 설정 → API Key
|
|
MODEL = "gemini-2.0-flash" # 오픈클로에서 쓰는 모델명
|
|
WAKE_WORD = "미나이"
|
|
MIC_INDEX = 3
|
|
# ================
|
|
|
|
def stt_google(audio_data):
|
|
"""Google STT로 음성 → 텍스트"""
|
|
recognizer = sr.Recognizer()
|
|
try:
|
|
return recognizer.recognize_google(audio_data, language='ko-KR')
|
|
except:
|
|
return None
|
|
|
|
def send_to_openwebui(text):
|
|
"""텍스트를 OpenWebUI 채팅 API로 전송"""
|
|
try:
|
|
headers = {
|
|
"Authorization": f"Bearer {API_KEY}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
payload = {
|
|
"model": MODEL,
|
|
"messages": [{"role": "user", "content": text}]
|
|
}
|
|
# OpenAI 호환 엔드포인트 사용
|
|
res = requests.post(
|
|
f"{OPENWEBUI_URL}/api/chat/completions",
|
|
headers=headers,
|
|
json=payload,
|
|
timeout=30
|
|
)
|
|
response_text = res.json()["choices"][0]["message"]["content"]
|
|
logger.info(f"AI 응답: {response_text}")
|
|
return response_text
|
|
except Exception as e:
|
|
logger.error(f"전송 실패: {e}")
|
|
return None
|
|
|
|
def main():
|
|
recognizer = sr.Recognizer()
|
|
recognizer.energy_threshold = 400
|
|
recognizer.pause_threshold = 1.0
|
|
mic = sr.Microphone(device_index=MIC_INDEX)
|
|
|
|
logger.info(f"==== '{WAKE_WORD}' 대기 중 ====")
|
|
|
|
while True:
|
|
try:
|
|
with mic as source:
|
|
audio = recognizer.listen(source, timeout=None, phrase_time_limit=3)
|
|
|
|
text = stt_google(audio)
|
|
if not text:
|
|
continue
|
|
|
|
if WAKE_WORD in text:
|
|
logger.info(f"웨이크워드 감지: {text}")
|
|
logger.info("명령 녹음 중...")
|
|
|
|
with mic as source:
|
|
cmd_audio = recognizer.listen(source, timeout=5, phrase_time_limit=10)
|
|
|
|
cmd_text = stt_google(cmd_audio)
|
|
if cmd_text:
|
|
logger.info(f"명령: {cmd_text}")
|
|
send_to_openwebui(cmd_text)
|
|
|
|
except Exception as e:
|
|
logger.error(f"루프 오류: {e}")
|
|
time.sleep(1)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|