38 lines
1.7 KiB
Python
38 lines
1.7 KiB
Python
"""PyQt6 host for the AI desktop dashboard: the no-AI Bridge plus one slot for the AI contract.
|
|
|
|
Copy next to the no-AI bridge.py and bot_runner.py, with server_stub.py and bands.py.
|
|
Run it with: python dashboard/ai_bridge.py
|
|
|
|
The page sends each AI request (templates/contract.md: runs, review rows, the approve gate)
|
|
as ai_request(id, method, path, body) and gets one `ai_response` envelope back. The review
|
|
backend answers it in Python, so the approve gate holds whatever the page does.
|
|
"""
|
|
import json, sys, threading
|
|
from PyQt6.QtCore import QUrl, pyqtSlot
|
|
from PyQt6.QtWidgets import QApplication, QMainWindow
|
|
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
|
from PyQt6.QtWebChannel import QWebChannel
|
|
|
|
import bridge
|
|
import server_stub as review # sample data. Once the project has its own review backend, import that (same dispatch()).
|
|
|
|
|
|
class AiBridge(bridge.Bridge):
|
|
@pyqtSlot(int, str, str, str)
|
|
def ai_request(self, req_id, method, path, body):
|
|
# ponytail: answered on the GUI thread; move to a worker if the backend gets slow.
|
|
status, payload = review.dispatch(method, path, json.loads(body or "{}"))
|
|
self.send_to_js("ai_response", {"id": req_id, "status": status, "body": payload})
|
|
|
|
|
|
if __name__ == "__main__":
|
|
threading.Thread(target=review.simulate_extraction, daemon=True).start() # sample data only: delete with server_stub
|
|
app = QApplication(sys.argv)
|
|
win, view = QMainWindow(), QWebEngineView()
|
|
host, channel = AiBridge(view), QWebChannel()
|
|
channel.registerObject("pyBridge", host)
|
|
view.page().setWebChannel(channel)
|
|
view.setUrl(QUrl.fromLocalFile(str(bridge.HTML_PATH)))
|
|
win.setCentralWidget(view); win.resize(1280, 820); win.setWindowTitle("{{APP_NAME}}"); win.show()
|
|
sys.exit(app.exec())
|