Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -546,6 +546,7 @@ HTML_CONTENT = """<!DOCTYPE html>
|
|
546 |
let animationFrame;
|
547 |
let audioContext, analyser, audioSource;
|
548 |
let dataChannel = null;
|
|
|
549 |
|
550 |
// Web search toggle functionality
|
551 |
searchToggle.addEventListener('click', () => {
|
@@ -575,16 +576,57 @@ HTML_CONTENT = """<!DOCTYPE html>
|
|
575 |
|
576 |
sendButton.addEventListener('click', sendTextMessage);
|
577 |
|
578 |
-
function sendTextMessage() {
|
579 |
const message = textInput.value.trim();
|
580 |
-
if (!message
|
581 |
|
|
|
582 |
addMessage('user', message);
|
583 |
-
dataChannel.send(JSON.stringify({
|
584 |
-
type: 'text_message',
|
585 |
-
content: message
|
586 |
-
}));
|
587 |
textInput.value = '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
588 |
}
|
589 |
|
590 |
function updateStatus(state) {
|
@@ -592,12 +634,14 @@ HTML_CONTENT = """<!DOCTYPE html>
|
|
592 |
if (state === 'connected') {
|
593 |
statusText.textContent = '์ฐ๊ฒฐ๋จ';
|
594 |
sendButton.style.display = 'block';
|
|
|
595 |
} else if (state === 'connecting') {
|
596 |
statusText.textContent = '์ฐ๊ฒฐ ์ค...';
|
597 |
sendButton.style.display = 'none';
|
598 |
} else {
|
599 |
statusText.textContent = '์ฐ๊ฒฐ ๋๊ธฐ ์ค';
|
600 |
-
sendButton.style.display = '
|
|
|
601 |
}
|
602 |
}
|
603 |
function updateButtonState() {
|
@@ -885,11 +929,80 @@ print(f"Search client initialized: {search_client is not None}, API key present:
|
|
885 |
# Store connection settings
|
886 |
connection_settings = {}
|
887 |
|
|
|
|
|
|
|
888 |
def update_chatbot(chatbot: list[dict], response: ResponseAudioTranscriptDoneEvent):
|
889 |
chatbot.append({"role": "assistant", "content": response.transcript})
|
890 |
return chatbot
|
891 |
|
892 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
893 |
class OpenAIHandler(AsyncStreamHandler):
|
894 |
def __init__(self, web_search_enabled: bool = False, target_language: str = "",
|
895 |
system_prompt: str = "", webrtc_id: str = None) -> None:
|
@@ -1231,6 +1344,29 @@ async def custom_offer(request: Request):
|
|
1231 |
return response
|
1232 |
|
1233 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1234 |
@app.post("/text_message/{webrtc_id}")
|
1235 |
async def receive_text_message(webrtc_id: str, request: Request):
|
1236 |
"""Receive text message from client"""
|
|
|
546 |
let animationFrame;
|
547 |
let audioContext, analyser, audioSource;
|
548 |
let dataChannel = null;
|
549 |
+
let isVoiceActive = false;
|
550 |
|
551 |
// Web search toggle functionality
|
552 |
searchToggle.addEventListener('click', () => {
|
|
|
576 |
|
577 |
sendButton.addEventListener('click', sendTextMessage);
|
578 |
|
579 |
+
async function sendTextMessage() {
|
580 |
const message = textInput.value.trim();
|
581 |
+
if (!message) return;
|
582 |
|
583 |
+
// Add user message to chat
|
584 |
addMessage('user', message);
|
|
|
|
|
|
|
|
|
585 |
textInput.value = '';
|
586 |
+
|
587 |
+
// Show sending indicator
|
588 |
+
const typingIndicator = document.createElement('div');
|
589 |
+
typingIndicator.classList.add('message', 'assistant');
|
590 |
+
typingIndicator.textContent = '์
๋ ฅ ์ค...';
|
591 |
+
typingIndicator.id = 'typing-indicator';
|
592 |
+
chatMessages.appendChild(typingIndicator);
|
593 |
+
chatMessages.scrollTop = chatMessages.scrollHeight;
|
594 |
+
|
595 |
+
try {
|
596 |
+
// Send to text chat endpoint
|
597 |
+
const response = await fetch('/chat/text', {
|
598 |
+
method: 'POST',
|
599 |
+
headers: { 'Content-Type': 'application/json' },
|
600 |
+
body: JSON.stringify({
|
601 |
+
message: message,
|
602 |
+
web_search_enabled: webSearchEnabled,
|
603 |
+
target_language: selectedLanguage,
|
604 |
+
system_prompt: systemPrompt
|
605 |
+
})
|
606 |
+
});
|
607 |
+
|
608 |
+
const data = await response.json();
|
609 |
+
|
610 |
+
// Remove typing indicator
|
611 |
+
const indicator = document.getElementById('typing-indicator');
|
612 |
+
if (indicator) indicator.remove();
|
613 |
+
|
614 |
+
if (data.error) {
|
615 |
+
showError(data.error);
|
616 |
+
} else {
|
617 |
+
// Add assistant response
|
618 |
+
let content = data.response;
|
619 |
+
if (selectedLanguage && data.language) {
|
620 |
+
content += ` <span class="language-info">[${data.language}]</span>`;
|
621 |
+
}
|
622 |
+
addMessage('assistant', content);
|
623 |
+
}
|
624 |
+
} catch (error) {
|
625 |
+
console.error('Error sending text message:', error);
|
626 |
+
const indicator = document.getElementById('typing-indicator');
|
627 |
+
if (indicator) indicator.remove();
|
628 |
+
showError('๋ฉ์์ง ์ ์ก ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.');
|
629 |
+
}
|
630 |
}
|
631 |
|
632 |
function updateStatus(state) {
|
|
|
634 |
if (state === 'connected') {
|
635 |
statusText.textContent = '์ฐ๊ฒฐ๋จ';
|
636 |
sendButton.style.display = 'block';
|
637 |
+
isVoiceActive = true;
|
638 |
} else if (state === 'connecting') {
|
639 |
statusText.textContent = '์ฐ๊ฒฐ ์ค...';
|
640 |
sendButton.style.display = 'none';
|
641 |
} else {
|
642 |
statusText.textContent = '์ฐ๊ฒฐ ๋๊ธฐ ์ค';
|
643 |
+
sendButton.style.display = 'block'; // Show send button even when disconnected for text chat
|
644 |
+
isVoiceActive = false;
|
645 |
}
|
646 |
}
|
647 |
function updateButtonState() {
|
|
|
929 |
# Store connection settings
|
930 |
connection_settings = {}
|
931 |
|
932 |
+
# Initialize OpenAI client for text chat
|
933 |
+
client = openai.AsyncOpenAI()
|
934 |
+
|
935 |
def update_chatbot(chatbot: list[dict], response: ResponseAudioTranscriptDoneEvent):
|
936 |
chatbot.append({"role": "assistant", "content": response.transcript})
|
937 |
return chatbot
|
938 |
|
939 |
|
940 |
+
def get_translation_instructions(target_language: str) -> str:
|
941 |
+
"""Get instructions for translation based on target language"""
|
942 |
+
if not target_language:
|
943 |
+
return ""
|
944 |
+
|
945 |
+
language_name = SUPPORTED_LANGUAGES.get(target_language, target_language)
|
946 |
+
return (
|
947 |
+
f"\n\nIMPORTANT: You must respond in {language_name} ({target_language}). "
|
948 |
+
f"Translate all your responses to {language_name}."
|
949 |
+
)
|
950 |
+
|
951 |
+
|
952 |
+
async def process_text_chat(message: str, web_search_enabled: bool, target_language: str,
|
953 |
+
system_prompt: str) -> Dict[str, str]:
|
954 |
+
"""Process text chat using GPT-4o-mini model"""
|
955 |
+
try:
|
956 |
+
# Prepare system message
|
957 |
+
base_instructions = system_prompt or "You are a helpful assistant."
|
958 |
+
translation_instructions = get_translation_instructions(target_language)
|
959 |
+
|
960 |
+
messages = [
|
961 |
+
{"role": "system", "content": base_instructions + translation_instructions}
|
962 |
+
]
|
963 |
+
|
964 |
+
# Handle web search if enabled
|
965 |
+
if web_search_enabled and search_client:
|
966 |
+
# Check if the message requires web search
|
967 |
+
search_keywords = ["๋ ์จ", "๊ธฐ์จ", "๋น", "๋", "๋ด์ค", "์์", "ํ์ฌ", "์ต๊ทผ",
|
968 |
+
"์ค๋", "์ง๊ธ", "๊ฐ๊ฒฉ", "ํ์จ", "์ฃผ๊ฐ", "weather", "news",
|
969 |
+
"current", "today", "price", "2024", "2025"]
|
970 |
+
|
971 |
+
should_search = any(keyword in message.lower() for keyword in search_keywords)
|
972 |
+
|
973 |
+
if should_search:
|
974 |
+
# Perform web search
|
975 |
+
search_results = await search_client.search(message)
|
976 |
+
if search_results:
|
977 |
+
search_context = "์น ๊ฒ์ ๊ฒฐ๊ณผ:\n\n"
|
978 |
+
for i, result in enumerate(search_results[:5], 1):
|
979 |
+
search_context += f"{i}. {result['title']}\n{result['description']}\n\n"
|
980 |
+
|
981 |
+
messages.append({
|
982 |
+
"role": "system",
|
983 |
+
"content": f"๋ค์ ์น ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ์ฐธ๊ณ ํ์ฌ ๋ต๋ณํ์ธ์:\n\n{search_context}"
|
984 |
+
})
|
985 |
+
|
986 |
+
messages.append({"role": "user", "content": message})
|
987 |
+
|
988 |
+
# Call GPT-4o-mini
|
989 |
+
response = await client.chat.completions.create(
|
990 |
+
model="gpt-4o-mini",
|
991 |
+
messages=messages,
|
992 |
+
temperature=0.7,
|
993 |
+
max_tokens=2000
|
994 |
+
)
|
995 |
+
|
996 |
+
return {
|
997 |
+
"response": response.choices[0].message.content,
|
998 |
+
"language": SUPPORTED_LANGUAGES.get(target_language, "") if target_language else ""
|
999 |
+
}
|
1000 |
+
|
1001 |
+
except Exception as e:
|
1002 |
+
print(f"Error in text chat: {e}")
|
1003 |
+
return {"error": str(e)}
|
1004 |
+
|
1005 |
+
|
1006 |
class OpenAIHandler(AsyncStreamHandler):
|
1007 |
def __init__(self, web_search_enabled: bool = False, target_language: str = "",
|
1008 |
system_prompt: str = "", webrtc_id: str = None) -> None:
|
|
|
1344 |
return response
|
1345 |
|
1346 |
|
1347 |
+
@app.post("/chat/text")
|
1348 |
+
async def chat_text(request: Request):
|
1349 |
+
"""Handle text chat messages using GPT-4o-mini"""
|
1350 |
+
try:
|
1351 |
+
body = await request.json()
|
1352 |
+
message = body.get("message", "")
|
1353 |
+
web_search_enabled = body.get("web_search_enabled", False)
|
1354 |
+
target_language = body.get("target_language", "")
|
1355 |
+
system_prompt = body.get("system_prompt", "")
|
1356 |
+
|
1357 |
+
if not message:
|
1358 |
+
return {"error": "๋ฉ์์ง๊ฐ ๋น์ด์์ต๋๋ค."}
|
1359 |
+
|
1360 |
+
# Process text chat
|
1361 |
+
result = await process_text_chat(message, web_search_enabled, target_language, system_prompt)
|
1362 |
+
|
1363 |
+
return result
|
1364 |
+
|
1365 |
+
except Exception as e:
|
1366 |
+
print(f"Error in chat_text endpoint: {e}")
|
1367 |
+
return {"error": "์ฑํ
์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค."}
|
1368 |
+
|
1369 |
+
|
1370 |
@app.post("/text_message/{webrtc_id}")
|
1371 |
async def receive_text_message(webrtc_id: str, request: Request):
|
1372 |
"""Receive text message from client"""
|