Chris4K commited on
Commit
a37520a
·
verified ·
1 Parent(s): 2bbcd9b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -136
app.py CHANGED
@@ -16,156 +16,136 @@ try:
16
  except Exception as e:
17
  print("Langfuse Offline")
18
 
19
-
20
-
21
- ########
22
-
23
- html = """
24
- <!DOCTYPE html>
25
- <html lang="en">
26
- <head>
27
- <meta charset="UTF-8">
28
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
29
- <title>AI State Machine</title>
30
- <style>
31
- body { font-family: Arial, sans-serif; text-align: center; }
32
- #chatbox { width: 80%; height: 300px; border: 1px solid #ccc; overflow-y: auto; margin: 20px auto; padding: 10px; }
33
- #inputbox { width: 70%; padding: 5px; }
34
- button { padding: 5px 10px; }
35
- </style>
36
- </head>
37
- <body>
38
- <h2>AI State Machine</h2>
39
- <div id="chatbox"></div>
40
- <input type="text" id="inputbox" placeholder="Type your message...">
41
- <button onclick="sendMessage()">Send</button>
42
-
43
- <script>
44
- let ws = new WebSocket("wss://chris4k-a-i-statemachine.hf.space/ws");
45
-
46
- ws.onopen = function() {
47
- console.log("WebSocket connected!");
48
- ws.send("Hello AI!"); // Send initial message
49
- };
50
-
51
- ws.onmessage = (event) => {
52
- console.log("Message from server: ", event.data);
53
- let chatbox = document.getElementById("chatbox");
54
- chatbox.innerHTML += `<p>${event.data}</p>`;
55
- chatbox.scrollTop = chatbox.scrollHeight;
56
- };
57
-
58
- ws.onerror = function(error) {
59
- console.error("WebSocket Error: ", error);
60
- };
61
-
62
- function sendMessage() {
63
- let input = document.getElementById("inputbox");
64
- let message = input.value.trim();
65
- if (message) {
66
- ws.send(message);
67
- input.value = "";
68
- }
69
- }
70
-
71
-
72
- </script>
73
- </body>
74
- </html>
75
- """
76
- ######
77
-
78
- import asyncio
79
- import random
80
- import time
81
  from fastapi import FastAPI, WebSocket
82
- from fastapi.responses import HTMLResponse
83
- import uvicorn
84
-
85
- class AIStateManager:
86
- def __init__(self):
87
- self.state = "awake"
88
- self.research_tasks = ["Explore AI Ethics", "Find latest AI models", "Investigate quantum computing"]
89
- self.current_task = None
90
- self.heartbeat_count = 0
91
- self.clients = set()
92
-
93
- async def set_state(self, new_state):
94
- """Thread-safe state change with logging."""
95
- print(f"[STATE CHANGE] {self.state} → {new_state}")
96
- self.state = new_state
97
- await self.broadcast(f"State changed to {new_state}")
98
-
99
- async def broadcast(self, message):
100
- """Broadcast message to all connected clients."""
101
- for client in list(self.clients):
102
- try:
103
- await client.send_text(message)
104
- except Exception as e:
105
- print(f"Broadcast error: {e}")
106
- self.clients.remove(client)
107
 
108
- async def research_cycle(self):
109
- """Manages research state tasks."""
110
- while True:
111
- await asyncio.sleep(5) # Longer interval for visibility
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- # State-based logic
114
- if self.state == "awake":
115
- self.heartbeat_count += 1
116
- if self.heartbeat_count >= 3:
117
- await self.set_state("research")
118
 
119
- elif self.state == "research":
120
- if self.research_tasks:
121
- task = self.research_tasks.pop(0)
122
- await self.broadcast(f"Researching: {task}")
 
 
 
 
 
123
 
124
- # Simulate task work
125
- await asyncio.sleep(3)
 
 
 
 
 
126
 
127
- # Random chance of generating follow-up
128
- if random.random() < 0.3:
129
- await self.broadcast(f"Question about {task}")
130
- else:
131
- await self.set_state("sleeping")
132
-
133
- elif self.state == "sleeping":
134
- await self.broadcast("System in sleep mode")
135
- await asyncio.sleep(10) # Longer sleep
136
-
137
- # Reset after sleep
138
- self.research_tasks = ["Explore AI Ethics", "Find latest AI models", "Investigate quantum computing"]
139
- await self.set_state("awake")
140
- self.heartbeat_count = 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
- # FastAPI Setup
143
- app = FastAPI()
144
- ai_manager = AIStateManager()
 
145
 
146
  @app.websocket("/ws")
147
  async def websocket_endpoint(websocket: WebSocket):
148
  await websocket.accept()
149
- ai_manager.clients.add(websocket)
150
  try:
151
- while True:
152
- data = await websocket.receive_text()
153
- await ai_manager.broadcast(f"Received: {data}")
154
  except Exception as e:
155
  print(f"WebSocket error: {e}")
156
  finally:
157
- ai_manager.clients.remove(websocket)
158
-
159
- @app.on_event("startup")
160
- async def startup_event():
161
- """Start research cycle on app startup."""
162
- asyncio.create_task(ai_manager.research_cycle())
163
-
164
-
165
- @app.get("/")
166
- async def get():
167
- """Serve frontend HTML."""
168
- return HTMLResponse(html)
169
 
170
  if __name__ == "__main__":
171
- uvicorn.run(app, host="localhost", port=8000)
 
 
16
  except Exception as e:
17
  print("Langfuse Offline")
18
 
19
+ # main.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  from fastapi import FastAPI, WebSocket
21
+ from fastapi.staticfiles import StaticFiles
22
+ from fastapi.responses import StreamingResponse, HTMLResponse
23
+ import asyncio
24
+ import json
25
+ import webrtcvad
26
+ import numpy as np
27
+ import wave
28
+ import io
29
+ from typing import AsyncGenerator
30
+
31
+ from utils import (
32
+ from_en_translation,
33
+ to_en_translation,
34
+ tts,
35
+ tts_to_bytesio,
36
+ )
37
+
38
+ from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel, VisitWebpageTool
 
 
 
 
 
 
 
39
 
40
+ app = FastAPI()
41
+ app.mount("/static", StaticFiles(directory="static"), name="static")
42
+
43
+ # Initialize tools and agent
44
+ model = HfApiModel()
45
+ search_tool = DuckDuckGoSearchTool()
46
+ visit_webpage_tool = VisitWebpageTool()
47
+ agent = CodeAgent(
48
+ tools=[search_tool, visit_webpage_tool],
49
+ model=model,
50
+ additional_authorized_imports=['requests', 'bs4', 'pandas', 'concurrent.futures', 'csv', 'json']
51
+ )
52
+
53
+ # Constants
54
+ SAMPLE_RATE = 16000
55
+ CHANNELS = 1
56
+ CHUNK_SIZE = 480 # 30ms chunks for VAD
57
+ VAD_MODE = 3 # Aggressiveness mode (3 is most aggressive)
58
+ desired_language = "de"
59
+ max_answer_length = 100
60
+ #response_generator_pipe = TextGenerationPipeline(max_length=max_answer_length)
61
+
62
+ # Initialize VAD
63
+ vad = webrtcvad.Vad(VAD_MODE)
64
+
65
+ async def detect_wakeword(audio_chunk: bytes) -> bool:
66
+ # TODO: Implement proper wake word detection
67
+ # For now, this is a placeholder that should be replaced with a proper wake word detection model
68
+ # You might want to use libraries like Porcupine or build your own wake word detector
69
+ return True
70
+
71
+ async def process_audio_stream(websocket: WebSocket) -> AsyncGenerator[str, None]:
72
+ buffer = []
73
+ is_speaking = False
74
+ silence_frames = 0
75
+
76
+ while True:
77
+ try:
78
+ audio_data = await websocket.receive_bytes()
79
 
80
+ # Convert audio data to the right format for VAD
81
+ is_speech = vad.is_speech(audio_data, SAMPLE_RATE)
 
 
 
82
 
83
+ if is_speech:
84
+ silence_frames = 0
85
+ buffer.append(audio_data)
86
+ is_speaking = True
87
+ elif is_speaking:
88
+ silence_frames += 1
89
+ if silence_frames > 30: # End of utterance detection
90
+ # Process complete utterance
91
+ audio_bytes = b''.join(buffer)
92
 
93
+ # Convert to wave file for speech recognition
94
+ wav_buffer = io.BytesIO()
95
+ with wave.open(wav_buffer, 'wb') as wav_file:
96
+ wav_file.setnchannels(CHANNELS)
97
+ wav_file.setsampwidth(2) # 16-bit audio
98
+ wav_file.setframerate(SAMPLE_RATE)
99
+ wav_file.writeframes(audio_bytes)
100
 
101
+ # Reset state
102
+ buffer = []
103
+ is_speaking = False
104
+ silence_frames = 0
105
+
106
+ # Check for wake word
107
+ if await detect_wakeword(audio_bytes):
108
+ # Process the audio and get response
109
+ user_speech_text = stt(wav_buffer, desired_language)
110
+ if "computer" in user_speech_text.lower():
111
+ translated_text = to_en_translation(user_speech_text, desired_language)
112
+ response = await agent.arun(translated_text) # Assuming agent.run is made async
113
+ bot_response_de = from_en_translation(response, desired_language)
114
+
115
+ # Stream the response
116
+ yield json.dumps({
117
+ "user_text": user_speech_text,
118
+ "response_de": bot_response_de,
119
+ "response_en": response
120
+ })
121
+
122
+ # Generate and stream audio response
123
+ bot_voice = tts(bot_response_de, desired_language)
124
+ bot_voice_bytes = tts_to_bytesio(bot_voice)
125
+ yield json.dumps({
126
+ "audio": bot_voice_bytes.decode('latin1')
127
+ })
128
+
129
+ except Exception as e:
130
+ print(f"Error processing audio: {e}")
131
+ break
132
 
133
+ @app.get("/", response_class=HTMLResponse)
134
+ async def get_index():
135
+ with open("static/index.html") as f:
136
+ return f.read()
137
 
138
  @app.websocket("/ws")
139
  async def websocket_endpoint(websocket: WebSocket):
140
  await websocket.accept()
 
141
  try:
142
+ async for response in process_audio_stream(websocket):
143
+ await websocket.send_text(response)
 
144
  except Exception as e:
145
  print(f"WebSocket error: {e}")
146
  finally:
147
+ await websocket.close()
 
 
 
 
 
 
 
 
 
 
 
148
 
149
  if __name__ == "__main__":
150
+ import uvicorn
151
+ uvicorn.run(app, host="0.0.0.0", port=8000)