File size: 4,382 Bytes
ba77d7c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import sys
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
import time
import requests
from dotenv import load_dotenv

load_dotenv()

# ── Simple status web server (HF Spaces requires port 7860) ──────────────────
STATUS_HTML = """<!DOCTYPE html>
<html lang="sw">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Habari Jarvis &mdash; Swahili AI Agent</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      font-family: 'Segoe UI', system-ui, sans-serif;
      background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
      min-height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #fff;
    }
    .card {
      background: rgba(255,255,255,0.07);
      backdrop-filter: blur(16px);
      border: 1px solid rgba(255,255,255,0.15);
      border-radius: 24px;
      padding: 48px 56px;
      text-align: center;
      max-width: 520px;
      width: 90%;
      box-shadow: 0 24px 64px rgba(0,0,0,0.4);
    }
    .emoji { font-size: 64px; margin-bottom: 16px; animation: pulse 2s infinite; }
    @keyframes pulse { 0%,100%{transform:scale(1)} 50%{transform:scale(1.08)} }
    h1 { font-size: 2rem; font-weight: 700; margin-bottom: 8px; }
    .sub { color: rgba(255,255,255,0.65); margin-bottom: 28px; font-size: 1.05rem; }
    .badge {
      display: inline-flex; align-items: center; gap: 8px;
      background: rgba(52,211,153,0.15);
      border: 1px solid rgba(52,211,153,0.4);
      color: #34d399;
      border-radius: 999px;
      padding: 8px 20px;
      font-size: 0.9rem;
      font-weight: 600;
      margin-bottom: 28px;
    }
    .dot { width: 10px; height: 10px; border-radius: 50%; background: #34d399;
           animation: blink 1.4s infinite; }
    @keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.2} }
    .info { font-size: 0.85rem; color: rgba(255,255,255,0.45); line-height: 1.6; }
    code { background: rgba(255,255,255,0.1); border-radius: 6px; padding: 2px 8px; }
  </style>
</head>
<body>
  <div class="card">
    <div class="emoji">πŸ€–</div>
    <h1>Habari Jarvis</h1>
    <p class="sub">Msaidizi wa Akili Bandia &bull; Swahili AI Voice Agent</p>
    <div class="badge"><div class="dot"></div> Agent Running</div>
    <p class="info">
      Connect via <code>LiveKit</code> to start a voice conversation.<br/>
      The agent listens and responds entirely in <strong>Kiswahili</strong>.<br/><br/>
      Powered by Groq Whisper &bull; LLaMA 3.3 70B &bull; MMS/API TTS
    </p>
  </div>
</body>
</html>
"""


class StatusHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/html; charset=utf-8")
        self.end_headers()
        self.wfile.write(STATUS_HTML.encode("utf-8"))

    def log_message(self, *args):  # silence access logs
        pass


def run_status_server():
    server = HTTPServer(("0.0.0.0", 7860), StatusHandler)
    server.serve_forever()


# ── Auto-ping mechanism to keep HF Space awake ───────────────────────────────
def keep_awake():
    # Wait for the server to start
    time.sleep(10)
    url = "https://huggingface.co/spaces/Mapmaven/duol-agent"
    while True:
        try:
            requests.get(url, timeout=10)
            print(f"[OK] Auto-pinged {url} to prevent sleep")
        except Exception as e:
            print(f"[WARN] Auto-ping failed: {e}")
        time.sleep(600)  # Ping every 10 minutes

# ── Start LiveKit agent worker ────────────────────────────────────────────────
from livekit.agents import WorkerOptions, cli
from agent import entrypoint

if __name__ == "__main__":
    # ── Start servers in background threads ───────────────────────────────────────
    t1 = threading.Thread(target=run_status_server, daemon=True)
    t1.start()

    t2 = threading.Thread(target=keep_awake, daemon=True)
    t2.start()
    print("[OK] Status server started on port 7860")

    cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint, agent_name="DUOL"))