// This program has been developed by students from the bachelor Computer Science at Utrecht // University within the Software Project course. // © Copyright Utrecht University (Department of Information and Computing Sciences) import styles from "./ExperimentLogs.module.css"; import {LogMessages} from "../../../components/Logging/Logging.tsx"; import {useEffect, useMemo, useState} from "react"; import {type LogFilterPredicate, type LogRecord, useLogs} from "../../../components/Logging/useLogs.ts"; import capitalize from "../../../utils/capitalize.ts"; import {useCell} from "../../../utils/cellStore.ts"; import { EXPERIMENT_FILTER_KEY, EXPERIMENT_LOGGER_NAME, type LoggingSettings, type MessageComponentProps, } from "../../../components/Logging/Definitions.ts"; import formatDuration from "../../../utils/formatDuration.ts"; import {create} from "zustand"; import Dialog from "../../../components/Dialog.tsx"; import delayedResolve from "../../../utils/delayedResolve.ts"; /** * Local Zustand store for logging UI preferences. */ const useLoggingSettings = create((set) => ({ showRelativeTime: false, setShowRelativeTime: (showRelativeTime: boolean) => set({ showRelativeTime }), })); /** * A dedicated component for rendering chat messages. * * @param record The chat record to render. */ function ChatMessage({ record }: { record: LogRecord }) { const { showRelativeTime, setShowRelativeTime } = useLoggingSettings(); const reverse = record.role === "user" ? styles.alternate : ""; return
{capitalize(record.role ?? "unknown")} setShowRelativeTime(!showRelativeTime)}>{ showRelativeTime ? formatDuration(record.relativeCreated) : new Date(record.created * 1000).toLocaleTimeString() }
{record.message}
} /** * A generic log message component showing the log level, time, and message text. * * @param record The log record to render. */ function DefaultMessage({ record }: { record: LogRecord }) { const { showRelativeTime, setShowRelativeTime } = useLoggingSettings(); return
{record.levelname} setShowRelativeTime(!showRelativeTime)}>{ showRelativeTime ? formatDuration(record.relativeCreated) : new Date(record.created * 1000).toLocaleTimeString() }
{record.message}
; } /** * A custom component for rendering experiment messages, which might include chat messages. * * @param recordCell The cell containing the log record to render. * @param onUpdate A callback to notify the parent component when the record changes. */ function ExperimentMessage({recordCell, onUpdate}: MessageComponentProps) { const record = useCell(recordCell); // Notify the parent component (e.g., for scroll updates) when this record changes. useEffect(() => { if (onUpdate) onUpdate(); }, [record, onUpdate]); if (record.levelname == "CHAT") { return } else { return } } /** * A download dialog listing experiment logs to download. * * @param filenames The list of available experiment logs to download. * @param refresh A callback to refresh the list of available experiment logs. */ function DownloadScreen({filenames, refresh}: {filenames: string[] | null, refresh: () => void}) { const list = (() => { if (filenames == null) return

Loading...

; if (filenames.length === 0) return

No files available.

return
    {filenames!.map((filename) => (
  1. {filename}
  2. ))}
; })(); return

Select a file to download:

{list}
; } /** * A button that opens a download dialog for experiment logs when pressed. */ function DownloadButton() { const [showModal, setShowModal] = useState(false); const [filenames, setFilenames] = useState(null); async function getFiles(): Promise { const response = await fetch("http://localhost:8000/api/logs/files"); const files = await response.json(); files.sort(); return files; } useEffect(() => { getFiles().then(setFilenames); }, [showModal]); return <> setShowModal(false)} classname={"padding-0 round-lg"}> { setFilenames(null); const files = await delayedResolve(getFiles(), 250); setFilenames(files); }} /> ; } /** * A component for rendering experiment logs. This component uses the `useLogs` hook with a filter to show only * experiment logs. */ export default function ExperimentLogs() { // Show only experiment logs in this logger const filters = useMemo(() => new Map([ [ EXPERIMENT_FILTER_KEY, { predicate: (r) => r.name == EXPERIMENT_LOGGER_NAME, priority: 999, value: null, } as LogFilterPredicate, ], ]), []); const { filteredLogs } = useLogs(filters); return ; }