-
Notifications
You must be signed in to change notification settings - Fork 471
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Plugin] AHDA LITE - Actually helpful digital assistant - control PC …
…with OMI (#1116) See https://github.com/ActuallyAdvanced/OMI-AHDA <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced two new plugins: "Gen Z/A Translator" for translating sentences into Gen Z or Gen Alpha slang. - Added the "AHDA" plugin for voice control of your PC, enhancing user accessibility. - **Documentation** - Updated README with a new section for AHDA setup instructions. - **Chores** - Implemented webhooks and configuration endpoints for AHDA integration. - Added utility functions for managing AHDA-related configurations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Loading branch information
Showing
6 changed files
with
326 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
from fastapi import APIRouter, Request, HTTPException, Form, Query, BackgroundTasks, Body | ||
from fastapi.responses import HTMLResponse, FileResponse, JSONResponse | ||
from db import get_ahda_url, store_ahda, get_ahda_os | ||
import os | ||
import requests | ||
from models import RealtimePluginRequest, EndpointResponse | ||
import time | ||
import asyncio | ||
import logging | ||
from langchain_openai import ChatOpenAI | ||
|
||
router = APIRouter() | ||
|
||
active_sessions = {} | ||
|
||
KEYWORD = "computer" | ||
COMMAND_TIMEOUT = 5 # Seconds to wait after the last word to finalize the command | ||
|
||
# Path to the directory containing `index.html` | ||
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | ||
INDEX_PATH = os.path.join(BASE_DIR, "index.html") | ||
|
||
chat = ChatOpenAI(model='gpt-4o', temperature=0) | ||
|
||
# Use requests to get raw text from URL | ||
prompt = requests.get("https://raw.githubusercontent.com/ActuallyAdvanced/OMI-AHDA/main/prompt.txt").text | ||
|
||
# Configure logging | ||
logging.basicConfig(level=logging.INFO) | ||
logger = logging.getLogger(__name__) | ||
|
||
# AHDA Utils | ||
def sendToPC(uid, response): | ||
ahda_url = get_ahda_url(uid) | ||
if not ahda_url: | ||
raise ValueError('AHDA URL not configured for this UID') | ||
payload = { | ||
'uid': uid, | ||
'response': response | ||
} | ||
try: | ||
resp = requests.post(ahda_url+"/recieve", json=payload) | ||
resp.raise_for_status() | ||
except requests.RequestException as e: | ||
logger.error(f"Error sending webhook: {e}") | ||
raise | ||
return {'message': 'Webhook sent successfully'} | ||
|
||
|
||
@router.post('/ahda/send-webhook', tags=['ahda', 'realtime']) | ||
async def send_ahda_webhook( | ||
uid: str = Query(...), | ||
data: dict = Body(...), | ||
background_tasks: BackgroundTasks = BackgroundTasks() | ||
): | ||
segments = data.get("segments") | ||
if not uid: | ||
raise HTTPException(status_code=400, detail="UID is required") | ||
|
||
if not segments or not isinstance(segments, list): | ||
raise HTTPException(status_code=400, detail="Invalid payload") | ||
|
||
if uid not in active_sessions: | ||
logger.info(f"New session started: {uid}") | ||
active_sessions[uid] = { | ||
"command": "", | ||
"last_received_time": time.time(), | ||
"active": False, | ||
"timer": None | ||
} | ||
|
||
async def schedule_finalize_command(uid, delay): | ||
await asyncio.sleep(delay) | ||
await finalize_command(uid) | ||
|
||
async def finalize_command(uid): | ||
final_command = active_sessions[uid]["command"].strip() | ||
if final_command: | ||
logger.info(f"Final command for session {uid}: {final_command}") | ||
await call_chatgpt_to_generate_code(final_command, uid) | ||
active_sessions[uid]["command"] = "" | ||
active_sessions[uid]["active"] = False | ||
active_sessions[uid]["timer"] = None | ||
|
||
# Adjusted to handle segments as dictionaries | ||
for segment in segments: | ||
text = segment.get("text", "").strip().lower() | ||
logger.info(f"Received segment: {text} (session_id: {uid})") | ||
|
||
if KEYWORD in text: | ||
logger.info("Activation keyword detected!") | ||
active_sessions[uid]["active"] = True | ||
active_sessions[uid]["last_received_time"] = time.time() | ||
|
||
if active_sessions[uid]["timer"]: | ||
pass | ||
|
||
active_sessions[uid]["timer"] = background_tasks.add_task( | ||
schedule_finalize_command, uid, COMMAND_TIMEOUT | ||
) | ||
continue | ||
|
||
if active_sessions[uid]["active"]: | ||
active_sessions[uid]["command"] += " " + text | ||
active_sessions[uid]["last_received_time"] = time.time() | ||
logger.info(f"Aggregating command: {active_sessions[uid]['command'].strip()}") | ||
|
||
if active_sessions[uid]["timer"]: | ||
pass | ||
|
||
active_sessions[uid]["timer"] = background_tasks.add_task( | ||
schedule_finalize_command, uid, COMMAND_TIMEOUT | ||
) | ||
|
||
return {"status": "success"} | ||
|
||
|
||
async def call_chatgpt_to_generate_code(command, uid): | ||
try: | ||
ahda_os = get_ahda_os(uid) | ||
messages = [ | ||
("system", prompt.replace("{os_name}",ahda_os)), | ||
("human", command), | ||
] | ||
ai_msg = chat.invoke(messages) | ||
sendToPC(uid, ai_msg) | ||
except Exception as e: | ||
logger.error(f"Error calling ChatGPT-4: {e}") | ||
return {"type": "error", "content": str(e)} | ||
|
||
@router.get('/ahda/index', response_class=HTMLResponse, tags=['ahda']) | ||
async def get_ahda_index(request: Request, uid: str = Query(None)): | ||
if not uid: | ||
raise HTTPException(status_code=400, detail="UID is required") | ||
return FileResponse(INDEX_PATH) | ||
|
||
@router.post('/ahda/configure', tags=['ahda']) | ||
def configure_ahda(uid: str = Form(...), url: str = Form(...), os: str = Form(...)): | ||
if not uid or not url: | ||
raise HTTPException(status_code=400, detail="Both UID, URL AND OS are required") | ||
|
||
store_ahda(uid, url, os) | ||
return {'message': 'AHDA configured successfully'} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>AHDA Integration</title> | ||
<link rel="preconnect" href="https://fonts.googleapis.com"> | ||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||
<link href="https://fonts.googleapis.com/css2?family=Assistant:[email protected]&display=swap" rel="stylesheet"> | ||
<style> | ||
body { | ||
font-family: Assistant, sans-serif; | ||
background-color: #f4f4f4; | ||
margin: 0; | ||
padding: 0; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 100vh; | ||
} | ||
|
||
.container { | ||
background-color: #fff; | ||
padding: 3rem; | ||
border-radius: 8px; | ||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); | ||
text-align: center; | ||
max-width: 400px; | ||
width: 100%; | ||
} | ||
|
||
h1 { | ||
margin-bottom: 2rem; | ||
} | ||
|
||
.logo-container { | ||
display: flex; | ||
justify-content: center; | ||
gap: 20px; | ||
margin-bottom: 2rem; | ||
} | ||
|
||
.logo-container img { | ||
width: 80px; | ||
height: 80px; | ||
object-fit: contain; | ||
} | ||
|
||
input[type="text"], | ||
button { | ||
width: 100%; | ||
padding: 10px; | ||
margin-bottom: 1rem; | ||
border: 1px solid #ddd; | ||
border-radius: 4px; | ||
box-sizing: border-box; | ||
} | ||
|
||
button { | ||
background-color: #000; | ||
color: #fff; | ||
border: none; | ||
cursor: pointer; | ||
font-size: 1rem; | ||
} | ||
|
||
button:hover { | ||
background-color: #333; | ||
} | ||
|
||
p { | ||
margin-top: 1rem; | ||
} | ||
|
||
.plugin-text { | ||
font-size: 1.2rem; | ||
font-weight: bold; | ||
margin-bottom: 1rem; | ||
} | ||
</style> | ||
</head> | ||
|
||
<body> | ||
<div class="container"> | ||
<div class="logo-container"> | ||
<img src="https://avatars.githubusercontent.com/u/162546372" alt="OMI Logo" /> | ||
<img src="https://avatars.githubusercontent.com/u/181646370" alt="AHDA Logo" /> | ||
</div> | ||
<p class="plugin-text">AHDA Integration Plugin</p> | ||
<h1>AHDA Integration</h1> | ||
<h1>Install https://github.com/ActuallyAdvanced/OMI-AHDA first</h1> | ||
<form id="ahda-form"> | ||
<input type="text" id="url" placeholder="Enter AHDA URL" required> | ||
<input type="text" id="os" placeholder="Enter Operating System" required> | ||
<button type="submit">Save Configuration</button> | ||
</form> | ||
<p id="response-message"></p> | ||
</div> | ||
|
||
<script> | ||
// Extract the uid from the URL query parameters | ||
const urlParams = new URLSearchParams(window.location.search); | ||
const uid = urlParams.get('uid'); | ||
|
||
if (!uid) { | ||
document.getElementById('response-message').textContent = "UID is missing. Please check the URL."; | ||
document.getElementById('response-message').style.color = 'red'; | ||
} | ||
|
||
document.getElementById('ahda-form').addEventListener('submit', async function (event) { | ||
event.preventDefault(); | ||
|
||
const url = document.getElementById('url').value; | ||
const os = document.getElementById('os').value; | ||
|
||
const response = await fetch('/ahda/configure', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}, | ||
body: new URLSearchParams({ uid, url, os }) | ||
}); | ||
|
||
const result = await response.json(); | ||
const messageElement = document.getElementById('response-message'); | ||
if (response.ok) { | ||
messageElement.textContent = result.message; | ||
messageElement.style.color = 'green'; | ||
} else { | ||
messageElement.textContent = result.detail || 'An error occurred. Please try again.'; | ||
messageElement.style.color = 'red'; | ||
} | ||
}); | ||
</script> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
### [Setup Guide for AHDA](https://github.com/ActuallyAdvanced/OMI-AHDA/blob/main/README.md) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.