Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -755,6 +755,13 @@ HTML_CONTENT = """<!DOCTYPE html>
|
|
755 |
const message = textInput.value.trim();
|
756 |
if (!message) return;
|
757 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
758 |
// Add user message to chat
|
759 |
addMessage('user', message);
|
760 |
textInput.value = '';
|
@@ -1246,13 +1253,18 @@ class PersonalAssistantDB:
|
|
1246 |
@staticmethod
|
1247 |
async def save_message(session_id: str, role: str, content: str):
|
1248 |
"""Save a message to the database"""
|
|
|
|
|
|
|
|
|
|
|
1249 |
# Detect language
|
1250 |
detected_language = None
|
1251 |
try:
|
1252 |
if content and len(content) > 10:
|
1253 |
detected_language = detect(content)
|
1254 |
-
except LangDetectException:
|
1255 |
-
|
1256 |
|
1257 |
async with aiosqlite.connect(DB_PATH) as db:
|
1258 |
await db.execute(
|
@@ -1390,7 +1402,7 @@ class PersonalAssistantDB:
|
|
1390 |
# Prepare conversation text for analysis
|
1391 |
conversation_text = "\n".join([
|
1392 |
f"{msg['role']}: {msg['content']}"
|
1393 |
-
for msg in messages
|
1394 |
])
|
1395 |
|
1396 |
# Use GPT to extract memories
|
@@ -1484,10 +1496,11 @@ def format_memories_for_prompt(memories: Dict[str, List[str]]) -> str:
|
|
1484 |
|
1485 |
memory_text = "\n\n=== 기억된 정보 ===\n"
|
1486 |
for category, items in memories.items():
|
1487 |
-
if items:
|
1488 |
memory_text += f"\n[{category}]\n"
|
1489 |
for item in items:
|
1490 |
-
|
|
|
1491 |
|
1492 |
return memory_text
|
1493 |
|
@@ -1496,6 +1509,10 @@ async def process_text_chat(message: str, web_search_enabled: bool, session_id:
|
|
1496 |
user_name: str = "", memories: Dict = None) -> Dict[str, str]:
|
1497 |
"""Process text chat using GPT-4o-mini model"""
|
1498 |
try:
|
|
|
|
|
|
|
|
|
1499 |
# Check for stop words
|
1500 |
stop_words = ["중단", "그만", "스톱", "stop", "닥쳐", "멈춰", "중지"]
|
1501 |
if any(word in message.lower() for word in stop_words):
|
@@ -1519,7 +1536,7 @@ IMPORTANT: Give only ONE response. Do not repeat or give multiple answers."""
|
|
1519 |
messages = [{"role": "system", "content": base_prompt}]
|
1520 |
|
1521 |
# Handle web search if enabled
|
1522 |
-
if web_search_enabled and search_client:
|
1523 |
search_keywords = ["날씨", "기온", "비", "눈", "뉴스", "소식", "현재", "최근",
|
1524 |
"오늘", "지금", "가격", "환율", "주가", "weather", "news",
|
1525 |
"current", "today", "price", "2024", "2025"]
|
@@ -1729,24 +1746,53 @@ IMPORTANT: Give only ONE response per user input. Do not repeat yourself or give
|
|
1729 |
"voice": "alloy"
|
1730 |
}
|
1731 |
|
1732 |
-
|
1733 |
-
|
1734 |
-
|
|
|
|
|
|
|
|
|
|
|
1735 |
|
1736 |
async for event in self.connection:
|
1737 |
-
#
|
1738 |
-
if event
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1739 |
if hasattr(event, 'item') and hasattr(event.item, 'role') and event.item.role == "user":
|
1740 |
if hasattr(event.item, 'content') and event.item.content:
|
1741 |
for content_item in event.item.content:
|
1742 |
-
if hasattr(content_item, 'transcript'):
|
1743 |
user_text = content_item.transcript.lower()
|
1744 |
stop_words = ["중단", "그만", "스톱", "stop", "닥쳐", "멈춰", "중지"]
|
1745 |
|
1746 |
if any(word in user_text for word in stop_words):
|
1747 |
print(f"[STOP DETECTED] User said: {content_item.transcript}")
|
1748 |
self.should_stop = True
|
1749 |
-
# Cancel any ongoing response
|
1750 |
if self.connection:
|
1751 |
try:
|
1752 |
await self.connection.response.cancel()
|
@@ -1758,25 +1804,25 @@ IMPORTANT: Give only ONE response per user input. Do not repeat yourself or give
|
|
1758 |
if self.session_id:
|
1759 |
await PersonalAssistantDB.save_message(self.session_id, "user", content_item.transcript)
|
1760 |
|
1761 |
-
|
1762 |
# Prevent multiple responses
|
1763 |
if self.is_responding:
|
1764 |
print("[DUPLICATE RESPONSE] Skipping duplicate response")
|
1765 |
continue
|
1766 |
|
1767 |
self.is_responding = True
|
1768 |
-
print(f"[RESPONSE] Transcript: {event.transcript[:100]}...")
|
1769 |
|
1770 |
# Detect language
|
1771 |
detected_language = None
|
1772 |
try:
|
1773 |
if event.transcript and len(event.transcript) > 10:
|
1774 |
detected_language = detect(event.transcript)
|
1775 |
-
except:
|
1776 |
-
|
1777 |
|
1778 |
# Save to database
|
1779 |
-
if self.session_id:
|
1780 |
await PersonalAssistantDB.save_message(self.session_id, "assistant", event.transcript)
|
1781 |
|
1782 |
output_data = {
|
@@ -1796,14 +1842,20 @@ IMPORTANT: Give only ONE response per user input. Do not repeat yourself or give
|
|
1796 |
if self.should_stop:
|
1797 |
continue
|
1798 |
|
1799 |
-
|
1800 |
-
(
|
1801 |
-
|
1802 |
-
|
1803 |
-
|
1804 |
-
|
1805 |
-
|
1806 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1807 |
|
1808 |
# Handle function calls
|
1809 |
elif event.type == "response.function_call_arguments.start":
|
@@ -1856,7 +1908,15 @@ IMPORTANT: Give only ONE response per user input. Do not repeat yourself or give
|
|
1856 |
print(f"[RECEIVE] No connection, skipping")
|
1857 |
return
|
1858 |
try:
|
|
|
|
|
|
|
|
|
1859 |
_, array = frame
|
|
|
|
|
|
|
|
|
1860 |
array = array.squeeze()
|
1861 |
audio_message = base64.b64encode(array.tobytes()).decode("utf-8")
|
1862 |
await self.connection.input_audio_buffer.append(audio=audio_message)
|
@@ -1908,20 +1968,26 @@ stream.mount(app)
|
|
1908 |
# Initialize database on startup
|
1909 |
@app.on_event("startup")
|
1910 |
async def startup_event():
|
1911 |
-
|
1912 |
-
|
1913 |
-
|
1914 |
-
|
1915 |
-
|
1916 |
-
|
1917 |
-
|
1918 |
-
|
1919 |
-
|
1920 |
-
|
1921 |
-
|
1922 |
-
|
1923 |
-
|
1924 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1925 |
|
1926 |
# Intercept offer to capture settings
|
1927 |
@app.post("/webrtc/offer", include_in_schema=False)
|
|
|
755 |
const message = textInput.value.trim();
|
756 |
if (!message) return;
|
757 |
|
758 |
+
// Check for stop words
|
759 |
+
const stopWords = ["중단", "그만", "스톱", "stop", "닥쳐", "멈춰", "중지"];
|
760 |
+
if (stopWords.some(word => message.toLowerCase().includes(word))) {
|
761 |
+
addMessage('assistant', '대화를 중단합니다.');
|
762 |
+
return;
|
763 |
+
}
|
764 |
+
|
765 |
// Add user message to chat
|
766 |
addMessage('user', message);
|
767 |
textInput.value = '';
|
|
|
1253 |
@staticmethod
|
1254 |
async def save_message(session_id: str, role: str, content: str):
|
1255 |
"""Save a message to the database"""
|
1256 |
+
# Check for None or empty content
|
1257 |
+
if not content:
|
1258 |
+
print(f"[SAVE_MESSAGE] Empty content for {role} message, skipping")
|
1259 |
+
return
|
1260 |
+
|
1261 |
# Detect language
|
1262 |
detected_language = None
|
1263 |
try:
|
1264 |
if content and len(content) > 10:
|
1265 |
detected_language = detect(content)
|
1266 |
+
except (LangDetectException, Exception) as e:
|
1267 |
+
print(f"Language detection error: {e}")
|
1268 |
|
1269 |
async with aiosqlite.connect(DB_PATH) as db:
|
1270 |
await db.execute(
|
|
|
1402 |
# Prepare conversation text for analysis
|
1403 |
conversation_text = "\n".join([
|
1404 |
f"{msg['role']}: {msg['content']}"
|
1405 |
+
for msg in messages if msg.get('content')
|
1406 |
])
|
1407 |
|
1408 |
# Use GPT to extract memories
|
|
|
1496 |
|
1497 |
memory_text = "\n\n=== 기억된 정보 ===\n"
|
1498 |
for category, items in memories.items():
|
1499 |
+
if items and isinstance(items, list):
|
1500 |
memory_text += f"\n[{category}]\n"
|
1501 |
for item in items:
|
1502 |
+
if item: # Check if item is not None or empty
|
1503 |
+
memory_text += f"- {item}\n"
|
1504 |
|
1505 |
return memory_text
|
1506 |
|
|
|
1509 |
user_name: str = "", memories: Dict = None) -> Dict[str, str]:
|
1510 |
"""Process text chat using GPT-4o-mini model"""
|
1511 |
try:
|
1512 |
+
# Check for empty or None message
|
1513 |
+
if not message:
|
1514 |
+
return {"error": "메시지가 비어있습니다."}
|
1515 |
+
|
1516 |
# Check for stop words
|
1517 |
stop_words = ["중단", "그만", "스톱", "stop", "닥쳐", "멈춰", "중지"]
|
1518 |
if any(word in message.lower() for word in stop_words):
|
|
|
1536 |
messages = [{"role": "system", "content": base_prompt}]
|
1537 |
|
1538 |
# Handle web search if enabled
|
1539 |
+
if web_search_enabled and search_client and message:
|
1540 |
search_keywords = ["날씨", "기온", "비", "눈", "뉴스", "소식", "현재", "최근",
|
1541 |
"오늘", "지금", "가격", "환율", "주가", "weather", "news",
|
1542 |
"current", "today", "price", "2024", "2025"]
|
|
|
1746 |
"voice": "alloy"
|
1747 |
}
|
1748 |
|
1749 |
+
try:
|
1750 |
+
await conn.session.update(session=session_update)
|
1751 |
+
self.connection = conn
|
1752 |
+
print(f"Connected with tools: {len(tools)} functions")
|
1753 |
+
print(f"Session update successful")
|
1754 |
+
except Exception as e:
|
1755 |
+
print(f"Error updating session: {e}")
|
1756 |
+
raise
|
1757 |
|
1758 |
async for event in self.connection:
|
1759 |
+
# Debug log for all events
|
1760 |
+
if hasattr(event, 'type'):
|
1761 |
+
if event.type not in ["response.audio.delta", "response.audio.done"]:
|
1762 |
+
print(f"[EVENT] Type: {event.type}")
|
1763 |
+
|
1764 |
+
# Handle user input audio transcription
|
1765 |
+
if event.type == "conversation.item.input_audio_transcription.completed":
|
1766 |
+
if hasattr(event, 'transcript') and event.transcript:
|
1767 |
+
user_text = event.transcript.lower()
|
1768 |
+
stop_words = ["중단", "그만", "스톱", "stop", "닥쳐", "멈춰", "중지"]
|
1769 |
+
|
1770 |
+
if any(word in user_text for word in stop_words):
|
1771 |
+
print(f"[STOP DETECTED] User said: {event.transcript}")
|
1772 |
+
self.should_stop = True
|
1773 |
+
if self.connection:
|
1774 |
+
try:
|
1775 |
+
await self.connection.response.cancel()
|
1776 |
+
except:
|
1777 |
+
pass
|
1778 |
+
continue
|
1779 |
+
|
1780 |
+
# Save user message to database
|
1781 |
+
if self.session_id:
|
1782 |
+
await PersonalAssistantDB.save_message(self.session_id, "user", event.transcript)
|
1783 |
+
|
1784 |
+
# Handle user transcription for stop detection (alternative event)
|
1785 |
+
elif event.type == "conversation.item.created":
|
1786 |
if hasattr(event, 'item') and hasattr(event.item, 'role') and event.item.role == "user":
|
1787 |
if hasattr(event.item, 'content') and event.item.content:
|
1788 |
for content_item in event.item.content:
|
1789 |
+
if hasattr(content_item, 'transcript') and content_item.transcript:
|
1790 |
user_text = content_item.transcript.lower()
|
1791 |
stop_words = ["중단", "그만", "스톱", "stop", "닥쳐", "멈춰", "중지"]
|
1792 |
|
1793 |
if any(word in user_text for word in stop_words):
|
1794 |
print(f"[STOP DETECTED] User said: {content_item.transcript}")
|
1795 |
self.should_stop = True
|
|
|
1796 |
if self.connection:
|
1797 |
try:
|
1798 |
await self.connection.response.cancel()
|
|
|
1804 |
if self.session_id:
|
1805 |
await PersonalAssistantDB.save_message(self.session_id, "user", content_item.transcript)
|
1806 |
|
1807 |
+
elif event.type == "response.audio_transcript.done":
|
1808 |
# Prevent multiple responses
|
1809 |
if self.is_responding:
|
1810 |
print("[DUPLICATE RESPONSE] Skipping duplicate response")
|
1811 |
continue
|
1812 |
|
1813 |
self.is_responding = True
|
1814 |
+
print(f"[RESPONSE] Transcript: {event.transcript[:100] if event.transcript else 'None'}...")
|
1815 |
|
1816 |
# Detect language
|
1817 |
detected_language = None
|
1818 |
try:
|
1819 |
if event.transcript and len(event.transcript) > 10:
|
1820 |
detected_language = detect(event.transcript)
|
1821 |
+
except Exception as e:
|
1822 |
+
print(f"Language detection error: {e}")
|
1823 |
|
1824 |
# Save to database
|
1825 |
+
if self.session_id and event.transcript:
|
1826 |
await PersonalAssistantDB.save_message(self.session_id, "assistant", event.transcript)
|
1827 |
|
1828 |
output_data = {
|
|
|
1842 |
if self.should_stop:
|
1843 |
continue
|
1844 |
|
1845 |
+
if hasattr(event, 'delta'):
|
1846 |
+
await self.output_queue.put(
|
1847 |
+
(
|
1848 |
+
self.output_sample_rate,
|
1849 |
+
np.frombuffer(
|
1850 |
+
base64.b64decode(event.delta), dtype=np.int16
|
1851 |
+
).reshape(1, -1),
|
1852 |
+
),
|
1853 |
+
)
|
1854 |
+
|
1855 |
+
# Handle errors
|
1856 |
+
elif event.type == "error":
|
1857 |
+
print(f"[ERROR] {event}")
|
1858 |
+
self.is_responding = False
|
1859 |
|
1860 |
# Handle function calls
|
1861 |
elif event.type == "response.function_call_arguments.start":
|
|
|
1908 |
print(f"[RECEIVE] No connection, skipping")
|
1909 |
return
|
1910 |
try:
|
1911 |
+
if frame is None or len(frame) < 2:
|
1912 |
+
print(f"[RECEIVE] Invalid frame")
|
1913 |
+
return
|
1914 |
+
|
1915 |
_, array = frame
|
1916 |
+
if array is None:
|
1917 |
+
print(f"[RECEIVE] Null array")
|
1918 |
+
return
|
1919 |
+
|
1920 |
array = array.squeeze()
|
1921 |
audio_message = base64.b64encode(array.tobytes()).decode("utf-8")
|
1922 |
await self.connection.input_audio_buffer.append(audio=audio_message)
|
|
|
1968 |
# Initialize database on startup
|
1969 |
@app.on_event("startup")
|
1970 |
async def startup_event():
|
1971 |
+
try:
|
1972 |
+
await PersonalAssistantDB.init()
|
1973 |
+
print(f"Database initialized at: {DB_PATH}")
|
1974 |
+
print(f"Persistent directory: {PERSISTENT_DIR}")
|
1975 |
+
print(f"DB file exists: {os.path.exists(DB_PATH)}")
|
1976 |
+
|
1977 |
+
# Check if we're in Hugging Face Space
|
1978 |
+
if os.path.exists("/data"):
|
1979 |
+
print("Running in Hugging Face Space with persistent storage")
|
1980 |
+
# List files in persistent directory
|
1981 |
+
try:
|
1982 |
+
files = os.listdir(PERSISTENT_DIR)
|
1983 |
+
print(f"Files in persistent directory: {files}")
|
1984 |
+
except Exception as e:
|
1985 |
+
print(f"Error listing files: {e}")
|
1986 |
+
except Exception as e:
|
1987 |
+
print(f"Error during startup: {e}")
|
1988 |
+
# Try to create directory if it doesn't exist
|
1989 |
+
os.makedirs(PERSISTENT_DIR, exist_ok=True)
|
1990 |
+
await PersonalAssistantDB.init()
|
1991 |
|
1992 |
# Intercept offer to capture settings
|
1993 |
@app.post("/webrtc/offer", include_in_schema=False)
|