AUXteam commited on
Commit
33b7c84
·
verified ·
1 Parent(s): 100efa5

Upload folder using huggingface_hub

Browse files
frontend/src/components/views/chat/chat.tsx CHANGED
@@ -30,6 +30,7 @@ import SampleTasks from "./sampletasks";
30
  import ProgressBar from "./progressbar";
31
  import { Tabs } from "antd";
32
  import Vision from "./vision";
 
33
 
34
  // Extend RunStatus for sidebar status reporting
35
  type SidebarRunStatus = BaseRunStatus | "final_answer_awaiting_input";
@@ -1361,6 +1362,15 @@ export default function ChatView({
1361
  </div>
1362
  ),
1363
  },
 
 
 
 
 
 
 
 
 
1364
  ]}
1365
  />
1366
  </div>
 
30
  import ProgressBar from "./progressbar";
31
  import { Tabs } from "antd";
32
  import Vision from "./vision";
33
+ import Logs from "./logs";
34
 
35
  // Extend RunStatus for sidebar status reporting
36
  type SidebarRunStatus = BaseRunStatus | "final_answer_awaiting_input";
 
1362
  </div>
1363
  ),
1364
  },
1365
+ {
1366
+ key: "logs",
1367
+ label: "Logs",
1368
+ children: (
1369
+ <div className="h-[calc(100vh-150px)] w-full">
1370
+ <Logs />
1371
+ </div>
1372
+ ),
1373
+ },
1374
  ]}
1375
  />
1376
  </div>
frontend/src/components/views/chat/logs.tsx ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react";
2
+ import { getServerUrl } from "../../utils";
3
+
4
+ export default function Logs() {
5
+ const [logs, setLogs] = React.useState<string[]>([]);
6
+ const logsEndRef = React.useRef<HTMLDivElement>(null);
7
+
8
+ React.useEffect(() => {
9
+ // Determine the WebSocket URL based on the server URL
10
+ const serverUrl = getServerUrl();
11
+ const isSecure = serverUrl.startsWith("https://");
12
+ const wsProtocol = isSecure ? "wss://" : "ws://";
13
+ // Usually getServerUrl returns something like http://localhost:8081/api
14
+ const domainAndPath = serverUrl.replace(/^https?:\/\//, "");
15
+
16
+ // Fallback if getServerUrl returns something unexpected
17
+ let wsUrl = "";
18
+ if (domainAndPath.includes("/api")) {
19
+ wsUrl = `${wsProtocol}${domainAndPath}/ws/app_logs`;
20
+ } else {
21
+ wsUrl = `${wsProtocol}${domainAndPath}/api/ws/app_logs`;
22
+ }
23
+
24
+ const socket = new WebSocket(wsUrl);
25
+
26
+ socket.onmessage = (event) => {
27
+ setLogs((prev) => {
28
+ // Keep the last 1000 logs to prevent memory issues
29
+ const newLogs = [...prev, event.data];
30
+ if (newLogs.length > 1000) {
31
+ return newLogs.slice(newLogs.length - 1000);
32
+ }
33
+ return newLogs;
34
+ });
35
+ };
36
+
37
+ socket.onclose = () => {
38
+ console.log("Log WebSocket connection closed");
39
+ };
40
+
41
+ return () => {
42
+ socket.close();
43
+ };
44
+ }, []);
45
+
46
+ React.useEffect(() => {
47
+ // Auto-scroll to bottom when new logs arrive
48
+ if (logsEndRef.current) {
49
+ logsEndRef.current.scrollIntoView({ behavior: "smooth" });
50
+ }
51
+ }, [logs]);
52
+
53
+ return (
54
+ <div className="h-full w-full bg-[#1e1e1e] text-green-400 font-mono text-xs p-4 overflow-y-auto rounded-md shadow-inner">
55
+ {logs.length === 0 ? (
56
+ <div className="opacity-50 text-gray-500 italic">Waiting for logs...</div>
57
+ ) : (
58
+ logs.map((log, index) => (
59
+ <div key={index} className="whitespace-pre-wrap mb-1 break-all">
60
+ {log}
61
+ </div>
62
+ ))
63
+ )}
64
+ <div ref={logsEndRef} />
65
+ </div>
66
+ );
67
+ }
src/magentic_ui/backend/web/routes/ws.py CHANGED
@@ -14,6 +14,44 @@ from ...utils.utils import construct_task
14
  router = APIRouter()
15
 
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  @router.websocket("/runs/{run_id}")
18
  async def run_websocket(
19
  websocket: WebSocket,
 
14
  router = APIRouter()
15
 
16
 
17
+ class LogStreamer:
18
+ def __init__(self):
19
+ self.queues: list[asyncio.Queue] = []
20
+
21
+ def sink(self, message):
22
+ record = message.record
23
+ log_str = f"{record['time'].strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]} | {record['level'].name:<8} | {record['name']}:{record['function']}:{record['line']} - {record['message']}"
24
+ for q in self.queues:
25
+ # Non-blocking put, skip if full to avoid blocking the logger
26
+ try:
27
+ q.put_nowait(log_str)
28
+ except asyncio.QueueFull:
29
+ pass
30
+
31
+ log_streamer = LogStreamer()
32
+ logger.add(log_streamer.sink, format="{message}")
33
+
34
+ @router.websocket("/app_logs")
35
+ async def app_logs_websocket(websocket: WebSocket):
36
+ """WebSocket endpoint to stream application logs"""
37
+ await websocket.accept()
38
+ queue = asyncio.Queue(maxsize=1000)
39
+ log_streamer.queues.append(queue)
40
+ try:
41
+ while True:
42
+ # Wait for a log message
43
+ log_str = await queue.get()
44
+ # Send the log message to the client
45
+ await websocket.send_text(log_str)
46
+ except WebSocketDisconnect:
47
+ pass
48
+ except Exception as e:
49
+ logger.error(f"Error streaming logs: {e}")
50
+ finally:
51
+ if queue in log_streamer.queues:
52
+ log_streamer.queues.remove(queue)
53
+
54
+
55
  @router.websocket("/runs/{run_id}")
56
  async def run_websocket(
57
  websocket: WebSocket,