From 0d7f44af03497191b1f525bd197a7a965e8dcb97 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Thu, 25 Apr 2024 15:22:14 +0530 Subject: [PATCH 01/38] added logs component to layout --- frontend/src/hooks/useResize.jsx | 48 +++++++++ .../src/layouts/page-layout/PageLayout.css | 16 +++ .../src/layouts/page-layout/PageLayout.jsx | 97 ++++++++++++++++++- 3 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 frontend/src/hooks/useResize.jsx diff --git a/frontend/src/hooks/useResize.jsx b/frontend/src/hooks/useResize.jsx new file mode 100644 index 000000000..c1759a159 --- /dev/null +++ b/frontend/src/hooks/useResize.jsx @@ -0,0 +1,48 @@ +import { useCallback, useEffect, useState } from "react"; +import PropTypes from "prop-types"; + +const useResize = ({ minHeight }) => { + const [isResizing, setIsResizing] = useState(false); + const [height, setHeight] = useState(minHeight); + + const enableResize = useCallback(() => { + setIsResizing(true); + }, [setIsResizing]); + + const disableResize = useCallback(() => { + setIsResizing(false); + }, [setIsResizing]); + + const resize = useCallback( + (e) => { + if (isResizing) { + const newHeight = window.innerHeight - e.clientY; // Adjusting for height + if (newHeight >= minHeight) { + setHeight(newHeight); + } + } + }, + [minHeight, isResizing, setHeight] + ); + + useEffect(() => { + const handleMouseMove = (e) => resize(e); + const handleMouseUp = () => disableResize(); + + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + + return () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + }, [disableResize, resize]); + + return { height, enableResize, setHeight }; +}; + +useResize.propTypes = { + minHeight: PropTypes.number.isRequired, +}; + +export { useResize }; diff --git a/frontend/src/layouts/page-layout/PageLayout.css b/frontend/src/layouts/page-layout/PageLayout.css index f72da70c4..2d2d7e4e4 100644 --- a/frontend/src/layouts/page-layout/PageLayout.css +++ b/frontend/src/layouts/page-layout/PageLayout.css @@ -3,6 +3,7 @@ height: 100%; width: 100%; grid-template-rows: auto 1fr; + overflow: hidden; } .topNav { @@ -22,3 +23,18 @@ margin-left: -10px; margin-top: -10px; } + +.log-footer { + padding: 0; + position: relative; +} + +.ide-collapse-panel { + background-color: var(--white); + border: none; + border-radius: 0px; + position: absolute; + bottom: 0; + width: 100%; + z-index: 100; +} diff --git a/frontend/src/layouts/page-layout/PageLayout.jsx b/frontend/src/layouts/page-layout/PageLayout.jsx index def141962..ef6980bd6 100644 --- a/frontend/src/layouts/page-layout/PageLayout.jsx +++ b/frontend/src/layouts/page-layout/PageLayout.jsx @@ -1,13 +1,80 @@ -import { LeftOutlined, RightOutlined } from "@ant-design/icons"; -import { Button, Layout } from "antd"; +import { + FullscreenExitOutlined, + FullscreenOutlined, + LeftOutlined, + RightOutlined, +} from "@ant-design/icons"; +import { Button, Collapse, Layout, Modal } from "antd"; import { useEffect, useState } from "react"; import { Outlet } from "react-router-dom"; import "./PageLayout.css"; import SideNavBar from "../../components/navigations/side-nav-bar/SideNavBar.jsx"; import { TopNavBar } from "../../components/navigations/top-nav-bar/TopNavBar.jsx"; +import { Footer } from "antd/es/layout/layout.js"; +import { DisplayLogs } from "../../components/custom-tools/display-logs/DisplayLogs.jsx"; +import { LogsLabel } from "../../components/agency/logs-label/LogsLabel.jsx"; +import { useResize } from "../../hooks/useResize.jsx"; function PageLayout() { + const [showLogsModal, setShowLogsModal] = useState(false); + const [activeKey, setActiveKey] = useState([]); + const { height, enableResize, setHeight } = useResize({ minHeight: 50 }); + + const openLogsModal = () => { + setShowLogsModal(true); + }; + + const closeLogsModal = () => { + setShowLogsModal(false); + }; + const handleCollapse = (keys) => { + setActiveKey(keys); + console.log(collapsed); + setHeight(keys.length > 0 ? 200 : 50); + }; + + const getItems = () => [ + { + key: "1", + label: + activeKey?.length > 0 ? ( + <> +
e.stopPropagation()} + /> + + + ) : ( + "Logs" + ), + children: ( +
+ +
+ ), + extra: genExtra(), + }, + ]; + + const genExtra = () => ( + { + // If you don't want click extra trigger collapse, you can prevent this: + openLogsModal(); + event.stopPropagation(); + }} + /> + ); + const initialCollapsedValue = JSON.parse(localStorage.getItem("collapsed")) || false; const [collapsed, setCollapsed] = useState(initialCollapsedValue); @@ -29,6 +96,32 @@ function PageLayout() { className="collapse_btn" /> +
+ + } + > + +
+ +
+
+
From 9b94cc89725fa0f5ee517ac056c3761d49d2039a Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Thu, 25 Apr 2024 15:29:29 +0530 Subject: [PATCH 02/38] sonar fix --- frontend/src/layouts/page-layout/PageLayout.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/layouts/page-layout/PageLayout.jsx b/frontend/src/layouts/page-layout/PageLayout.jsx index ef6980bd6..2cd835877 100644 --- a/frontend/src/layouts/page-layout/PageLayout.jsx +++ b/frontend/src/layouts/page-layout/PageLayout.jsx @@ -41,6 +41,7 @@ function PageLayout() { activeKey?.length > 0 ? ( <> -
- - } - > - -
- -
-
-
); } diff --git a/frontend/src/components/agency/logs-label/LogsLabel.jsx b/frontend/src/components/agency/logs-label/LogsLabel.jsx index 6f5a50e7a..3f5138975 100644 --- a/frontend/src/components/agency/logs-label/LogsLabel.jsx +++ b/frontend/src/components/agency/logs-label/LogsLabel.jsx @@ -9,24 +9,30 @@ function LogsLabel() { Time + + Level + Stage Step - - Message + + State - Cost Type + Prompt Key - Cost Units + Doc Name Cost Value + + Message + Iteration diff --git a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx index f0f6b7bd0..f5389039c 100644 --- a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx @@ -2,54 +2,98 @@ import { useEffect, useRef } from "react"; import { Col, Row, Typography } from "antd"; import "../../agency/display-logs/DisplayLogs.css"; -import { useSocketCustomToolStore } from "../../../store/socket-custom-tool"; import { getDateTimeString } from "../../../helpers/GetStaticData"; +import { useAxiosPrivate } from "../../../hooks/useAxiosPrivate"; +import { useSessionStore } from "../../../store/session-store"; +import { useSocketLogsStore } from "../../../store/socket-logs-store"; function DisplayLogs() { const bottomRef = useRef(null); - const { messages } = useSocketCustomToolStore(); + const { logs } = useSocketLogsStore(); + const axiosPrivate = useAxiosPrivate(); + const { sessionDetails } = useSessionStore(); useEffect(() => { - if (messages?.length) { + if (logs?.length) { // Scroll down to the lastest chat. bottomRef.current?.scrollIntoView({ behavior: "smooth" }); } - }, [messages]); + }, [logs]); + + useEffect(() => { + const requestOptions = { + method: "GET", + url: `/api/v1/unstract/${sessionDetails?.orgId}/logs/`, + }; + + axiosPrivate(requestOptions) + .then((res) => { + console.log(res); + }) + .catch((err) => { + console.log(err); + }); + }); return (
- {messages.map((message) => { + {logs.map((log) => { return ( -
+
- + - {getDateTimeString(message?.timestamp)} + {getDateTimeString(log?.timestamp)} + + + + + {log?.level} + + + + + {log?.stage} + + + + + {log?.step} - {message?.level} + {log?.state} - {message?.state} + {log?.component?.prompt_key} - + - {message?.component?.prompt_key} + {log?.component?.doc_name} - + - {message?.component?.doc_name} + {log?.cost} - + + + {log?.message} + + + + + {log?.iteration} + + + - {message?.message} + {log?.iteration_total} diff --git a/frontend/src/components/custom-tools/manage-docs-modal/ManageDocsModal.jsx b/frontend/src/components/custom-tools/manage-docs-modal/ManageDocsModal.jsx index e82457114..6d344ca9b 100644 --- a/frontend/src/components/custom-tools/manage-docs-modal/ManageDocsModal.jsx +++ b/frontend/src/components/custom-tools/manage-docs-modal/ManageDocsModal.jsx @@ -25,13 +25,13 @@ import { useExceptionHandler } from "../../../hooks/useExceptionHandler"; import { useAlertStore } from "../../../store/alert-store"; import { useCustomToolStore } from "../../../store/custom-tool-store"; import { useSessionStore } from "../../../store/session-store"; -import { useSocketCustomToolStore } from "../../../store/socket-custom-tool"; import { ConfirmModal } from "../../widgets/confirm-modal/ConfirmModal"; import { EmptyState } from "../../widgets/empty-state/EmptyState"; import SpaceWrapper from "../../widgets/space-wrapper/SpaceWrapper"; import { SpinnerLoader } from "../../widgets/spinner-loader/SpinnerLoader"; import "./ManageDocsModal.css"; import usePostHogEvents from "../../../hooks/usePostHogEvents"; +import { useSocketLogsStore } from "../../../store/socket-logs-store"; let SummarizeStatusTitle = null; try { @@ -76,7 +76,7 @@ function ManageDocsModal({ summarizeIndexStatus, isSinglePassExtractLoading, } = useCustomToolStore(); - const { messages } = useSocketCustomToolStore(); + const { logs } = useSocketLogsStore(); const axiosPrivate = useAxiosPrivate(); const handleException = useExceptionHandler(); const { setPostHogCustomEvent } = usePostHogEvents(); @@ -154,7 +154,7 @@ function ManageDocsModal({ useEffect(() => { // Reverse the array to have the latest logs at the beginning - let newMessages = [...messages].reverse(); + let newMessages = [...logs].reverse(); // If there are no new messages, return early if (newMessages?.length === 0) { @@ -203,7 +203,7 @@ function ManageDocsModal({ // Update the timestamp of the last received message setLastMessagesUpdate(newMessages[0]?.timestamp); - }, [messages]); + }, [logs]); const handleLoading = (indexType, value) => { if (indexType === indexTypes.raw) { @@ -437,7 +437,7 @@ function ManageDocsModal({ rawIndexStatus, summarizeIndexStatus, indexDocs, - messages, + logs, isSinglePassExtractLoading, ]); diff --git a/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx b/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx index 07f372737..e8b3ba377 100644 --- a/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx +++ b/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx @@ -39,7 +39,6 @@ import { useExceptionHandler } from "../../../hooks/useExceptionHandler"; import { useAlertStore } from "../../../store/alert-store"; import { useCustomToolStore } from "../../../store/custom-tool-store"; import { useSessionStore } from "../../../store/session-store"; -import { useSocketCustomToolStore } from "../../../store/socket-custom-tool"; import { ConfirmModal } from "../../widgets/confirm-modal/ConfirmModal"; import SpaceWrapper from "../../widgets/space-wrapper/SpaceWrapper"; import { SpinnerLoader } from "../../widgets/spinner-loader/SpinnerLoader"; @@ -49,6 +48,7 @@ import "./PromptCard.css"; import { TokenCount } from "../token-count/TokenCount"; import usePostHogEvents from "../../../hooks/usePostHogEvents"; +import { useSocketLogsStore } from "../../../store/socket-logs-store"; const EvalBtn = null; const EvalMetrics = null; @@ -100,7 +100,7 @@ function PromptCard({ singlePassExtractMode, isSinglePassExtractLoading, } = useCustomToolStore(); - const { messages } = useSocketCustomToolStore(); + const { logs } = useSocketLogsStore(); const { sessionDetails } = useSessionStore(); const { setAlertDetails } = useAlertStore(); const axiosPrivate = useAxiosPrivate(); @@ -109,7 +109,7 @@ function PromptCard({ useEffect(() => { // Find the latest message that matches the criteria - const msg = [...messages] + const msg = [...logs] .reverse() .find( (item) => @@ -128,7 +128,7 @@ function PromptCard({ message: msg?.message || "", level: msg?.level || "INFO", }); - }, [messages]); + }, [logs]); useEffect(() => { if (promptDetails?.is_assert) { @@ -737,7 +737,9 @@ function PromptCard({ promptDetails?.prompt_id && updateStatus?.status === promptStudioUpdateStatus.isUpdating) || - disableLlmOrDocChange?.length > 0 || + disableLlmOrDocChange.includes( + promptDetails?.prompt_id + ) || indexDocs.includes(selectedDoc?.document_id) } > diff --git a/frontend/src/components/custom-tools/tool-ide/ToolIde.jsx b/frontend/src/components/custom-tools/tool-ide/ToolIde.jsx index 7ade1571c..a9bc9e01b 100644 --- a/frontend/src/components/custom-tools/tool-ide/ToolIde.jsx +++ b/frontend/src/components/custom-tools/tool-ide/ToolIde.jsx @@ -1,5 +1,4 @@ -import { FullscreenExitOutlined, FullscreenOutlined } from "@ant-design/icons"; -import { Col, Collapse, Modal, Row } from "antd"; +import { Col, Row } from "antd"; import { useState } from "react"; import { useAxiosPrivate } from "../../../hooks/useAxiosPrivate"; @@ -8,10 +7,8 @@ import { useAlertStore } from "../../../store/alert-store"; import { useCustomToolStore } from "../../../store/custom-tool-store"; import { useSessionStore } from "../../../store/session-store"; import { CustomSynonymsModal } from "../custom-synonyms-modal/CustomSynonymsModal"; -import { DisplayLogs } from "../display-logs/DisplayLogs"; import { DocumentManager } from "../document-manager/DocumentManager"; import { Header } from "../header/Header"; -import { LogsLabel } from "../logs-label/LogsLabel"; import { SettingsModal } from "../settings-modal/SettingsModal"; import { ToolsMain } from "../tools-main/ToolsMain"; import "./ToolIde.css"; @@ -30,8 +27,6 @@ try { } function ToolIde() { - const [showLogsModal, setShowLogsModal] = useState(false); - const [activeKey, setActiveKey] = useState([]); const [openCusSynonymsModal, setOpenCusSynonymsModal] = useState(false); const [openSettings, setOpenSettings] = useState(false); const { @@ -51,40 +46,6 @@ function ToolIde() { const [loginModalOpen, setLoginModalOpen] = useState(true); const { setPostHogCustomEvent } = usePostHogEvents(); - const openLogsModal = () => { - setShowLogsModal(true); - }; - - const closeLogsModal = () => { - setShowLogsModal(false); - }; - - const genExtra = () => ( - { - openLogsModal(); - event.stopPropagation(); - }} - /> - ); - - const getItems = () => [ - { - key: "1", - label: activeKey?.length > 0 ? : "Logs", - children: ( -
- -
- ), - extra: genExtra(), - }, - ]; - - const handleCollapse = (keys) => { - setActiveKey(keys); - }; - const generateIndex = async (doc) => { const docId = doc?.document_id; @@ -210,29 +171,6 @@ function ToolIde() {
-
- -
- } - open={showLogsModal} - onCancel={closeLogsModal} - className="agency-ide-log-modal" - footer={null} - width={1400} - closeIcon={} - > -
- -
-
[ - { - key: "1", - label: activeKey?.length > 0 ? : "Logs", - children: ( -
- - - -
- ), - }, - ]; - - const handleCollapse = (keys) => { - setActiveKey(keys); - }; if (isTableLoading) { return ; } @@ -56,19 +35,6 @@ function Body({ type, columns, tableData, isTableLoading, openAddModal }) { }} /> - {deploymentsStaticContent[type].isLogsRequired && ( - <> -
- - - )}
); diff --git a/frontend/src/components/helpers/custom-tools/CustomToolsHelper.js b/frontend/src/components/helpers/custom-tools/CustomToolsHelper.js index b9c60781b..c7e36cb05 100644 --- a/frontend/src/components/helpers/custom-tools/CustomToolsHelper.js +++ b/frontend/src/components/helpers/custom-tools/CustomToolsHelper.js @@ -6,7 +6,6 @@ import { useExceptionHandler } from "../../../hooks/useExceptionHandler"; import { useAlertStore } from "../../../store/alert-store"; import { useCustomToolStore } from "../../../store/custom-tool-store"; import { useSessionStore } from "../../../store/session-store"; -import { useSocketCustomToolStore } from "../../../store/socket-custom-tool"; import { SpinnerLoader } from "../../widgets/spinner-loader/SpinnerLoader"; function CustomToolsHelper() { @@ -14,7 +13,6 @@ function CustomToolsHelper() { const { id } = useParams(); const { sessionDetails } = useSessionStore(); const { updateCustomTool, setDefaultCustomTool } = useCustomToolStore(); - const { emptyCusToolMessages } = useSocketCustomToolStore(); const { setAlertDetails } = useAlertStore(); const navigate = useNavigate(); const axiosPrivate = useAxiosPrivate(); @@ -92,7 +90,6 @@ function CustomToolsHelper() { useEffect(() => { return () => { setDefaultCustomTool(); - emptyCusToolMessages(); }; }, []); diff --git a/frontend/src/components/helpers/socket-messages/SocketMessages.js b/frontend/src/components/helpers/socket-messages/SocketMessages.js index 207120ef8..0a879244e 100644 --- a/frontend/src/components/helpers/socket-messages/SocketMessages.js +++ b/frontend/src/components/helpers/socket-messages/SocketMessages.js @@ -5,7 +5,6 @@ import { useExceptionHandler } from "../../../hooks/useExceptionHandler"; import { useAlertStore } from "../../../store/alert-store"; import { useSocketLogsStore } from "../../../store/socket-logs-store"; import { useSocketMessagesStore } from "../../../store/socket-messages-store"; -import { useSocketCustomToolStore } from "../../../store/socket-custom-tool"; import { useSessionStore } from "../../../store/session-store"; import { useUsageStore } from "../../../store/usage-store"; @@ -19,7 +18,6 @@ function SocketMessages() { setPointer, } = useSocketMessagesStore(); const { pushLogMessages } = useSocketLogsStore(); - const { updateCusToolMessages } = useSocketCustomToolStore(); const { sessionDetails } = useSessionStore(); const socket = useContext(SocketContext); const { setAlertDetails } = useAlertStore(); @@ -44,7 +42,7 @@ function SocketMessages() { pushStagedMessage(msg); } if (msg?.type === "LOG" && msg?.service === "prompt") { - updateCusToolMessages(msg); + pushLogMessages(msg); } if (msg?.type === "LOG" && msg?.service === "usage") { const remainingTokens = diff --git a/frontend/src/layouts/page-layout/PageLayout.jsx b/frontend/src/layouts/page-layout/PageLayout.jsx index 2cd835877..e0c045285 100644 --- a/frontend/src/layouts/page-layout/PageLayout.jsx +++ b/frontend/src/layouts/page-layout/PageLayout.jsx @@ -30,7 +30,6 @@ function PageLayout() { }; const handleCollapse = (keys) => { setActiveKey(keys); - console.log(collapsed); setHeight(keys.length > 0 ? 200 : 50); }; diff --git a/frontend/src/store/socket-logs-store.js b/frontend/src/store/socket-logs-store.js index 7ef8f8595..bd9323373 100644 --- a/frontend/src/store/socket-logs-store.js +++ b/frontend/src/store/socket-logs-store.js @@ -18,9 +18,10 @@ const useSocketLogsStore = create((setState, getState) => ({ level: msg?.level, stage: msg?.stage, step: msg?.step, + state: msg?.state, + prompt_key: msg?.component?.prompt_key, + doc_name: msg?.component?.doc_name, message: msg?.message, - cost_type: msg?.cost_type, - cost_units: msg?.cost_units, cost_value: msg?.cost, iteration: msg?.iteration, iteration_total: msg?.iteration_total, From 512222d7e37705b138bfc5094fb2d47ab5547d93 Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Mon, 13 May 2024 19:59:52 +0530 Subject: [PATCH 08/38] Minor fixes --- frontend/src/components/agency/agency/Agency.jsx | 4 ---- .../src/components/custom-tools/display-logs/DisplayLogs.jsx | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/agency/agency/Agency.jsx b/frontend/src/components/agency/agency/Agency.jsx index 38bbaa698..ac9ac5501 100644 --- a/frontend/src/components/agency/agency/Agency.jsx +++ b/frontend/src/components/agency/agency/Agency.jsx @@ -7,7 +7,6 @@ import { IslandLayout } from "../../../layouts/island-layout/IslandLayout"; import { Actions } from "../actions/Actions"; import { WorkflowExecution } from "../workflow-execution/WorkflowExecution"; import "./Agency.css"; -import { useSocketLogsStore } from "../../../store/socket-logs-store"; import { useSocketMessagesStore } from "../../../store/socket-messages-store"; import { useWorkflowStore } from "../../../store/workflow-store"; import { SidePanel } from "../side-panel/SidePanel"; @@ -21,7 +20,6 @@ function Agency() { const [sourceMsg, setSourceMsg] = useState(""); const [destinationMsg, setDestinationMsg] = useState(""); const { message, setDefault } = useSocketMessagesStore(); - const { emptyLogs } = useSocketLogsStore(); const workflowStore = useWorkflowStore(); const { details, loadingType } = workflowStore; const prompt = details?.prompt_text; @@ -58,7 +56,6 @@ function Agency() { setOutputMd(""); setStatusBarMsg(""); setDefault(); - emptyLogs(); setSourceMsg(""); setDestinationMsg(""); }; @@ -67,7 +64,6 @@ function Agency() { // Clean up function to clear all the socket messages return () => { setDefault(); - emptyLogs(); }; }, []); diff --git a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx index f5389039c..916161d53 100644 --- a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx @@ -2,7 +2,6 @@ import { useEffect, useRef } from "react"; import { Col, Row, Typography } from "antd"; import "../../agency/display-logs/DisplayLogs.css"; -import { getDateTimeString } from "../../../helpers/GetStaticData"; import { useAxiosPrivate } from "../../../hooks/useAxiosPrivate"; import { useSessionStore } from "../../../store/session-store"; import { useSocketLogsStore } from "../../../store/socket-logs-store"; @@ -33,7 +32,7 @@ function DisplayLogs() { .catch((err) => { console.log(err); }); - }); + }, []); return (
@@ -43,7 +42,7 @@ function DisplayLogs() { - {getDateTimeString(log?.timestamp)} + {log?.timestamp} From 8a9f8a4dbbfdc0cae93e140ef68a1f4b8b2bae82 Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Tue, 21 May 2024 18:48:05 +0530 Subject: [PATCH 09/38] Display log messages from the API response --- .../components/custom-tools/display-logs/DisplayLogs.jsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx index 916161d53..f6fdcd40d 100644 --- a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx @@ -8,7 +8,7 @@ import { useSocketLogsStore } from "../../../store/socket-logs-store"; function DisplayLogs() { const bottomRef = useRef(null); - const { logs } = useSocketLogsStore(); + const { logs, pushLogMessages } = useSocketLogsStore(); const axiosPrivate = useAxiosPrivate(); const { sessionDetails } = useSessionStore(); @@ -27,7 +27,12 @@ function DisplayLogs() { axiosPrivate(requestOptions) .then((res) => { - console.log(res); + const data = res?.data?.data || {}; + const keys = Object.keys(data); + + keys.forEach((key) => { + pushLogMessages(JSON.parse(data[key])); + }); }) .catch((err) => { console.log(err); From 0526d35a76212f8a19d445cfe4fb17b84ebf63eb Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Wed, 29 May 2024 13:31:28 +0530 Subject: [PATCH 10/38] store notifications to redis logs --- backend/logs_helper/urls.py | 2 +- backend/logs_helper/views.py | 22 ++-- .../src/components/agency/agency/Agency.css | 9 +- .../agency/display-logs/DisplayLogs.css | 20 +++- .../agency/logs-label/LogsLabel.jsx | 51 +++++---- .../custom-tools/display-logs/DisplayLogs.jsx | 105 ++++++++---------- .../custom-tools/tool-ide/ToolIde.css | 86 +++++++------- frontend/src/helpers/GetStaticData.js | 15 +++ frontend/src/hooks/useResize.jsx | 2 +- .../src/layouts/page-layout/PageLayout.css | 9 ++ .../src/layouts/page-layout/PageLayout.jsx | 58 ++++++---- frontend/src/store/alert-store.js | 39 +++++-- frontend/src/store/socket-logs-store.js | 27 ++++- .../core/src/unstract/core/pubsub_helper.py | 8 +- 14 files changed, 273 insertions(+), 180 deletions(-) diff --git a/backend/logs_helper/urls.py b/backend/logs_helper/urls.py index df7a46aa2..697ef8f86 100644 --- a/backend/logs_helper/urls.py +++ b/backend/logs_helper/urls.py @@ -10,7 +10,7 @@ path( "logs/", logs_helper_get, - name="logs-helper-ping-pong", + name="logs-helper", ), ] ) diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index 9a8b681a8..8d796e1ed 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -1,5 +1,6 @@ import logging import os +import json from datetime import datetime, timezone import redis @@ -39,13 +40,17 @@ def get_logs(self, request): # Retrieve keys matching the pattern keys = self.r.keys(redis_key) - # Retrieve values corresponding to the keys - data = {} + # Retrieve values corresponding to the keys and sort them by timestamp + logs = [] for key in keys: - # Decode the byte response to string and store in the data dictionary - data[key.decode()] = self.r.get(key).decode() + log_data = self.r.get(key).decode() + log_entry = json.loads(log_data) + logs.append(log_entry) - return Response({"data": data}, status=status.HTTP_200_OK) + # Sort logs based on timestamp + sorted_logs = sorted(logs, key=lambda x: x['timestamp']) + + return Response({"data": sorted_logs}, status=status.HTTP_200_OK) except Exception as e: # Handle other exceptions error_msg = "An unexpected error occurred while retrieving logs" @@ -57,19 +62,20 @@ def store_log(self, request): """Store log message in Redis.""" try: # Extract the session ID + logs_expiry = int(os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600)) session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID) serializer = StoreLogMessagesSerializer(data=request.data) serializer.is_valid(raise_exception=True) # Extract the log message from the validated data - log: str = serializer.validated_data.get(LogsHelperKeys.LOG) + log: str = serializer.validated_data.get("log") timestamp = datetime.now(timezone.utc).timestamp() redis_key = f"logs:{session_id}:{timestamp}" - - self.r.setex(redis_key, 60, log) + + self.r.setex(redis_key, logs_expiry, log) return Response({"message": "Successfully stored the message in redis"}) except KeyError as e: diff --git a/frontend/src/components/agency/agency/Agency.css b/frontend/src/components/agency/agency/Agency.css index 3eb162ba6..f50843827 100644 --- a/frontend/src/components/agency/agency/Agency.css +++ b/frontend/src/components/agency/agency/Agency.css @@ -4,6 +4,7 @@ height: 100%; display: flex; flex-direction: column; + padding-bottom: 10px; } .agency-sider-layout { @@ -40,20 +41,16 @@ padding: 12px; } -.agency-ide-logs { - height: 10vh; -} - .agency-ide-collapse-panel { background-color: var(--white); border: none; border-radius: 0px; } -.agency-ide-log-modal .ant-modal-content{ +.agency-ide-log-modal .ant-modal-content { height: 80vh; } -.agency-ide-log-modal .agency-ide-logs{ +.agency-ide-log-modal .agency-ide-logs { height: 70vh !important; } diff --git a/frontend/src/components/agency/display-logs/DisplayLogs.css b/frontend/src/components/agency/display-logs/DisplayLogs.css index d1e9541d3..071980459 100644 --- a/frontend/src/components/agency/display-logs/DisplayLogs.css +++ b/frontend/src/components/agency/display-logs/DisplayLogs.css @@ -1,15 +1,25 @@ /* Styles for DisplayLogs */ .tool-logs { - height: 100%; - overflow-y: auto; + height: 100%; + overflow-y: auto; } .display-logs-col-first { - font-size: 12px; + font-size: 12px; } .display-logs-col { - font-size: 12px; - padding-left: 5px; + font-size: 12px; + padding-left: 5px; +} + +.display-logs-container { + padding-right: 25px; + padding-top: 2px; + border-top: 1px rgba(0, 0, 0, 0.15) solid; +} + +.display-logs-error-bg { + background-color: #fad4d4; } diff --git a/frontend/src/components/agency/logs-label/LogsLabel.jsx b/frontend/src/components/agency/logs-label/LogsLabel.jsx index 3f5138975..20083379c 100644 --- a/frontend/src/components/agency/logs-label/LogsLabel.jsx +++ b/frontend/src/components/agency/logs-label/LogsLabel.jsx @@ -1,8 +1,13 @@ import { Col, Row, Typography } from "antd"; import "./LogsLabel.css"; +import { useLocation } from "react-router-dom"; function LogsLabel() { + const location = useLocation(); // Get the current route location + const isWorkflowSubPage = /^\/[^/]+\/workflows\/.+/.test(location.pathname); + const isPromptStudioPage = /^\/[^/]+\/tools\/.+/.test(location.pathname); + return (
@@ -12,33 +17,35 @@ function LogsLabel() { Level - - Stage - - - Step - - - State + + Type - Prompt Key - - - Doc Name - - - Cost Value + Stage - + {isWorkflowSubPage && ( + <> + + Step + + + State + + + )} + {isPromptStudioPage && ( + <> + + Prompt Key + + + Doc Name + + + )} + Message - - Iteration - - - Iteration Total -
); diff --git a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx index f6fdcd40d..cbe757ac8 100644 --- a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx @@ -2,15 +2,17 @@ import { useEffect, useRef } from "react"; import { Col, Row, Typography } from "antd"; import "../../agency/display-logs/DisplayLogs.css"; -import { useAxiosPrivate } from "../../../hooks/useAxiosPrivate"; -import { useSessionStore } from "../../../store/session-store"; import { useSocketLogsStore } from "../../../store/socket-logs-store"; +import { uniqueId } from "lodash"; +import { convertTimestampToHHMMSS } from "../../../helpers/GetStaticData"; +import { useLocation } from "react-router-dom"; function DisplayLogs() { const bottomRef = useRef(null); - const { logs, pushLogMessages } = useSocketLogsStore(); - const axiosPrivate = useAxiosPrivate(); - const { sessionDetails } = useSessionStore(); + const { logs } = useSocketLogsStore(); + const location = useLocation(); // Get the current route location + const isWorkflowSubPage = /^\/[^/]+\/workflows\/.+/.test(location.pathname); + const isPromptStudioPage = /^\/[^/]+\/tools\/.+/.test(location.pathname); useEffect(() => { if (logs?.length) { @@ -19,35 +21,20 @@ function DisplayLogs() { } }, [logs]); - useEffect(() => { - const requestOptions = { - method: "GET", - url: `/api/v1/unstract/${sessionDetails?.orgId}/logs/`, - }; - - axiosPrivate(requestOptions) - .then((res) => { - const data = res?.data?.data || {}; - const keys = Object.keys(data); - - keys.forEach((key) => { - pushLogMessages(JSON.parse(data[key])); - }); - }) - .catch((err) => { - console.log(err); - }); - }, []); - return (
{logs.map((log) => { return ( -
+
- {log?.timestamp} + {convertTimestampToHHMMSS(log?.timestamp)} @@ -55,51 +42,49 @@ function DisplayLogs() { {log?.level} - - - {log?.stage} - - - - - {log?.step} - - - + - {log?.state} + {log?.type} - {log?.component?.prompt_key} - - - - - {log?.component?.doc_name} - - - - - {log?.cost} + {log?.stage} + {isWorkflowSubPage && ( + <> + + + {log?.step} + + + + + {log?.state} + + + + )} + {isPromptStudioPage && ( + <> + + + {log?.component?.prompt_key} + + + + + {log?.component?.doc_name} + + + + )} {log?.message} - - - {log?.iteration} - - - - - {log?.iteration_total} - -
diff --git a/frontend/src/components/custom-tools/tool-ide/ToolIde.css b/frontend/src/components/custom-tools/tool-ide/ToolIde.css index 41cb40103..2c50fb7dd 100644 --- a/frontend/src/components/custom-tools/tool-ide/ToolIde.css +++ b/frontend/src/components/custom-tools/tool-ide/ToolIde.css @@ -1,103 +1,105 @@ /* Styles for ToolIde */ .tool-ide-layout { - background-color: var(--page-bg-2); - height: 100%; - display: flex; - flex-direction: column; + background-color: var(--page-bg-2); + height: 100%; + display: flex; + flex-direction: column; } .tool-ide-body { - flex: 1; - overflow-y: hidden; + flex: 1; + overflow-y: hidden; + padding-bottom: 10px; } .tool-ide-body-2 { - display: flex; - flex-direction: column; - height: 100%; + display: flex; + flex-direction: column; + height: 100%; } .tool-ide-main { - flex-grow: 1; - overflow-y: hidden; + flex-grow: 1; + overflow-y: hidden; } .tool-ide-col { - height: 100%; + height: 100%; } .tool-ide-main-row { - height: 100%; + height: 100%; } .tool-ide-footer { - padding: 0px 12px 12px 12px; + padding: 0px 12px 12px 12px; } .tool-ide-prompts { - padding: 12px 6px 1px 12px; - height: 100%; + padding: 12px 6px 1px 12px; + height: 100%; } .tool-ide-pdf { - padding: 12px 12px 1px 6px; - height: 100%; + padding: 12px 12px 1px 6px; + height: 100%; } -.tool-ide-prompts > div, .tool-ide-pdf > div { - background-color: var(--white); - height: 100%; +.tool-ide-prompts > div, +.tool-ide-pdf > div { + background-color: var(--white); + height: 100%; } .tool-ide-actions { - padding: 0px 12px; + padding: 0px 12px; } .tool-ide-logs { - height: 10vh; + height: 10vh; } .tool-ide-sider { - background-color: transparent !important; - height: 100%; + background-color: transparent !important; + height: 100%; } .tool-ide-sider-layout { - height: 100%; - background-color: transparent !important; + height: 100%; + background-color: transparent !important; } .tool-ide-sider-btn { - position: fixed; - transform: translate(-50%, 100%); - z-index: 1; - transition: "left 0.1s linear"; + position: fixed; + transform: translate(-50%, 100%); + z-index: 1; + transition: "left 0.1s linear"; } .tool-ide-collapse-panel { - background-color: var(--white); - border: none; - border-radius: 0px; + background-color: var(--white); + border: none; + border-radius: 0px; } /* Remove padding from modal content */ .custom-modal-wrapper .ant-modal-content { - padding: 0; - height: 90vh; - overflow-y: auto; + padding: 0; + height: 90vh; + overflow-y: auto; } .custom-modal-wrapper .tools-prompts-header-layout { - border-radius: 5px 5px 0px 0px; + border-radius: 5px 5px 0px 0px; } .custom-modal-gen-index .ant-modal { - top: 20px; - right: 20px; - position: absolute !important; + top: 20px; + right: 20px; + position: absolute !important; } .tool-ide-main-card .card-text { - font-size: 12px; + font-size: 12px; } diff --git a/frontend/src/helpers/GetStaticData.js b/frontend/src/helpers/GetStaticData.js index b9ac03252..07581a4ad 100644 --- a/frontend/src/helpers/GetStaticData.js +++ b/frontend/src/helpers/GetStaticData.js @@ -408,6 +408,20 @@ const generateUUID = () => { const uuid = uuidv4(); return uuid; }; +const convertTimestampToHHMMSS = (timestamp) => { + // Convert the timestamp to milliseconds + const date = new Date(timestamp * 1000); + + // Extract hours, minutes, and seconds + const hours = date.getUTCHours().toString().padStart(2, "0"); + const minutes = date.getUTCMinutes().toString().padStart(2, "0"); + const seconds = date.getUTCSeconds().toString().padStart(2, "0"); + + // Combine to form hh:mm:ss format + const timeString = `${hours}:${minutes}:${seconds}`; + + return timeString; +}; export { CONNECTOR_TYPE_MAP, @@ -446,4 +460,5 @@ export { isNonNegativeNumber, defaultTokenUsage, generateUUID, + convertTimestampToHHMMSS, }; diff --git a/frontend/src/hooks/useResize.jsx b/frontend/src/hooks/useResize.jsx index c1759a159..fda5eceb6 100644 --- a/frontend/src/hooks/useResize.jsx +++ b/frontend/src/hooks/useResize.jsx @@ -16,7 +16,7 @@ const useResize = ({ minHeight }) => { const resize = useCallback( (e) => { if (isResizing) { - const newHeight = window.innerHeight - e.clientY; // Adjusting for height + const newHeight = window.innerHeight - e.clientY; if (newHeight >= minHeight) { setHeight(newHeight); } diff --git a/frontend/src/layouts/page-layout/PageLayout.css b/frontend/src/layouts/page-layout/PageLayout.css index 817e496f3..a3683b12b 100644 --- a/frontend/src/layouts/page-layout/PageLayout.css +++ b/frontend/src/layouts/page-layout/PageLayout.css @@ -38,4 +38,13 @@ bottom: 0; width: 100%; z-index: 100; + max-height: 90vh; + display: flex; + flex-direction: column; + justify-content: center; +} + +.logs-title { + text-align: center; + align-items: center; } diff --git a/frontend/src/layouts/page-layout/PageLayout.jsx b/frontend/src/layouts/page-layout/PageLayout.jsx index e0c045285..a18197343 100644 --- a/frontend/src/layouts/page-layout/PageLayout.jsx +++ b/frontend/src/layouts/page-layout/PageLayout.jsx @@ -1,10 +1,10 @@ import { + ExceptionOutlined, FullscreenExitOutlined, - FullscreenOutlined, LeftOutlined, RightOutlined, } from "@ant-design/icons"; -import { Button, Collapse, Layout, Modal } from "antd"; +import { Button, Collapse, Layout, Modal, Typography } from "antd"; import { useEffect, useState } from "react"; import { Outlet } from "react-router-dom"; import "./PageLayout.css"; @@ -15,14 +15,34 @@ import { Footer } from "antd/es/layout/layout.js"; import { DisplayLogs } from "../../components/custom-tools/display-logs/DisplayLogs.jsx"; import { LogsLabel } from "../../components/agency/logs-label/LogsLabel.jsx"; import { useResize } from "../../hooks/useResize.jsx"; +import axios from "axios"; +import { useSessionStore } from "../../store/session-store.js"; +import { useSocketLogsStore } from "../../store/socket-logs-store.js"; function PageLayout() { const [showLogsModal, setShowLogsModal] = useState(false); const [activeKey, setActiveKey] = useState([]); const { height, enableResize, setHeight } = useResize({ minHeight: 50 }); + const { sessionDetails } = useSessionStore(); + const initialCollapsedValue = + JSON.parse(localStorage.getItem("collapsed")) || false; + const [collapsed, setCollapsed] = useState(initialCollapsedValue); - const openLogsModal = () => { - setShowLogsModal(true); + const getLogs = async () => { + const requestOptions = { + method: "GET", + url: `/api/v1/unstract/${sessionDetails.orgId}/logs/`, + headers: { + "X-CSRFToken": sessionDetails.csrfToken, + }, + }; + + try { + const response = await axios(requestOptions); + return response.data; + } catch (error) { + return; + } }; const closeLogsModal = () => { @@ -54,33 +74,29 @@ function PageLayout() { ) : ( - "Logs" + + + Logs + ), children: ( -
+
), - extra: genExtra(), }, ]; - const genExtra = () => ( - { - // If you don't want click extra trigger collapse, you can prevent this: - openLogsModal(); - event.stopPropagation(); - }} - /> - ); - - const initialCollapsedValue = - JSON.parse(localStorage.getItem("collapsed")) || false; - const [collapsed, setCollapsed] = useState(initialCollapsedValue); useEffect(() => { localStorage.setItem("collapsed", JSON.stringify(collapsed)); - }, [collapsed]); + if (activeKey.length > 0) { + getLogs().then((res) => + useSocketLogsStore.setState(() => ({ + logs: [...res.data], + })) + ); + } + }, [collapsed, activeKey]); return (
diff --git a/frontend/src/store/alert-store.js b/frontend/src/store/alert-store.js index b95a0b66b..95649c773 100644 --- a/frontend/src/store/alert-store.js +++ b/frontend/src/store/alert-store.js @@ -1,5 +1,7 @@ import { create } from "zustand"; import { isNonNegativeNumber } from "../helpers/GetStaticData"; +import { useSocketLogsStore } from "../store/socket-logs-store"; +import { uniqueId } from "lodash"; const STORE_VARIABLES = { alertDetails: { @@ -10,21 +12,42 @@ const STORE_VARIABLES = { key: null, }, }; + const useAlertStore = create((setState) => ({ ...STORE_VARIABLES, setAlertDetails: (details) => { - setState(() => { - const isErrorType = details?.type === "error"; - details["title"] = - details["title"] || (isErrorType ? "Failed" : "Success"); - details["duration"] = isNonNegativeNumber(details.duration) + if (details.type === "ERROR_LOG") { + setState({ + alertDetails: { + content: details.message, + title: "Failed", + duration: 0, + key: `open${Date.now()}-${uniqueId()}`, + type: "error", + }, + }); + return; + } + const { pushLogMessages } = useSocketLogsStore.getState(); + const isErrorType = details?.type === "error"; + const updatedDetails = { + ...details, + title: details.title || (isErrorType ? "Failed" : "Success"), + duration: isNonNegativeNumber(details.duration) ? details.duration : isErrorType ? 0 - : undefined; - details.key = `open${Date.now()}`; - return { alertDetails: { ...details } }; + : undefined, + key: `open${Date.now()}-${uniqueId()}`, + }; + + pushLogMessages({ + level: isErrorType ? "ERROR" : "SUCCESS", + message: updatedDetails.content, + type: "NOTIFICATION", }); + + setState({ alertDetails: updatedDetails }); }, })); diff --git a/frontend/src/store/socket-logs-store.js b/frontend/src/store/socket-logs-store.js index bd9323373..bd6c87343 100644 --- a/frontend/src/store/socket-logs-store.js +++ b/frontend/src/store/socket-logs-store.js @@ -1,6 +1,8 @@ import { create } from "zustand"; -import { getTimeForLogs } from "../helpers/GetStaticData"; +import axios from "axios"; +import { useSessionStore } from "./session-store"; +import { useAlertStore } from "./alert-store"; const STORE_VARIABLES = { logs: [], @@ -10,10 +12,12 @@ const useSocketLogsStore = create((setState, getState) => ({ ...STORE_VARIABLES, pushLogMessages: (msg) => { const existingState = { ...getState() }; + const { sessionDetails } = useSessionStore.getState(); + const { setAlertDetails } = useAlertStore.getState(); let logsData = [...(existingState?.logs || [])]; const newLog = { - timestamp: getTimeForLogs(), + timestamp: Math.floor(Date.now() / 1000), key: logsData?.length + 1, level: msg?.level, stage: msg?.stage, @@ -25,10 +29,27 @@ const useSocketLogsStore = create((setState, getState) => ({ cost_value: msg?.cost, iteration: msg?.iteration, iteration_total: msg?.iteration_total, + type: msg?.type, }; logsData.push(newLog); - + if (newLog?.type === "LOG" && newLog?.level === "ERROR") { + setAlertDetails({ + type: "ERROR_LOG", + message: newLog?.message, + }); + } + if (newLog?.type === "NOTIFICATION" && sessionDetails?.isLoggedIn) { + const requestOptions = { + method: "POST", + url: `/api/v1/unstract/${sessionDetails?.orgId}/logs/`, + headers: { + "X-CSRFToken": sessionDetails?.csrfToken, + }, + data: { log: JSON.stringify(newLog) }, + }; + axios(requestOptions).catch((err) => {}); + } // Remove the previous logs if the length exceeds 200 const logsDataLength = logsData?.length; if (logsDataLength > 200) { diff --git a/unstract/core/src/unstract/core/pubsub_helper.py b/unstract/core/src/unstract/core/pubsub_helper.py index 3374e76a4..6410bc12c 100644 --- a/unstract/core/src/unstract/core/pubsub_helper.py +++ b/unstract/core/src/unstract/core/pubsub_helper.py @@ -96,8 +96,10 @@ def log_prompt( @classmethod def publish(cls, channel_id: str, payload: dict[str, Any]) -> bool: channel = f"logs:{channel_id}" + logs_expiry = int(os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600)) try: - cls.r.publish(channel, json.dumps(payload)) + log_data = json.dumps(payload) + cls.r.publish(channel, log_data) # Check if the payload type is "LOG" if payload["type"] == "LOG": @@ -108,8 +110,8 @@ def publish(cls, channel_id: str, payload: dict[str, Any]) -> bool: redis_key = f"{channel}:{timestamp}" # Store logs in Redis with expiration of 1 hour - cls.r.setex(redis_key, 3600, json.dumps(payload)) + cls.r.setex(redis_key, logs_expiry, log_data) except Exception as e: - logging.error(f"Failed to publish '{channel}' <= {payload}: {e}") + logging.error(f"Failed to publish '{redis_key}' <= {log_data}: {e}") return False return True From 4e3d00f557333136de0b849cb80f3d9cc3afbc8e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 08:02:15 +0000 Subject: [PATCH 11/38] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backend/logs_helper/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index 8d796e1ed..2416eb679 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -1,6 +1,6 @@ +import json import logging import os -import json from datetime import datetime, timezone import redis @@ -48,7 +48,7 @@ def get_logs(self, request): logs.append(log_entry) # Sort logs based on timestamp - sorted_logs = sorted(logs, key=lambda x: x['timestamp']) + sorted_logs = sorted(logs, key=lambda x: x["timestamp"]) return Response({"data": sorted_logs}, status=status.HTTP_200_OK) except Exception as e: @@ -74,7 +74,7 @@ def store_log(self, request): timestamp = datetime.now(timezone.utc).timestamp() redis_key = f"logs:{session_id}:{timestamp}" - + self.r.setex(redis_key, logs_expiry, log) return Response({"message": "Successfully stored the message in redis"}) From b30cc6d1005aa75f5c70d9aa7fe9b06b966db920 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Fri, 31 May 2024 10:20:02 +0530 Subject: [PATCH 12/38] updated sample.env --- backend/sample.env | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/sample.env b/backend/sample.env index 9f3441568..afcd46a5a 100644 --- a/backend/sample.env +++ b/backend/sample.env @@ -137,3 +137,5 @@ ENABLE_LOG_HISTORY=True LOG_HISTORY_CONSUMER_INTERVAL=30 # Maximum number of logs to insert in a single batch. LOGS_BATCH_LIMIT=30 +# Logs Expiry +LOGS_EXPIRATION_TIME_IN_SECOND=3600 \ No newline at end of file From 71c4de751b57fcd653a87ed0747933f87de930cd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 04:50:54 +0000 Subject: [PATCH 13/38] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backend/sample.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/sample.env b/backend/sample.env index afcd46a5a..a77b73730 100644 --- a/backend/sample.env +++ b/backend/sample.env @@ -138,4 +138,4 @@ LOG_HISTORY_CONSUMER_INTERVAL=30 # Maximum number of logs to insert in a single batch. LOGS_BATCH_LIMIT=30 # Logs Expiry -LOGS_EXPIRATION_TIME_IN_SECOND=3600 \ No newline at end of file +LOGS_EXPIRATION_TIME_IN_SECOND=3600 From 0d91070ceae227521e034c1a6eae8c33ae4b09e2 Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Mon, 3 Jun 2024 09:31:47 +0530 Subject: [PATCH 14/38] Removed exceptions as it is handled by default --- backend/logs_helper/views.py | 100 ++++++++++++++--------------------- 1 file changed, 39 insertions(+), 61 deletions(-) diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index 2416eb679..a3a21f606 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -5,10 +5,8 @@ import redis from logs_helper.constants import LogsHelperKeys -from logs_helper.exceptions import InvalidValueError, MissingFieldsKeyError from rest_framework import status, viewsets from rest_framework.decorators import action -from rest_framework.exceptions import APIException from rest_framework.response import Response from utils.local_context import StateStore @@ -28,66 +26,46 @@ class LogsHelperView(viewsets.ModelViewSet): ) @action(detail=False, methods=["get"]) - def get_logs(self, request): - try: - # Extract the session ID - session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID) - - # Construct the Redis key pattern to match keys - # associated with the session ID - redis_key = f"logs:{session_id}*" - - # Retrieve keys matching the pattern - keys = self.r.keys(redis_key) - - # Retrieve values corresponding to the keys and sort them by timestamp - logs = [] - for key in keys: - log_data = self.r.get(key).decode() - log_entry = json.loads(log_data) - logs.append(log_entry) - - # Sort logs based on timestamp - sorted_logs = sorted(logs, key=lambda x: x["timestamp"]) - - return Response({"data": sorted_logs}, status=status.HTTP_200_OK) - except Exception as e: - # Handle other exceptions - error_msg = "An unexpected error occurred while retrieving logs" - logger.error(f"{error_msg}: {e}") - raise APIException(error_msg) + def get_logs(self): + # Extract the session ID + session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID) + + # Construct the Redis key pattern to match keys + # associated with the session ID + redis_key = f"logs:{session_id}*" + + # Retrieve keys matching the pattern + keys = self.r.keys(redis_key) + + # Retrieve values corresponding to the keys and sort them by timestamp + logs = [] + for key in keys: + log_data = self.r.get(key).decode() + log_entry = json.loads(log_data) + logs.append(log_entry) + + # Sort logs based on timestamp + sorted_logs = sorted(logs, key=lambda x: x["timestamp"]) + + return Response({"data": sorted_logs}, status=status.HTTP_200_OK) @action(detail=False, methods=["post"]) def store_log(self, request): """Store log message in Redis.""" - try: - # Extract the session ID - logs_expiry = int(os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600)) - session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID) - - serializer = StoreLogMessagesSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - - # Extract the log message from the validated data - log: str = serializer.validated_data.get("log") - - timestamp = datetime.now(timezone.utc).timestamp() - - redis_key = f"logs:{session_id}:{timestamp}" - - self.r.setex(redis_key, logs_expiry, log) - - return Response({"message": "Successfully stored the message in redis"}) - except KeyError as e: - # Handle KeyError - logger.error(f"Log is missing: {e}") - raise MissingFieldsKeyError() - except ValueError as e: - # Handle ValueError - logger.error(f"Invalid value: {e}") - raise InvalidValueError() - except Exception as e: - # Handle other exceptions - error_msg = "An unexpected error occurred while store the log message" - logger.error(f"{error_msg}: {e}") - raise APIException(error_msg) + # Extract the session ID + logs_expiry = int(os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600)) + session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID) + + serializer = StoreLogMessagesSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + # Extract the log message from the validated data + log: str = serializer.validated_data.get("log") + + timestamp = datetime.now(timezone.utc).timestamp() + + redis_key = f"logs:{session_id}:{timestamp}" + + self.r.setex(redis_key, logs_expiry, log) + + return Response({"message": "Successfully stored the message in redis"}) From b091bd71f326db42a4c24272e77ceac8d13873c4 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Mon, 3 Jun 2024 16:32:54 +0530 Subject: [PATCH 15/38] changed to django redis --- backend/logs_helper/views.py | 11 +++-------- frontend/src/layouts/page-layout/PageLayout.jsx | 2 +- unstract/core/src/unstract/core/pubsub_helper.py | 10 ++-------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index a3a21f606..ffd9788e4 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -3,12 +3,12 @@ import os from datetime import datetime, timezone -import redis from logs_helper.constants import LogsHelperKeys from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from utils.local_context import StateStore +from django_redis import get_redis_connection from .serializers import StoreLogMessagesSerializer @@ -18,15 +18,10 @@ class LogsHelperView(viewsets.ModelViewSet): """Viewset to handle all Tool Studio prompt related API logics.""" - r = redis.Redis( - host=os.environ.get("REDIS_HOST", "http://localhost"), - port=os.environ.get("REDIS_PORT", "6379"), - username=os.environ.get("REDIS_USER", ""), - password=os.environ.get("REDIS_PASSWORD", ""), - ) + r = get_redis_connection("default") @action(detail=False, methods=["get"]) - def get_logs(self): + def get_logs(self, request): # Extract the session ID session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID) diff --git a/frontend/src/layouts/page-layout/PageLayout.jsx b/frontend/src/layouts/page-layout/PageLayout.jsx index a18197343..48b5267b0 100644 --- a/frontend/src/layouts/page-layout/PageLayout.jsx +++ b/frontend/src/layouts/page-layout/PageLayout.jsx @@ -41,7 +41,7 @@ function PageLayout() { const response = await axios(requestOptions); return response.data; } catch (error) { - return; + return { data: [] }; } }; diff --git a/unstract/core/src/unstract/core/pubsub_helper.py b/unstract/core/src/unstract/core/pubsub_helper.py index 6410bc12c..6ed013cb5 100644 --- a/unstract/core/src/unstract/core/pubsub_helper.py +++ b/unstract/core/src/unstract/core/pubsub_helper.py @@ -3,17 +3,11 @@ import os from datetime import datetime, timezone from typing import Any, Optional - -import redis +from django_redis import get_redis_connection class LogPublisher: - r = redis.Redis( - host=os.environ.get("REDIS_HOST", "http://localhost"), - port=os.environ.get("REDIS_PORT", "6379"), - username=os.environ.get("REDIS_USER", ""), - password=os.environ.get("REDIS_PASSWORD", ""), - ) + r = get_redis_connection("default") @staticmethod def log_usage( From 23318c294d7d9a1274a004d4638d3dde109edfc1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:41:34 +0000 Subject: [PATCH 16/38] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backend/logs_helper/views.py | 2 +- unstract/core/src/unstract/core/pubsub_helper.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index ffd9788e4..4a33e677f 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -3,12 +3,12 @@ import os from datetime import datetime, timezone +from django_redis import get_redis_connection from logs_helper.constants import LogsHelperKeys from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from utils.local_context import StateStore -from django_redis import get_redis_connection from .serializers import StoreLogMessagesSerializer diff --git a/unstract/core/src/unstract/core/pubsub_helper.py b/unstract/core/src/unstract/core/pubsub_helper.py index 6ed013cb5..ea4e19f70 100644 --- a/unstract/core/src/unstract/core/pubsub_helper.py +++ b/unstract/core/src/unstract/core/pubsub_helper.py @@ -3,6 +3,7 @@ import os from datetime import datetime, timezone from typing import Any, Optional + from django_redis import get_redis_connection From b3eaf66892c2e5a4132e96bcd6e3e709696ad3de Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Mon, 3 Jun 2024 17:20:48 +0530 Subject: [PATCH 17/38] build fix --- .../src/components/helpers/custom-tools/CustomToolsHelper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/helpers/custom-tools/CustomToolsHelper.js b/frontend/src/components/helpers/custom-tools/CustomToolsHelper.js index 368caaa7f..dd938ecef 100644 --- a/frontend/src/components/helpers/custom-tools/CustomToolsHelper.js +++ b/frontend/src/components/helpers/custom-tools/CustomToolsHelper.js @@ -92,7 +92,6 @@ function CustomToolsHelper() { useEffect(() => { return () => { setDefaultCustomTool(); - emptyCusToolMessages(); resetTokenUsage(); }; }, []); From 6806d51f6fc6e97c38b652ff5c660773e1875c77 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Wed, 5 Jun 2024 11:50:29 +0530 Subject: [PATCH 18/38] remove logs on logout --- backend/account/authentication_controller.py | 3 +++ backend/logs_helper/log_service.py | 17 +++++++++++++++++ backend/logs_helper/urls.py | 4 ++-- backend/logs_helper/views.py | 4 ++-- 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 backend/logs_helper/log_service.py diff --git a/backend/account/authentication_controller.py b/backend/account/authentication_controller.py index 18822d21c..a6576c05a 100644 --- a/backend/account/authentication_controller.py +++ b/backend/account/authentication_controller.py @@ -47,6 +47,7 @@ from utils.cache_service import CacheService from utils.local_context import StateStore from utils.user_session import UserSessionUtils +from logs_helper.log_service import LogService Logger = logging.getLogger(__name__) @@ -259,6 +260,8 @@ def make_user_organization_display_name(self, user_name: str) -> str: return self.auth_service.make_user_organization_display_name(user_name) def user_logout(self, request: Request) -> Response: + session_id: str = request.COOKIES.get("sessionid") + LogService.remove_logs_on_logout(session_id=session_id) response = self.auth_service.user_logout(request=request) organization_id = UserSessionUtils.get_organization_id(request) user_id = UserSessionUtils.get_user_id(request) diff --git a/backend/logs_helper/log_service.py b/backend/logs_helper/log_service.py new file mode 100644 index 000000000..b329857f7 --- /dev/null +++ b/backend/logs_helper/log_service.py @@ -0,0 +1,17 @@ +from django_redis import get_redis_connection + +class LogService: + @staticmethod + def remove_logs_on_logout(session_id): + + if session_id: + # Get the Redis connection + r = get_redis_connection("default") + + # Construct the Redis key pattern to match keys associated with the session ID + redis_key_pattern = f"logs:{session_id}*" + + # Retrieve keys matching the pattern and delete them + keys = r.keys(redis_key_pattern) + if keys: + r.delete(*keys) diff --git a/backend/logs_helper/urls.py b/backend/logs_helper/urls.py index 697ef8f86..95f748dd2 100644 --- a/backend/logs_helper/urls.py +++ b/backend/logs_helper/urls.py @@ -1,9 +1,9 @@ from django.urls import path from rest_framework.urlpatterns import format_suffix_patterns -from .views import LogsHelperView +from .views import LogsHelperViewSet -logs_helper_get = LogsHelperView.as_view({"get": "get_logs", "post": "store_log"}) +logs_helper_get = LogsHelperViewSet.as_view({"get": "get_logs", "post": "store_log"}) urlpatterns = format_suffix_patterns( [ diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index 4a33e677f..7015a41a3 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -3,19 +3,19 @@ import os from datetime import datetime, timezone -from django_redis import get_redis_connection from logs_helper.constants import LogsHelperKeys from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from utils.local_context import StateStore +from django_redis import get_redis_connection from .serializers import StoreLogMessagesSerializer logger = logging.getLogger(__name__) -class LogsHelperView(viewsets.ModelViewSet): +class LogsHelperViewSet(viewsets.ModelViewSet): """Viewset to handle all Tool Studio prompt related API logics.""" r = get_redis_connection("default") From 6b1949faeb17078af2e5dbd73e9e1d3067d1e8f5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 06:21:27 +0000 Subject: [PATCH 19/38] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backend/account/authentication_controller.py | 2 +- backend/logs_helper/log_service.py | 3 ++- backend/logs_helper/views.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/account/authentication_controller.py b/backend/account/authentication_controller.py index a6576c05a..ce509f4a2 100644 --- a/backend/account/authentication_controller.py +++ b/backend/account/authentication_controller.py @@ -39,6 +39,7 @@ from django.middleware import csrf from django.shortcuts import redirect from django_tenants.utils import tenant_context +from logs_helper.log_service import LogService from rest_framework import status from rest_framework.request import Request from rest_framework.response import Response @@ -47,7 +48,6 @@ from utils.cache_service import CacheService from utils.local_context import StateStore from utils.user_session import UserSessionUtils -from logs_helper.log_service import LogService Logger = logging.getLogger(__name__) diff --git a/backend/logs_helper/log_service.py b/backend/logs_helper/log_service.py index b329857f7..570982190 100644 --- a/backend/logs_helper/log_service.py +++ b/backend/logs_helper/log_service.py @@ -1,9 +1,10 @@ from django_redis import get_redis_connection + class LogService: @staticmethod def remove_logs_on_logout(session_id): - + if session_id: # Get the Redis connection r = get_redis_connection("default") diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index 7015a41a3..26df13fc3 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -3,12 +3,12 @@ import os from datetime import datetime, timezone +from django_redis import get_redis_connection from logs_helper.constants import LogsHelperKeys from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from utils.local_context import StateStore -from django_redis import get_redis_connection from .serializers import StoreLogMessagesSerializer From c25f2ea90cd9afa53a62b49c8aa5d08af1973560 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Wed, 5 Jun 2024 11:56:53 +0530 Subject: [PATCH 20/38] pre-commit fix --- backend/logs_helper/log_service.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/logs_helper/log_service.py b/backend/logs_helper/log_service.py index b329857f7..1be13bc5a 100644 --- a/backend/logs_helper/log_service.py +++ b/backend/logs_helper/log_service.py @@ -8,10 +8,9 @@ def remove_logs_on_logout(session_id): # Get the Redis connection r = get_redis_connection("default") - # Construct the Redis key pattern to match keys associated with the session ID - redis_key_pattern = f"logs:{session_id}*" + key_pattern = f"logs:{session_id}*" # Retrieve keys matching the pattern and delete them - keys = r.keys(redis_key_pattern) + keys = r.keys(key_pattern) if keys: r.delete(*keys) From 91af677688b23ce80014037f595f74e101c3d2d0 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Mon, 10 Jun 2024 14:19:07 +0530 Subject: [PATCH 21/38] reused CacheService --- backend/account/authentication_controller.py | 2 +- backend/backend/settings/base.py | 1 + backend/logs_helper/log_service.py | 13 +++----- backend/logs_helper/urls.py | 4 +-- backend/logs_helper/views.py | 31 +++++++++---------- backend/utils/cache_service.py | 10 ++++-- backend/utils/user_session.py | 6 ++++ .../core/src/unstract/core/pubsub_helper.py | 10 ++++-- 8 files changed, 44 insertions(+), 33 deletions(-) diff --git a/backend/account/authentication_controller.py b/backend/account/authentication_controller.py index ce509f4a2..583137301 100644 --- a/backend/account/authentication_controller.py +++ b/backend/account/authentication_controller.py @@ -260,7 +260,7 @@ def make_user_organization_display_name(self, user_name: str) -> str: return self.auth_service.make_user_organization_display_name(user_name) def user_logout(self, request: Request) -> Response: - session_id: str = request.COOKIES.get("sessionid") + session_id: str = UserSessionUtils.get_session_id(request=request) LogService.remove_logs_on_logout(session_id=session_id) response = self.auth_service.user_logout(request=request) organization_id = UserSessionUtils.get_organization_id(request) diff --git a/backend/backend/settings/base.py b/backend/backend/settings/base.py index d043d1664..fbdfd252d 100644 --- a/backend/backend/settings/base.py +++ b/backend/backend/settings/base.py @@ -160,6 +160,7 @@ def get_required_setting( get_required_setting("LOG_HISTORY_CONSUMER_INTERVAL", "60") ) LOGS_BATCH_LIMIT = int(get_required_setting("LOGS_BATCH_LIMIT", "30")) +LOGS_EXPIRATION_TIME_IN_SECOND=int(os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600)) # Flag to Enable django admin ADMIN_ENABLED = False diff --git a/backend/logs_helper/log_service.py b/backend/logs_helper/log_service.py index 7781a258b..18edca551 100644 --- a/backend/logs_helper/log_service.py +++ b/backend/logs_helper/log_service.py @@ -1,17 +1,12 @@ -from django_redis import get_redis_connection +from utils.cache_service import CacheService class LogService: @staticmethod - def remove_logs_on_logout(session_id): + def remove_logs_on_logout(session_id: str) -> None: if session_id: - # Get the Redis connection - r = get_redis_connection("default") - key_pattern = f"logs:{session_id}*" - # Retrieve keys matching the pattern and delete them - keys = r.keys(key_pattern) - if keys: - r.delete(*keys) + # Delete keys matching the pattern + CacheService.clear_cache(key_pattern=key_pattern) diff --git a/backend/logs_helper/urls.py b/backend/logs_helper/urls.py index 95f748dd2..41c547986 100644 --- a/backend/logs_helper/urls.py +++ b/backend/logs_helper/urls.py @@ -3,13 +3,13 @@ from .views import LogsHelperViewSet -logs_helper_get = LogsHelperViewSet.as_view({"get": "get_logs", "post": "store_log"}) +logs_helper = LogsHelperViewSet.as_view({"get": "get_logs", "post": "store_log"}) urlpatterns = format_suffix_patterns( [ path( "logs/", - logs_helper_get, + logs_helper, name="logs-helper", ), ] diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index 26df13fc3..97761a73b 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -3,13 +3,13 @@ import os from datetime import datetime, timezone -from django_redis import get_redis_connection -from logs_helper.constants import LogsHelperKeys from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response -from utils.local_context import StateStore - +from django.http import HttpRequest +from utils.cache_service import CacheService +from django.conf import settings +from utils.user_session import UserSessionUtils from .serializers import StoreLogMessagesSerializer logger = logging.getLogger(__name__) @@ -18,26 +18,25 @@ class LogsHelperViewSet(viewsets.ModelViewSet): """Viewset to handle all Tool Studio prompt related API logics.""" - r = get_redis_connection("default") - @action(detail=False, methods=["get"]) - def get_logs(self, request): + def get_logs(self, request: HttpRequest) -> Response: # Extract the session ID - session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID) + session_id: str = UserSessionUtils.get_session_id(request=request) # Construct the Redis key pattern to match keys # associated with the session ID redis_key = f"logs:{session_id}*" # Retrieve keys matching the pattern - keys = self.r.keys(redis_key) + keys = CacheService.get_all_keys(redis_key) # Retrieve values corresponding to the keys and sort them by timestamp logs = [] for key in keys: - log_data = self.r.get(key).decode() - log_entry = json.loads(log_data) - logs.append(log_entry) + log_data = CacheService.get_key(key) + if log_data: + log_entry = json.loads(log_data) + logs.append(log_entry) # Sort logs based on timestamp sorted_logs = sorted(logs, key=lambda x: x["timestamp"]) @@ -45,11 +44,11 @@ def get_logs(self, request): return Response({"data": sorted_logs}, status=status.HTTP_200_OK) @action(detail=False, methods=["post"]) - def store_log(self, request): + def store_log(self, request: HttpRequest) -> Response: """Store log message in Redis.""" # Extract the session ID - logs_expiry = int(os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600)) - session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID) + logs_expiry = settings.LOGS_EXPIRATION_TIME_IN_SECOND + session_id: str = UserSessionUtils.get_session_id(request=request) serializer = StoreLogMessagesSerializer(data=request.data) serializer.is_valid(raise_exception=True) @@ -61,6 +60,6 @@ def store_log(self, request): redis_key = f"logs:{session_id}:{timestamp}" - self.r.setex(redis_key, logs_expiry, log) + CacheService.set_key(redis_key, log, logs_expiry) return Response({"message": "Successfully stored the message in redis"}) diff --git a/backend/utils/cache_service.py b/backend/utils/cache_service.py index 691b8ee2a..a95537c1a 100644 --- a/backend/utils/cache_service.py +++ b/backend/utils/cache_service.py @@ -1,4 +1,4 @@ -from typing import Any, Optional +from typing import Any, Optional, List from django.conf import settings from django.core.cache import cache @@ -28,6 +28,12 @@ def set_key( expire, ) + @staticmethod + def get_all_keys(key_pattern: str) -> List[str]: + keys = redis_cache.keys(key_pattern) + # Ensure all keys are strings + return [key.decode("utf-8") if isinstance(key, bytes) else key for key in keys] + @staticmethod def clear_cache(key_pattern: str) -> Any: """Delete keys in bulk based on the key pattern.""" @@ -41,7 +47,7 @@ def check_a_key_exist(key: str, version: Any = None) -> bool: @staticmethod def delete_a_key(key: str, version: Any = None) -> None: cache.delete(key, version) - + @staticmethod def set_user_organizations(user_id: str, organizations: list[str]) -> None: key: str = f"{user_id}|organizations" diff --git a/backend/utils/user_session.py b/backend/utils/user_session.py index 7df8c2995..cd123aa0e 100644 --- a/backend/utils/user_session.py +++ b/backend/utils/user_session.py @@ -15,3 +15,9 @@ def set_organization_id(request: HttpRequest, organization_id: str) -> None: @staticmethod def get_user_id(request: HttpRequest) -> Optional[str]: return request.session.get("user_id") + + @staticmethod + def get_session_id(request: HttpRequest) -> Optional[str]: + return request.session.session_key + + diff --git a/unstract/core/src/unstract/core/pubsub_helper.py b/unstract/core/src/unstract/core/pubsub_helper.py index ea4e19f70..74bd516b0 100644 --- a/unstract/core/src/unstract/core/pubsub_helper.py +++ b/unstract/core/src/unstract/core/pubsub_helper.py @@ -4,11 +4,15 @@ from datetime import datetime, timezone from typing import Any, Optional -from django_redis import get_redis_connection - +import redis class LogPublisher: - r = get_redis_connection("default") + r = redis.Redis( + host=os.environ.get("REDIS_HOST", "http://localhost"), + port=os.environ.get("REDIS_PORT", "6379"), + username=os.environ.get("REDIS_USER", ""), + password=os.environ.get("REDIS_PASSWORD", ""), + ) @staticmethod def log_usage( From c451c51e4778eda50796dd67758085a7181bb3a4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 08:55:13 +0000 Subject: [PATCH 22/38] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- unstract/core/src/unstract/core/pubsub_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unstract/core/src/unstract/core/pubsub_helper.py b/unstract/core/src/unstract/core/pubsub_helper.py index c514ea068..9011c40a0 100644 --- a/unstract/core/src/unstract/core/pubsub_helper.py +++ b/unstract/core/src/unstract/core/pubsub_helper.py @@ -2,6 +2,7 @@ import os from datetime import datetime, timezone from typing import Any, Optional + from kombu import Connection from unstract.core.constants import LogEventArgument, LogProcessingTask @@ -10,7 +11,7 @@ class LogPublisher: kombu_conn = Connection(os.environ.get("CELERY_BROKER_URL")) - + @staticmethod def log_usage( level: str = "INFO", @@ -115,7 +116,6 @@ def _get_task_header(cls, task_name: str) -> dict[str, Any]: @classmethod def publish(cls, channel_id: str, payload: dict[str, Any]) -> bool: - """Publish a message to the queue.""" try: with cls.kombu_conn.Producer(serializer="json") as producer: From c643b027f0941531ea4f563079cb3fe421f1663e Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Mon, 10 Jun 2024 14:55:38 +0530 Subject: [PATCH 23/38] merge fix --- frontend/src/components/custom-tools/prompt-card/PromptCard.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx b/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx index 059363bde..3ffb04c61 100644 --- a/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx +++ b/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx @@ -10,7 +10,6 @@ import { useExceptionHandler } from "../../../hooks/useExceptionHandler"; import { useAlertStore } from "../../../store/alert-store"; import { useCustomToolStore } from "../../../store/custom-tool-store"; import { useSessionStore } from "../../../store/session-store"; -import { useSocketCustomToolStore } from "../../../store/socket-custom-tool"; import { OutputForDocModal } from "../output-for-doc-modal/OutputForDocModal"; import usePostHogEvents from "../../../hooks/usePostHogEvents"; import { useSocketLogsStore } from "../../../store/socket-logs-store"; From 68516da2a63991389e4a2646030788d96593d51c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 09:26:22 +0000 Subject: [PATCH 24/38] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backend/backend/settings/base.py | 4 +++- backend/logs_helper/views.py | 6 +++--- backend/utils/cache_service.py | 6 +++--- backend/utils/user_session.py | 2 -- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/backend/settings/base.py b/backend/backend/settings/base.py index d0eecdfbf..b5413d975 100644 --- a/backend/backend/settings/base.py +++ b/backend/backend/settings/base.py @@ -160,7 +160,9 @@ def get_required_setting( get_required_setting("LOG_HISTORY_CONSUMER_INTERVAL", "60") ) LOGS_BATCH_LIMIT = int(get_required_setting("LOGS_BATCH_LIMIT", "30")) -LOGS_EXPIRATION_TIME_IN_SECOND=int(os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600)) +LOGS_EXPIRATION_TIME_IN_SECOND = int( + os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600) +) CELERY_BROKER_URL = get_required_setting( "CELERY_BROKER_URL", f"redis://{REDIS_HOST}:{REDIS_PORT}" ) diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index 97761a73b..86b009c18 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -1,15 +1,15 @@ import json import logging -import os from datetime import datetime, timezone +from django.conf import settings +from django.http import HttpRequest from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response -from django.http import HttpRequest from utils.cache_service import CacheService -from django.conf import settings from utils.user_session import UserSessionUtils + from .serializers import StoreLogMessagesSerializer logger = logging.getLogger(__name__) diff --git a/backend/utils/cache_service.py b/backend/utils/cache_service.py index a95537c1a..3460a62b5 100644 --- a/backend/utils/cache_service.py +++ b/backend/utils/cache_service.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, List +from typing import Any, List, Optional from django.conf import settings from django.core.cache import cache @@ -29,7 +29,7 @@ def set_key( ) @staticmethod - def get_all_keys(key_pattern: str) -> List[str]: + def get_all_keys(key_pattern: str) -> list[str]: keys = redis_cache.keys(key_pattern) # Ensure all keys are strings return [key.decode("utf-8") if isinstance(key, bytes) else key for key in keys] @@ -47,7 +47,7 @@ def check_a_key_exist(key: str, version: Any = None) -> bool: @staticmethod def delete_a_key(key: str, version: Any = None) -> None: cache.delete(key, version) - + @staticmethod def set_user_organizations(user_id: str, organizations: list[str]) -> None: key: str = f"{user_id}|organizations" diff --git a/backend/utils/user_session.py b/backend/utils/user_session.py index cd123aa0e..1d84cc3eb 100644 --- a/backend/utils/user_session.py +++ b/backend/utils/user_session.py @@ -19,5 +19,3 @@ def get_user_id(request: HttpRequest) -> Optional[str]: @staticmethod def get_session_id(request: HttpRequest) -> Optional[str]: return request.session.session_key - - From b8f2a33786ebc6be426da1530bb22036226bd604 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Mon, 10 Jun 2024 15:14:34 +0530 Subject: [PATCH 25/38] fixed return type --- backend/utils/cache_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/utils/cache_service.py b/backend/utils/cache_service.py index a95537c1a..666e1f7c7 100644 --- a/backend/utils/cache_service.py +++ b/backend/utils/cache_service.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, List +from typing import Any, Optional from django.conf import settings from django.core.cache import cache @@ -29,7 +29,7 @@ def set_key( ) @staticmethod - def get_all_keys(key_pattern: str) -> List[str]: + def get_all_keys(key_pattern: str) -> Any: keys = redis_cache.keys(key_pattern) # Ensure all keys are strings return [key.decode("utf-8") if isinstance(key, bytes) else key for key in keys] From 7e9511fa7f3144344527b9ac08cb76e9f1c69d51 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Thu, 20 Jun 2024 09:47:54 +0530 Subject: [PATCH 26/38] code cleanup --- frontend/src/components/agency/logs-label/LogsLabel.jsx | 5 +++-- frontend/src/helpers/GetStaticData.js | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/agency/logs-label/LogsLabel.jsx b/frontend/src/components/agency/logs-label/LogsLabel.jsx index 20083379c..e9b6fdb05 100644 --- a/frontend/src/components/agency/logs-label/LogsLabel.jsx +++ b/frontend/src/components/agency/logs-label/LogsLabel.jsx @@ -2,11 +2,12 @@ import { Col, Row, Typography } from "antd"; import "./LogsLabel.css"; import { useLocation } from "react-router-dom"; +import { isSubPage } from "../../../helpers/GetStaticData"; function LogsLabel() { const location = useLocation(); // Get the current route location - const isWorkflowSubPage = /^\/[^/]+\/workflows\/.+/.test(location.pathname); - const isPromptStudioPage = /^\/[^/]+\/tools\/.+/.test(location.pathname); + const isWorkflowSubPage = isSubPage("workflows", location.pathname); + const isPromptStudioPage = isSubPage("tools", location.pathname); return (
diff --git a/frontend/src/helpers/GetStaticData.js b/frontend/src/helpers/GetStaticData.js index 07581a4ad..05122c058 100644 --- a/frontend/src/helpers/GetStaticData.js +++ b/frontend/src/helpers/GetStaticData.js @@ -423,6 +423,11 @@ const convertTimestampToHHMMSS = (timestamp) => { return timeString; }; +const isSubPage = (type, path) => { + const regex = new RegExp(`^/[^/]+/${type}/.+`); + return regex.test(path); +}; + export { CONNECTOR_TYPE_MAP, O_AUTH_PROVIDERS, @@ -461,4 +466,5 @@ export { defaultTokenUsage, generateUUID, convertTimestampToHHMMSS, + isSubPage, }; From c0fcfde6928be618a2c45f766f1369fcf5244302 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Thu, 20 Jun 2024 09:49:32 +0530 Subject: [PATCH 27/38] code cleanup --- .../components/custom-tools/display-logs/DisplayLogs.jsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx index cbe757ac8..bc91b72f2 100644 --- a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx @@ -4,15 +4,18 @@ import { Col, Row, Typography } from "antd"; import "../../agency/display-logs/DisplayLogs.css"; import { useSocketLogsStore } from "../../../store/socket-logs-store"; import { uniqueId } from "lodash"; -import { convertTimestampToHHMMSS } from "../../../helpers/GetStaticData"; +import { + convertTimestampToHHMMSS, + isSubPage, +} from "../../../helpers/GetStaticData"; import { useLocation } from "react-router-dom"; function DisplayLogs() { const bottomRef = useRef(null); const { logs } = useSocketLogsStore(); const location = useLocation(); // Get the current route location - const isWorkflowSubPage = /^\/[^/]+\/workflows\/.+/.test(location.pathname); - const isPromptStudioPage = /^\/[^/]+\/tools\/.+/.test(location.pathname); + const isWorkflowSubPage = isSubPage("workflows", location.pathname); + const isPromptStudioPage = isSubPage("tools", location.pathname); useEffect(() => { if (logs?.length) { From 2619260cf34ed10c14ebbc9d1d88d25f753390ce Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Mon, 24 Jun 2024 11:09:15 +0530 Subject: [PATCH 28/38] encapsulated redis key creation --- backend/logs_helper/constants.py | 5 ----- backend/logs_helper/exceptions.py | 12 ------------ backend/logs_helper/log_service.py | 13 +++++++++++++ backend/logs_helper/views.py | 7 +++++-- 4 files changed, 18 insertions(+), 19 deletions(-) delete mode 100644 backend/logs_helper/exceptions.py diff --git a/backend/logs_helper/constants.py b/backend/logs_helper/constants.py index b338bcc42..18de76e1f 100644 --- a/backend/logs_helper/constants.py +++ b/backend/logs_helper/constants.py @@ -1,8 +1,3 @@ class LogsHelperKeys: LOG = "LOG" LOG_EVENTS_ID = "log_events_id" - - -class LogsHelperExceptionMessages: - MISSING_FIELDS = "Log message is missing" - INVALID_VALUE = "Invalid value for the log message" diff --git a/backend/logs_helper/exceptions.py b/backend/logs_helper/exceptions.py deleted file mode 100644 index 4acd2bbf8..000000000 --- a/backend/logs_helper/exceptions.py +++ /dev/null @@ -1,12 +0,0 @@ -from logs_helper.constants import LogsHelperExceptionMessages -from rest_framework.exceptions import APIException - - -class MissingFieldsKeyError(APIException): - status_code = 400 - default_detail = LogsHelperExceptionMessages.MISSING_FIELDS - - -class InvalidValueError(APIException): - status_code = 400 - default_detail = LogsHelperExceptionMessages.INVALID_VALUE diff --git a/backend/logs_helper/log_service.py b/backend/logs_helper/log_service.py index 18edca551..eb2bd8f9e 100644 --- a/backend/logs_helper/log_service.py +++ b/backend/logs_helper/log_service.py @@ -10,3 +10,16 @@ def remove_logs_on_logout(session_id: str) -> None: # Delete keys matching the pattern CacheService.clear_cache(key_pattern=key_pattern) + + @staticmethod + def generate_redis_key(session_id): + """ + Generate a Redis key for logs based on the provided session_id. + + Parameters: + session_id (str): The session identifier to include in the Redis key. + + Returns: + str: The constructed Redis key. + """ + return f"logs:{session_id}*" \ No newline at end of file diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index 86b009c18..bfa0326c2 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -11,6 +11,7 @@ from utils.user_session import UserSessionUtils from .serializers import StoreLogMessagesSerializer +from .log_service import LogService logger = logging.getLogger(__name__) @@ -25,7 +26,7 @@ def get_logs(self, request: HttpRequest) -> Response: # Construct the Redis key pattern to match keys # associated with the session ID - redis_key = f"logs:{session_id}*" + redis_key = LogService.generate_redis_key(session_id=session_id) # Retrieve keys matching the pattern keys = CacheService.get_all_keys(redis_key) @@ -58,7 +59,9 @@ def store_log(self, request: HttpRequest) -> Response: timestamp = datetime.now(timezone.utc).timestamp() - redis_key = f"logs:{session_id}:{timestamp}" + redis_key = f"{ + LogService.generate_redis_key(session_id=session_id) + }:{timestamp}" CacheService.set_key(redis_key, log, logs_expiry) From 08b9a9ae302e5ef559dcfbdccb039b731b88b562 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 05:40:02 +0000 Subject: [PATCH 29/38] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backend/logs_helper/log_service.py | 11 +++++------ backend/logs_helper/views.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/backend/logs_helper/log_service.py b/backend/logs_helper/log_service.py index eb2bd8f9e..22add8480 100644 --- a/backend/logs_helper/log_service.py +++ b/backend/logs_helper/log_service.py @@ -10,16 +10,15 @@ def remove_logs_on_logout(session_id: str) -> None: # Delete keys matching the pattern CacheService.clear_cache(key_pattern=key_pattern) - - @staticmethod + + @staticmethod def generate_redis_key(session_id): - """ - Generate a Redis key for logs based on the provided session_id. - + """Generate a Redis key for logs based on the provided session_id. + Parameters: session_id (str): The session identifier to include in the Redis key. Returns: str: The constructed Redis key. """ - return f"logs:{session_id}*" \ No newline at end of file + return f"logs:{session_id}*" diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index bfa0326c2..c58966c35 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -10,8 +10,8 @@ from utils.cache_service import CacheService from utils.user_session import UserSessionUtils -from .serializers import StoreLogMessagesSerializer from .log_service import LogService +from .serializers import StoreLogMessagesSerializer logger = logging.getLogger(__name__) From e9badc367644c2248e8e5fa76cd1273abfd6d20c Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Mon, 24 Jun 2024 11:23:33 +0530 Subject: [PATCH 30/38] code optimization --- backend/logs_helper/log_service.py | 11 +++++------ backend/logs_helper/views.py | 8 ++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/backend/logs_helper/log_service.py b/backend/logs_helper/log_service.py index eb2bd8f9e..22add8480 100644 --- a/backend/logs_helper/log_service.py +++ b/backend/logs_helper/log_service.py @@ -10,16 +10,15 @@ def remove_logs_on_logout(session_id: str) -> None: # Delete keys matching the pattern CacheService.clear_cache(key_pattern=key_pattern) - - @staticmethod + + @staticmethod def generate_redis_key(session_id): - """ - Generate a Redis key for logs based on the provided session_id. - + """Generate a Redis key for logs based on the provided session_id. + Parameters: session_id (str): The session identifier to include in the Redis key. Returns: str: The constructed Redis key. """ - return f"logs:{session_id}*" \ No newline at end of file + return f"logs:{session_id}*" diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index bfa0326c2..786967f73 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -10,8 +10,8 @@ from utils.cache_service import CacheService from utils.user_session import UserSessionUtils -from .serializers import StoreLogMessagesSerializer from .log_service import LogService +from .serializers import StoreLogMessagesSerializer logger = logging.getLogger(__name__) @@ -59,9 +59,9 @@ def store_log(self, request: HttpRequest) -> Response: timestamp = datetime.now(timezone.utc).timestamp() - redis_key = f"{ - LogService.generate_redis_key(session_id=session_id) - }:{timestamp}" + redis_key = ( + f"{LogService.generate_redis_key(session_id=session_id)}:{timestamp}" + ) CacheService.set_key(redis_key, log, logs_expiry) From e898b18691e17dc15c5454e03e8843aa4f19e7ff Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Mon, 29 Jul 2024 13:42:01 +0530 Subject: [PATCH 31/38] added logs to redis --- backend/logs_helper/log_service.py | 2 +- backend/logs_helper/views.py | 10 ++++------ .../core/src/unstract/core/pubsub_helper.py | 18 +++++++++++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/backend/logs_helper/log_service.py b/backend/logs_helper/log_service.py index 22add8480..59c4df4cb 100644 --- a/backend/logs_helper/log_service.py +++ b/backend/logs_helper/log_service.py @@ -21,4 +21,4 @@ def generate_redis_key(session_id): Returns: str: The constructed Redis key. """ - return f"logs:{session_id}*" + return f"logs:{session_id}" diff --git a/backend/logs_helper/views.py b/backend/logs_helper/views.py index 786967f73..26cec8e52 100644 --- a/backend/logs_helper/views.py +++ b/backend/logs_helper/views.py @@ -29,15 +29,13 @@ def get_logs(self, request: HttpRequest) -> Response: redis_key = LogService.generate_redis_key(session_id=session_id) # Retrieve keys matching the pattern - keys = CacheService.get_all_keys(redis_key) + keys = CacheService.get_all_keys(f"{redis_key}*") # Retrieve values corresponding to the keys and sort them by timestamp logs = [] for key in keys: log_data = CacheService.get_key(key) - if log_data: - log_entry = json.loads(log_data) - logs.append(log_entry) + logs.append(log_data) # Sort logs based on timestamp sorted_logs = sorted(logs, key=lambda x: x["timestamp"]) @@ -56,13 +54,13 @@ def store_log(self, request: HttpRequest) -> Response: # Extract the log message from the validated data log: str = serializer.validated_data.get("log") - + log_data = json.loads(log) timestamp = datetime.now(timezone.utc).timestamp() redis_key = ( f"{LogService.generate_redis_key(session_id=session_id)}:{timestamp}" ) - CacheService.set_key(redis_key, log, logs_expiry) + CacheService.set_key(redis_key, log_data, logs_expiry) return Response({"message": "Successfully stored the message in redis"}) diff --git a/unstract/core/src/unstract/core/pubsub_helper.py b/unstract/core/src/unstract/core/pubsub_helper.py index 9011c40a0..a0090e19b 100644 --- a/unstract/core/src/unstract/core/pubsub_helper.py +++ b/unstract/core/src/unstract/core/pubsub_helper.py @@ -1,15 +1,16 @@ +import json import logging import os from datetime import datetime, timezone from typing import Any, Optional +from django_redis import get_redis_connection from kombu import Connection from unstract.core.constants import LogEventArgument, LogProcessingTask class LogPublisher: - kombu_conn = Connection(os.environ.get("CELERY_BROKER_URL")) @staticmethod @@ -116,8 +117,11 @@ def _get_task_header(cls, task_name: str) -> dict[str, Any]: @classmethod def publish(cls, channel_id: str, payload: dict[str, Any]) -> bool: + channel = f"logs:{channel_id}" + r = get_redis_connection("default") """Publish a message to the queue.""" try: + with cls.kombu_conn.Producer(serializer="json") as producer: event = f"logs:{channel_id}" task_message = cls._get_task_message( @@ -136,6 +140,18 @@ def publish(cls, channel_id: str, payload: dict[str, Any]) -> bool: retry=True, ) logging.debug(f"Published '{channel_id}' <= {payload}") + log_data = json.dumps(payload) + # Check if the payload type is "LOG" + if payload["type"] == "LOG": + # Extract timestamp from payload + timestamp = payload["timestamp"] + + # Construct Redis key using channel and timestamp + redis_key = f"{channel}:{timestamp}" + + # Store logs in Redis with expiration of 1 hour + r.setex(redis_key, 3600, log_data) + except Exception as e: logging.error(f"Failed to publish '{channel_id}' <= {payload}: {e}") return False From 45a9bb162bc0167057eac32cbb806172efea1658 Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Tue, 30 Jul 2024 20:05:49 +0530 Subject: [PATCH 32/38] UI/UX improvements in unified notifications --- .../list-of-tools/ListOfTools.css | 2 +- .../list-of-tools/ListOfTools.jsx | 4 +- .../custom-tools/tool-ide/ToolIde.css | 2 +- .../workflows/workflow/Workflows.css | 2 +- .../workflows/workflow/Workflows.jsx | 4 +- .../src/layouts/menu-layout/MenuLayout.css | 6 +- .../src/layouts/menu-layout/MenuLayout.jsx | 6 +- .../src/layouts/page-layout/PageLayout.css | 29 +++++++- .../src/layouts/page-layout/PageLayout.jsx | 72 +++++++++++-------- frontend/src/store/socket-logs-store.js | 14 +++- 10 files changed, 97 insertions(+), 44 deletions(-) diff --git a/frontend/src/components/custom-tools/list-of-tools/ListOfTools.css b/frontend/src/components/custom-tools/list-of-tools/ListOfTools.css index 7c7810592..fd2d58be3 100644 --- a/frontend/src/components/custom-tools/list-of-tools/ListOfTools.css +++ b/frontend/src/components/custom-tools/list-of-tools/ListOfTools.css @@ -1,7 +1,7 @@ /* Styles for ListOfTools */ .list-of-tools-layout { - height: 100%; + flex: 1; background-color: var(--page-bg-2); padding: 12px; overflow-y: auto; diff --git a/frontend/src/components/custom-tools/list-of-tools/ListOfTools.jsx b/frontend/src/components/custom-tools/list-of-tools/ListOfTools.jsx index f610b7b5d..6677ac995 100644 --- a/frontend/src/components/custom-tools/list-of-tools/ListOfTools.jsx +++ b/frontend/src/components/custom-tools/list-of-tools/ListOfTools.jsx @@ -275,7 +275,7 @@ function ListOfTools() { }; return ( - <> +
)} - +
); } diff --git a/frontend/src/components/custom-tools/tool-ide/ToolIde.css b/frontend/src/components/custom-tools/tool-ide/ToolIde.css index 94e45ba60..7be1f7c06 100644 --- a/frontend/src/components/custom-tools/tool-ide/ToolIde.css +++ b/frontend/src/components/custom-tools/tool-ide/ToolIde.css @@ -104,7 +104,7 @@ font-size: 12px; } -.public-tool-ide-body{ +.public-tool-ide-body { flex: 1 1; overflow-y: hidden; width: 100vw; diff --git a/frontend/src/components/workflows/workflow/Workflows.css b/frontend/src/components/workflows/workflow/Workflows.css index 22d6ec9e9..f65b11669 100644 --- a/frontend/src/components/workflows/workflow/Workflows.css +++ b/frontend/src/components/workflows/workflow/Workflows.css @@ -16,7 +16,7 @@ .workflows-pg-layout { background-color: var(--page-bg-2); padding: 12px; - height: 100%; + flex: 1; overflow-y: auto; } diff --git a/frontend/src/components/workflows/workflow/Workflows.jsx b/frontend/src/components/workflows/workflow/Workflows.jsx index 08839404f..f2772f4e5 100644 --- a/frontend/src/components/workflows/workflow/Workflows.jsx +++ b/frontend/src/components/workflows/workflow/Workflows.jsx @@ -190,7 +190,7 @@ function Workflows() { }; return ( - <> +
- +
); } diff --git a/frontend/src/layouts/menu-layout/MenuLayout.css b/frontend/src/layouts/menu-layout/MenuLayout.css index 8dab5529a..fc5d9869f 100644 --- a/frontend/src/layouts/menu-layout/MenuLayout.css +++ b/frontend/src/layouts/menu-layout/MenuLayout.css @@ -5,7 +5,7 @@ align-items: center; /* column-gap: 12px; */ height: min-content; - background-color: #F5F7F9; + background-color: #f5f7f9; padding-block: 8px; padding-inline: 12px; } @@ -19,7 +19,7 @@ .appBody { background-color: var(--page-bg-2); overflow: hidden; - height: 100%; + flex: 1; } .appBody2 { @@ -47,7 +47,7 @@ border-radius: 6px; margin: 0 5px; border-inline-start-width: 1px; - position: static!important; + position: static !important; } .radio_btn:hover { border-inline-start-width: 1px; diff --git a/frontend/src/layouts/menu-layout/MenuLayout.jsx b/frontend/src/layouts/menu-layout/MenuLayout.jsx index 893130fde..748b4237c 100644 --- a/frontend/src/layouts/menu-layout/MenuLayout.jsx +++ b/frontend/src/layouts/menu-layout/MenuLayout.jsx @@ -29,7 +29,7 @@ function MenuLayout({ children }) { }, []); return ( - <> +
-
+
{children}
- +
); } diff --git a/frontend/src/layouts/page-layout/PageLayout.css b/frontend/src/layouts/page-layout/PageLayout.css index a3683b12b..d51ef30e1 100644 --- a/frontend/src/layouts/page-layout/PageLayout.css +++ b/frontend/src/layouts/page-layout/PageLayout.css @@ -27,7 +27,7 @@ .log-footer { padding: 0; position: relative; - height: 53px; + height: 50px; } .ide-collapse-panel { @@ -48,3 +48,30 @@ text-align: center; align-items: center; } + +.page-layout-body { + height: 100%; + display: flex; + flex-direction: column; +} + +.page-layout-main { + flex: 1; +} + +@keyframes blink { + 0% { + border-color: #ff4f4f; + } + 50% { + border-color: transparent; + } + 100% { + border-color: #ff4f4f; + } +} + +.blinking-border { + border: 2px solid #ff4f4f; + animation: blink 2s infinite; +} diff --git a/frontend/src/layouts/page-layout/PageLayout.jsx b/frontend/src/layouts/page-layout/PageLayout.jsx index 48b5267b0..ba862832c 100644 --- a/frontend/src/layouts/page-layout/PageLayout.jsx +++ b/frontend/src/layouts/page-layout/PageLayout.jsx @@ -22,11 +22,21 @@ import { useSocketLogsStore } from "../../store/socket-logs-store.js"; function PageLayout() { const [showLogsModal, setShowLogsModal] = useState(false); const [activeKey, setActiveKey] = useState([]); + const [isBlink, setIsBlink] = useState(false); const { height, enableResize, setHeight } = useResize({ minHeight: 50 }); const { sessionDetails } = useSessionStore(); const initialCollapsedValue = JSON.parse(localStorage.getItem("collapsed")) || false; const [collapsed, setCollapsed] = useState(initialCollapsedValue); + const { blink, updateBlink } = useSocketLogsStore(); + + useEffect(() => { + setIsBlink(!activeKey?.length && blink); + + if (activeKey?.length && blink) { + updateBlink(false); + } + }, [activeKey, blink]); const getLogs = async () => { const requestOptions = { @@ -103,7 +113,7 @@ function PageLayout() { - +
diff --git a/frontend/src/store/socket-logs-store.js b/frontend/src/store/socket-logs-store.js index bd6c87343..5b076c11f 100644 --- a/frontend/src/store/socket-logs-store.js +++ b/frontend/src/store/socket-logs-store.js @@ -6,6 +6,7 @@ import { useAlertStore } from "./alert-store"; const STORE_VARIABLES = { logs: [], + blink: false, }; const useSocketLogsStore = create((setState, getState) => ({ @@ -33,11 +34,13 @@ const useSocketLogsStore = create((setState, getState) => ({ }; logsData.push(newLog); + let blink = false; if (newLog?.type === "LOG" && newLog?.level === "ERROR") { setAlertDetails({ type: "ERROR_LOG", message: newLog?.message, }); + blink = true; } if (newLog?.type === "NOTIFICATION" && sessionDetails?.isLoggedIn) { const requestOptions = { @@ -57,8 +60,15 @@ const useSocketLogsStore = create((setState, getState) => ({ logsData = logsData.slice(index); } - existingState.logs = logsData; - setState(existingState); + const result = { + logs: logsData, + blink, + }; + setState({ ...existingState, ...result }); + }, + updateBlink: (value) => { + const existingState = { ...getState() }; + setState({ ...existingState, ...{ blink: value } }); }, emptyLogs: () => { setState({ logs: [] }); From 8ee5c59315f906f933fcd3eda99fa6e2ebe6c434 Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Tue, 30 Jul 2024 20:22:43 +0530 Subject: [PATCH 33/38] Handled default duration of popup message --- frontend/src/hooks/useExceptionHandler.jsx | 2 +- frontend/src/store/alert-store.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/hooks/useExceptionHandler.jsx b/frontend/src/hooks/useExceptionHandler.jsx index 4f7835e54..00366f07e 100644 --- a/frontend/src/hooks/useExceptionHandler.jsx +++ b/frontend/src/hooks/useExceptionHandler.jsx @@ -8,7 +8,7 @@ const useExceptionHandler = () => { errMessage = "Something went wrong", setBackendErrors = undefined, title = "Failed", - duration = 0 + duration = 6 ) => { if (!err) { return { diff --git a/frontend/src/store/alert-store.js b/frontend/src/store/alert-store.js index 95649c773..efcbe4db6 100644 --- a/frontend/src/store/alert-store.js +++ b/frontend/src/store/alert-store.js @@ -3,12 +3,14 @@ import { isNonNegativeNumber } from "../helpers/GetStaticData"; import { useSocketLogsStore } from "../store/socket-logs-store"; import { uniqueId } from "lodash"; +const DEFAULT_DURATION = 6; + const STORE_VARIABLES = { alertDetails: { type: "", content: "", title: "", - duration: undefined, + duration: DEFAULT_DURATION, key: null, }, }; @@ -21,7 +23,7 @@ const useAlertStore = create((setState) => ({ alertDetails: { content: details.message, title: "Failed", - duration: 0, + duration: DEFAULT_DURATION, key: `open${Date.now()}-${uniqueId()}`, type: "error", }, @@ -35,9 +37,7 @@ const useAlertStore = create((setState) => ({ title: details.title || (isErrorType ? "Failed" : "Success"), duration: isNonNegativeNumber(details.duration) ? details.duration - : isErrorType - ? 0 - : undefined, + : DEFAULT_DURATION, key: `open${Date.now()}-${uniqueId()}`, }; From 2de8a68f61fb6674f3328bf5e64bcec39ddd91fd Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Wed, 31 Jul 2024 02:41:02 +0530 Subject: [PATCH 34/38] sonar issue fix --- backend/utils/user_session.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/utils/user_session.py b/backend/utils/user_session.py index e6deb6515..c298b372a 100644 --- a/backend/utils/user_session.py +++ b/backend/utils/user_session.py @@ -29,6 +29,7 @@ def get_user_id(request: HttpRequest) -> Optional[str]: def get_session_id(request: HttpRequest) -> Optional[str]: return request.session.session_key + @staticmethod def set_organization_member_role( request: HttpRequest, member: OrganizationMember ) -> None: From a1be3080a7edfdd343ff08bd0c2a6f25b753abcf Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Wed, 31 Jul 2024 10:33:03 +0530 Subject: [PATCH 35/38] settings changes --- backend/backend/settings/base.py | 2 +- backend/logs_helper/log_service.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/backend/settings/base.py b/backend/backend/settings/base.py index 5b7f41420..3c8edf47f 100644 --- a/backend/backend/settings/base.py +++ b/backend/backend/settings/base.py @@ -164,7 +164,7 @@ def get_required_setting( ) LOGS_BATCH_LIMIT = int(get_required_setting("LOGS_BATCH_LIMIT", "30")) LOGS_EXPIRATION_TIME_IN_SECOND = int( - os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600) + get_required_setting("LOGS_EXPIRATION_TIME_IN_SECOND") ) CELERY_BROKER_URL = get_required_setting( "CELERY_BROKER_URL", f"redis://{REDIS_HOST}:{REDIS_PORT}" diff --git a/backend/logs_helper/log_service.py b/backend/logs_helper/log_service.py index 59c4df4cb..61a4bc797 100644 --- a/backend/logs_helper/log_service.py +++ b/backend/logs_helper/log_service.py @@ -6,7 +6,7 @@ class LogService: def remove_logs_on_logout(session_id: str) -> None: if session_id: - key_pattern = f"logs:{session_id}*" + key_pattern = f"{LogService.generate_redis_key(session_id=session_id)}*" # Delete keys matching the pattern CacheService.clear_cache(key_pattern=key_pattern) From 808c31e2bb6c35608089fbd5bfc91812325e5ffc Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Wed, 31 Jul 2024 11:17:59 +0530 Subject: [PATCH 36/38] code refactor --- frontend/src/helpers/GetStaticData.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/frontend/src/helpers/GetStaticData.js b/frontend/src/helpers/GetStaticData.js index 50907f979..b0824792a 100644 --- a/frontend/src/helpers/GetStaticData.js +++ b/frontend/src/helpers/GetStaticData.js @@ -424,14 +424,13 @@ const convertTimestampToHHMMSS = (timestamp) => { const date = new Date(timestamp * 1000); // Extract hours, minutes, and seconds - const hours = date.getUTCHours().toString().padStart(2, "0"); - const minutes = date.getUTCMinutes().toString().padStart(2, "0"); - const seconds = date.getUTCSeconds().toString().padStart(2, "0"); - - // Combine to form hh:mm:ss format - const timeString = `${hours}:${minutes}:${seconds}`; - - return timeString; + const [hours, minutes, seconds] = [ + date.getUTCHours(), + date.getUTCMinutes(), + date.getUTCSeconds(), + ].map((unit) => unit.toString().padStart(2, "0")); + // Return the formatted time string + return `${hours}:${minutes}:${seconds}`; }; const isSubPage = (type, path) => { From fa633cf04d11e34fa8a50bbe3724079f41ac8f28 Mon Sep 17 00:00:00 2001 From: jagadeeswaran-zipstack Date: Wed, 31 Jul 2024 11:24:24 +0530 Subject: [PATCH 37/38] moved inline style to class --- frontend/src/layouts/page-layout/PageLayout.css | 8 ++++++++ frontend/src/layouts/page-layout/PageLayout.jsx | 8 +------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/frontend/src/layouts/page-layout/PageLayout.css b/frontend/src/layouts/page-layout/PageLayout.css index d51ef30e1..1425999cc 100644 --- a/frontend/src/layouts/page-layout/PageLayout.css +++ b/frontend/src/layouts/page-layout/PageLayout.css @@ -75,3 +75,11 @@ border: 2px solid #ff4f4f; animation: blink 2s infinite; } + +.resize-handle { + position: absolute; + width: 100%; + top: 0; + cursor: row-resize; + height: 10px; +} diff --git a/frontend/src/layouts/page-layout/PageLayout.jsx b/frontend/src/layouts/page-layout/PageLayout.jsx index ba862832c..0bd3194e4 100644 --- a/frontend/src/layouts/page-layout/PageLayout.jsx +++ b/frontend/src/layouts/page-layout/PageLayout.jsx @@ -71,13 +71,7 @@ function PageLayout() { <>