From 047aed266c52d1bd7101082b05524f6e38bae231 Mon Sep 17 00:00:00 2001 From: harini-venkataraman <115449948+harini-venkataraman@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:17:23 +0530 Subject: [PATCH] [FEAT] Prompt Studio Public Share & Clone (#429) * Cloud URLs for public share * Adding public share cloud routes * Public share OSS components * Fixing linting issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Import fixes * Fixing path for FE Plugins * Exposing URLs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Sonar issue fixes * Fixing prettier issues * Fixing prettier issues * Removing console logs * Optimising code block * Fixing Cloud Import URLs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Seperating clone and Share components * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Resolving conflicts * Moving URLs to Cloud * Fixing headers * Fixing linting issues * Disabling Multi LLM runs for Public sharing * Adding URLs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Pre-commit fixes * Adding prompt studio helper * Resolving conflicts * Condition optimization Co-authored-by: jagadeeswaran-zipstack Signed-off-by: harini-venkataraman <115449948+harini-venkataraman@users.noreply.github.com> * Adapter null fixes Signed-off-by: harini-venkataraman <115449948+harini-venkataraman@users.noreply.github.com> * Fixing prompt_id key Signed-off-by: harini-venkataraman <115449948+harini-venkataraman@users.noreply.github.com> * Linting issues * Disabling checkboxes * Fixing sonar issues --------- Signed-off-by: harini-venkataraman <115449948+harini-venkataraman@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: jagadeeswaran-zipstack Co-authored-by: Hari John Kuriakose --- backend/backend/public_urls.py | 9 ++- backend/backend/urls.py | 26 +++++++- .../prompt_studio_helper.py | 8 +++ .../combined-output/CombinedOutput.jsx | 37 ++++++++--- .../custom-tools/combined-output/JsonView.jsx | 2 +- .../custom-synonyms/CustomSynonyms.jsx | 6 +- .../document-manager/DocumentManager.jsx | 18 +++++- .../editable-text/EditableText.jsx | 8 ++- .../custom-tools/header-title/HeaderTitle.css | 12 ++++ .../custom-tools/header-title/HeaderTitle.jsx | 34 ++++++++++ .../components/custom-tools/header/Header.jsx | 63 +++++++++++-------- .../manage-docs-modal/ManageDocsModal.jsx | 13 ++-- .../manage-llm-profiles/ManageLlmProfiles.jsx | 23 +++++-- .../OutputForDocModal.jsx | 18 +++++- .../PreAndPostAmbleModal.jsx | 15 ++++- .../custom-tools/prompt-card/Header.jsx | 14 +++-- .../custom-tools/prompt-card/PromptCard.jsx | 20 +++++- .../prompt-card/PromptCardItems.jsx | 11 +++- .../custom-tools/tool-ide/ToolIde.jsx | 59 ++++++++++++++++- .../helpers/custom-tools/CustomToolsHelper.js | 9 +++ frontend/src/routes/Router.jsx | 21 ++++++- frontend/src/store/custom-tool-store.js | 2 + .../connectors/filesystems/__init__.py | 2 + 23 files changed, 356 insertions(+), 74 deletions(-) create mode 100644 frontend/src/components/custom-tools/header-title/HeaderTitle.css create mode 100644 frontend/src/components/custom-tools/header-title/HeaderTitle.jsx diff --git a/backend/backend/public_urls.py b/backend/backend/public_urls.py index 27ece8786..ffeb5f02e 100644 --- a/backend/backend/public_urls.py +++ b/backend/backend/public_urls.py @@ -22,6 +22,7 @@ path_prefix = settings.PATH_PREFIX api_path_prefix = settings.API_DEPLOYMENT_PATH_PREFIX +share_path_prefix = settings.PUBLIC_PATH_PREFIX urlpatterns = [ path(f"{path_prefix}/", include("account.urls")), @@ -47,10 +48,16 @@ try: - import pluggable_apps.platform_admin.urls # noqa: F401 + import pluggable_apps.platform_admin.urls # noqa # pylint: disable=unused-import + import pluggable_apps.public_shares_share_controller.urls # noqa # pylint: disable=unused-import urlpatterns += [ path(f"{path_prefix}/", include("pluggable_apps.platform_admin.urls")), + # Public Sharing + path( + f"{share_path_prefix}/", + include("pluggable_apps.public_shares.share_controller.urls"), + ), ] except ImportError: pass diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 8b299ecc6..a86a6c7b5 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -84,7 +84,7 @@ # Subscription urls try: - import pluggable_apps.subscription.urls # noqa # pylint: disable=unused-import + import pluggable_apps.subscription.urls # noqa # pylint: disable=unused-import urlpatterns += [ path("", include("pluggable_apps.subscription.urls")), @@ -93,10 +93,32 @@ pass try: - import pluggable_apps.manual_review.urls # noqa: F401 + import pluggable_apps.manual_review.urls # noqa # pylint: disable=unused-import urlpatterns += [ path("manual_review/", include("pluggable_apps.manual_review.urls")), ] except ImportError: pass + +# Public share urls +try: + + import pluggable_apps.public_shares.share_manager.urls # noqa # pylint: disable=unused-import + + urlpatterns += [ + path("", include("pluggable_apps.public_shares.share_manager.urls")), + ] +except ImportError: + pass + +# Clone urls +try: + + import pluggable_apps.clone.urls # noqa # pylint: disable=unused-import + + urlpatterns += [ + path("", include("pluggable_apps.clone.urls")), + ] +except ImportError: + pass diff --git a/backend/prompt_studio/prompt_studio_core/prompt_studio_helper.py b/backend/prompt_studio/prompt_studio_core/prompt_studio_helper.py index 8f07ac336..5750e789c 100644 --- a/backend/prompt_studio/prompt_studio_core/prompt_studio_helper.py +++ b/backend/prompt_studio/prompt_studio_core/prompt_studio_helper.py @@ -970,3 +970,11 @@ def _fetch_single_pass_response( ) output_response = json.loads(answer["structure_output"]) return output_response + + @staticmethod + def get_tool_from_tool_id(tool_id: str) -> Optional[CustomTool]: + try: + tool: CustomTool = CustomTool.objects.get(tool_id=tool_id) + return tool + except CustomTool.DoesNotExist: + return None diff --git a/frontend/src/components/custom-tools/combined-output/CombinedOutput.jsx b/frontend/src/components/custom-tools/combined-output/CombinedOutput.jsx index 2c1f88d01..57d4d08a8 100644 --- a/frontend/src/components/custom-tools/combined-output/CombinedOutput.jsx +++ b/frontend/src/components/custom-tools/combined-output/CombinedOutput.jsx @@ -18,6 +18,7 @@ import { SpinnerLoader } from "../../widgets/spinner-loader/SpinnerLoader"; import "./CombinedOutput.css"; import { useExceptionHandler } from "../../../hooks/useExceptionHandler"; import { JsonView } from "./JsonView"; +import { useParams } from "react-router-dom"; let TableView; let promptOutputApiSps; @@ -29,11 +30,22 @@ try { } catch { // The component will remain null of it is not available } +let publicOutputsDocApi; +let publicAdapterApi; +try { + publicOutputsDocApi = + require("../../../plugins/prompt-studio-public-share/helpers/PublicShareAPIs").publicOutputsDocApi; + publicAdapterApi = + require("../../../plugins/prompt-studio-public-share/helpers/PublicShareAPIs").publicAdapterApi; +} catch { + // The component will remain null of it is not available +} function CombinedOutput({ docId, setFilledFields }) { const [combinedOutput, setCombinedOutput] = useState({}); const [isOutputLoading, setIsOutputLoading] = useState(false); const [adapterData, setAdapterData] = useState([]); const [activeKey, setActiveKey] = useState("0"); + const { id } = useParams(); const { details, defaultLlmProfile, @@ -41,6 +53,7 @@ function CombinedOutput({ docId, setFilledFields }) { isSinglePassExtractLoading, llmProfiles, isSimplePromptStudio, + isPublicSource, } = useCustomToolStore(); const { sessionDetails } = useSessionStore(); const { setAlertDetails } = useAlertStore(); @@ -119,6 +132,13 @@ function CombinedOutput({ docId, setFilledFields }) { let url; if (isSimplePromptStudio) { url = promptOutputApiSps(details?.tool_id, null, docId); + } else if (isPublicSource) { + url = publicOutputsDocApi( + id, + docId, + selectedProfile || defaultLlmProfile, + singlePassExtractMode + ); } else { url = `/api/v1/unstract/${ sessionDetails?.orgId @@ -135,7 +155,6 @@ function CombinedOutput({ docId, setFilledFields }) { "X-CSRFToken": sessionDetails?.csrfToken, }, }; - return axiosPrivate(requestOptions) .then((res) => res) .catch((err) => { @@ -144,14 +163,14 @@ function CombinedOutput({ docId, setFilledFields }) { }; const getAdapterInfo = () => { - axiosPrivate - .get( - `/api/v1/unstract/${sessionDetails?.orgId}/adapter/?adapter_type=LLM` - ) - .then((res) => { - const adapterList = res?.data; - setAdapterData(getLLMModelNamesForProfiles(llmProfiles, adapterList)); - }); + let url = `/api/v1/unstract/${sessionDetails?.orgId}/adapter/?adapter_type=LLM`; + if (isPublicSource) { + url = publicAdapterApi(id, "LLM"); + } + axiosPrivate.get(url).then((res) => { + const adapterList = res?.data; + setAdapterData(getLLMModelNamesForProfiles(llmProfiles, adapterList)); + }); }; if (isOutputLoading) { diff --git a/frontend/src/components/custom-tools/combined-output/JsonView.jsx b/frontend/src/components/custom-tools/combined-output/JsonView.jsx index 61bfc9f46..d78bbe77c 100644 --- a/frontend/src/components/custom-tools/combined-output/JsonView.jsx +++ b/frontend/src/components/custom-tools/combined-output/JsonView.jsx @@ -22,7 +22,7 @@ function JsonView({
}> Default} key={"0"}> - {adapterData.map((adapter, index) => ( + {[...(adapterData || [])].map((adapter, index) => ( {adapter.llm_model}} key={(index + 1)?.toString()} diff --git a/frontend/src/components/custom-tools/custom-synonyms/CustomSynonyms.jsx b/frontend/src/components/custom-tools/custom-synonyms/CustomSynonyms.jsx index 2a85e4cb1..e6422218b 100644 --- a/frontend/src/components/custom-tools/custom-synonyms/CustomSynonyms.jsx +++ b/frontend/src/components/custom-tools/custom-synonyms/CustomSynonyms.jsx @@ -37,7 +37,7 @@ function CustomSynonyms() { const [rows, setRows] = useState([]); const [isLoading, setIsLoading] = useState(false); const { sessionDetails } = useSessionStore(); - const { details, updateCustomTool } = useCustomToolStore(); + const { details, updateCustomTool, isPublicSource } = useCustomToolStore(); const { setAlertDetails } = useAlertStore(); const axiosPrivate = useAxiosPrivate(); const handleException = useExceptionHandler(); @@ -96,7 +96,7 @@ function CustomSynonyms() { handleConfirm={() => handleDelete(index)} content="The word, along with its corresponding synonyms, will be permanently deleted." > - @@ -213,6 +213,7 @@ function CustomSynonyms() { type="primary" icon={} onClick={handleAddRow} + disabled={isPublicSource} > Rows @@ -223,6 +224,7 @@ function CustomSynonyms() { type="primary" onClick={handleSave} loading={isLoading} + disabled={isPublicSource} > Save diff --git a/frontend/src/components/custom-tools/document-manager/DocumentManager.jsx b/frontend/src/components/custom-tools/document-manager/DocumentManager.jsx index 249a34edd..0b2a27fa3 100644 --- a/frontend/src/components/custom-tools/document-manager/DocumentManager.jsx +++ b/frontend/src/components/custom-tools/document-manager/DocumentManager.jsx @@ -21,6 +21,7 @@ import { ManageDocsModal } from "../manage-docs-modal/ManageDocsModal"; import { PdfViewer } from "../pdf-viewer/PdfViewer"; import { TextViewerPre } from "../text-viewer-pre/TextViewerPre"; import usePostHogEvents from "../../../hooks/usePostHogEvents"; +import { useParams } from "react-router-dom"; const items = [ { @@ -63,7 +64,13 @@ try { } catch { // The component will remain null of it is not available } - +let publicDocumentApi; +try { + publicDocumentApi = + require("../../../plugins/prompt-studio-public-share/helpers/PublicShareAPIs").publicDocumentApi; +} catch { + // The component will remain null of it is not available +} function DocumentManager({ generateIndex, handleUpdateTool, handleDocChange }) { const [openManageDocsModal, setOpenManageDocsModal] = useState(false); const [page, setPage] = useState(1); @@ -85,10 +92,12 @@ function DocumentManager({ generateIndex, handleUpdateTool, handleDocChange }) { indexDocs, isSinglePassExtractLoading, isSimplePromptStudio, + isPublicSource, } = useCustomToolStore(); const { sessionDetails } = useSessionStore(); const axiosPrivate = useAxiosPrivate(); const { setPostHogCustomEvent } = usePostHogEvents(); + const { id } = useParams(); useEffect(() => { if (isSimplePromptStudio) { @@ -186,11 +195,14 @@ function DocumentManager({ generateIndex, handleUpdateTool, handleDocChange }) { }; const getDocuments = async (viewType) => { + let url = `/api/v1/unstract/${sessionDetails?.orgId}/prompt-studio/file/${details?.tool_id}?document_id=${selectedDoc?.document_id}&view_type=${viewType}`; + if (isPublicSource) { + url = publicDocumentApi(id, selectedDoc?.document_id, viewType); + } const requestOptions = { + url, method: "GET", - url: `/api/v1/unstract/${sessionDetails?.orgId}/prompt-studio/file/${details?.tool_id}?document_id=${selectedDoc?.document_id}&view_type=${viewType}`, }; - return axiosPrivate(requestOptions) .then((res) => res) .catch((err) => { diff --git a/frontend/src/components/custom-tools/editable-text/EditableText.jsx b/frontend/src/components/custom-tools/editable-text/EditableText.jsx index 2344429e5..37c2c81bd 100644 --- a/frontend/src/components/custom-tools/editable-text/EditableText.jsx +++ b/frontend/src/components/custom-tools/editable-text/EditableText.jsx @@ -25,6 +25,7 @@ function EditableText({ indexDocs, selectedDoc, isSinglePassExtractLoading, + isPublicSource, } = useCustomToolStore(); useEffect(() => { @@ -90,7 +91,9 @@ function EditableText({ onBlur={handleBlur} onClick={() => setIsEditing(true)} disabled={ - disableLlmOrDocChange.includes(promptId) || isSinglePassExtractLoading + disableLlmOrDocChange.includes(promptId) || + isSinglePassExtractLoading || + isPublicSource } /> ); @@ -114,7 +117,8 @@ function EditableText({ disabled={ disableLlmOrDocChange.includes(promptId) || indexDocs.includes(selectedDoc?.document_id) || - isSinglePassExtractLoading + isSinglePassExtractLoading || + isPublicSource } /> ); diff --git a/frontend/src/components/custom-tools/header-title/HeaderTitle.css b/frontend/src/components/custom-tools/header-title/HeaderTitle.css new file mode 100644 index 000000000..919146393 --- /dev/null +++ b/frontend/src/components/custom-tools/header-title/HeaderTitle.css @@ -0,0 +1,12 @@ +.custom-tools-name { + padding: 0px 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-left:auto; +} +.custom-tools-header { + display: flex; + justify-content: space-between; + margin-right: auto; +} diff --git a/frontend/src/components/custom-tools/header-title/HeaderTitle.jsx b/frontend/src/components/custom-tools/header-title/HeaderTitle.jsx new file mode 100644 index 000000000..d5db9cab6 --- /dev/null +++ b/frontend/src/components/custom-tools/header-title/HeaderTitle.jsx @@ -0,0 +1,34 @@ +import { ArrowLeftOutlined, EditOutlined } from "@ant-design/icons"; +import { Button, Typography } from "antd"; +import { useNavigate } from "react-router-dom"; + +import { useCustomToolStore } from "../../../store/custom-tool-store"; +import { useSessionStore } from "../../../store/session-store"; +import "./HeaderTitle.css"; + +function HeaderTitle() { + const navigate = useNavigate(); + const { details } = useCustomToolStore(); + const { sessionDetails } = useSessionStore(); + + return ( +
+
+ +
+
+ {details?.tool_name} + +
+
+ ); +} +export { HeaderTitle }; diff --git a/frontend/src/components/custom-tools/header/Header.jsx b/frontend/src/components/custom-tools/header/Header.jsx index 8cdf6bc56..da24362d3 100644 --- a/frontend/src/components/custom-tools/header/Header.jsx +++ b/frontend/src/components/custom-tools/header/Header.jsx @@ -1,14 +1,9 @@ -import { - ArrowLeftOutlined, - EditOutlined, - SettingOutlined, -} from "@ant-design/icons"; +import { SettingOutlined } from "@ant-design/icons"; import { Button, Tooltip, Typography } from "antd"; import PropTypes from "prop-types"; import { useState } from "react"; -import { useNavigate } from "react-router-dom"; -import "./Header.css"; +import { HeaderTitle } from "../header-title/HeaderTitle.jsx"; import { ExportToolIcon } from "../../../assets"; import { useAxiosPrivate } from "../../../hooks/useAxiosPrivate"; import { useExceptionHandler } from "../../../hooks/useExceptionHandler"; @@ -18,21 +13,36 @@ import { useSessionStore } from "../../../store/session-store"; import { CustomButton } from "../../widgets/custom-button/CustomButton"; import { ExportTool } from "../export-tool/ExportTool"; import usePostHogEvents from "../../../hooks/usePostHogEvents"; +import "./Header.css"; let SinglePassToggleSwitch; +let CloneButton; +let PromptShareButton; try { SinglePassToggleSwitch = require("../../../plugins/single-pass-toggle-switch/SinglePassToggleSwitch").SinglePassToggleSwitch; } catch { // The variable will remain undefined if the component is not available. } -function Header({ setOpenSettings, handleUpdateTool }) { +try { + PromptShareButton = + require("../../../plugins/prompt-studio-public-share/public-share-btn/PromptShareButton.jsx").PromptShareButton; + CloneButton = + require("../../../plugins/prompt-studio-clone/clone-btn/CloneButton.jsx").CloneButton; +} catch { + // The variable will remain undefined if the component is not available. +} +function Header({ + setOpenSettings, + handleUpdateTool, + setOpenShareModal, + setOpenCloneModal, +}) { const [isExportLoading, setIsExportLoading] = useState(false); - const { details } = useCustomToolStore(); + const { details, isPublicSource } = useCustomToolStore(); const { sessionDetails } = useSessionStore(); const { setAlertDetails } = useAlertStore(); const axiosPrivate = useAxiosPrivate(); - const navigate = useNavigate(); const handleException = useExceptionHandler(); const [userList, setUserList] = useState([]); const [openExportToolModal, setOpenExportToolModal] = useState(false); @@ -138,23 +148,15 @@ function Header({ setOpenSettings, handleUpdateTool }) { return (
-
- -
-
- {details?.tool_name} -
-
- -
+ {isPublicSource ? ( +
+ + {details?.tool_name} + +
+ ) : ( + + )}
{SinglePassToggleSwitch && ( @@ -167,6 +169,10 @@ function Header({ setOpenSettings, handleUpdateTool }) { />
+ {CloneButton && } + {PromptShareButton && ( + + )}
@@ -174,6 +180,7 @@ function Header({ setOpenSettings, handleUpdateTool }) { type="primary" onClick={() => handleShare(true)} loading={isExportLoading} + disabled={isPublicSource} > @@ -195,6 +202,8 @@ function Header({ setOpenSettings, handleUpdateTool }) { Header.propTypes = { setOpenSettings: PropTypes.func.isRequired, handleUpdateTool: PropTypes.func.isRequired, + setOpenCloneModal: PropTypes.func.isRequired, + setOpenShareModal: PropTypes.func.isRequired, }; export { Header }; 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..49ff09bbf 100644 --- a/frontend/src/components/custom-tools/manage-docs-modal/ManageDocsModal.jsx +++ b/frontend/src/components/custom-tools/manage-docs-modal/ManageDocsModal.jsx @@ -75,6 +75,7 @@ function ManageDocsModal({ rawIndexStatus, summarizeIndexStatus, isSinglePassExtractLoading, + isPublicSource, } = useCustomToolStore(); const { messages } = useSocketCustomToolStore(); const axiosPrivate = useAxiosPrivate(); @@ -137,10 +138,9 @@ function ManageDocsModal({ }, [defaultLlmProfile, details]); useEffect(() => { - if (!open) { + if (!open || isPublicSource) { return; } - handleGetIndexStatus(rawLlmProfile, indexTypes.raw); }, [indexDocs, rawLlmProfile, open]); @@ -384,7 +384,8 @@ function ManageDocsModal({ isSinglePassExtractLoading || indexDocs.includes(item?.document_id) || isUploading || - !defaultLlmProfile + !defaultLlmProfile || + isPublicSource } /> @@ -408,7 +409,8 @@ function ManageDocsModal({ disableLlmOrDocChange?.length > 0 || isSinglePassExtractLoading || indexDocs.includes(item?.document_id) || - isUploading + isUploading || + isPublicSource } > @@ -423,7 +425,8 @@ function ManageDocsModal({ disabled={ disableLlmOrDocChange?.length > 0 || isSinglePassExtractLoading || - indexDocs.includes(item?.document_id) + indexDocs.includes(item?.document_id) || + isPublicSource } /> ), diff --git a/frontend/src/components/custom-tools/manage-llm-profiles/ManageLlmProfiles.jsx b/frontend/src/components/custom-tools/manage-llm-profiles/ManageLlmProfiles.jsx index c49b40c80..643abe2e3 100644 --- a/frontend/src/components/custom-tools/manage-llm-profiles/ManageLlmProfiles.jsx +++ b/frontend/src/components/custom-tools/manage-llm-profiles/ManageLlmProfiles.jsx @@ -65,8 +65,13 @@ function ManageLlmProfiles() { const [editLlmProfileId, setEditLlmProfileId] = useState(null); const axiosPrivate = useAxiosPrivate(); const { sessionDetails } = useSessionStore(); - const { details, defaultLlmProfile, updateCustomTool, llmProfiles } = - useCustomToolStore(); + const { + details, + defaultLlmProfile, + updateCustomTool, + llmProfiles, + isPublicSource, + } = useCustomToolStore(); const { setAlertDetails } = useAlertStore(); const handleException = useExceptionHandler(); const { setPostHogCustomEvent } = usePostHogEvents(); @@ -125,7 +130,11 @@ function ManageLlmProfiles() { handleConfirm={() => handleDelete(item?.profile_id)} content="The LLM profile will be permanently deleted." > - @@ -134,6 +143,7 @@ function ManageLlmProfiles() {
- + Add New LLM Profile
diff --git a/frontend/src/components/custom-tools/output-for-doc-modal/OutputForDocModal.jsx b/frontend/src/components/custom-tools/output-for-doc-modal/OutputForDocModal.jsx index 69f517f75..0d053e4f3 100644 --- a/frontend/src/components/custom-tools/output-for-doc-modal/OutputForDocModal.jsx +++ b/frontend/src/components/custom-tools/output-for-doc-modal/OutputForDocModal.jsx @@ -6,7 +6,7 @@ import { CloseCircleFilled, InfoCircleFilled, } from "@ant-design/icons"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { useCustomToolStore } from "../../../store/custom-tool-store"; import { useSessionStore } from "../../../store/session-store"; @@ -24,6 +24,13 @@ import { useTokenUsageStore } from "../../../store/token-usage-store"; import TabPane from "antd/es/tabs/TabPane"; import { ProfileInfoBar } from "../profile-info-bar/ProfileInfoBar"; +let publicOutputsApi; +try { + publicOutputsApi = + require("../../../plugins/prompt-studio-public-share/helpers/PublicShareAPIs").publicOutputsApi; +} catch { + // The component will remain null of it is not available +} const columns = [ { title: "Document", @@ -72,11 +79,13 @@ function OutputForDocModal({ disableLlmOrDocChange, singlePassExtractMode, isSinglePassExtractLoading, + isPublicSource, llmProfiles, } = useCustomToolStore(); const { sessionDetails } = useSessionStore(); const axiosPrivate = useAxiosPrivate(); const navigate = useNavigate(); + const { id } = useParams(); const { setAlertDetails } = useAlertStore(); const handleException = useExceptionHandler(); const { tokenUsage } = useTokenUsageStore(); @@ -180,14 +189,17 @@ function OutputForDocModal({ setRows([]); return; } + let url = `/api/v1/unstract/${sessionDetails?.orgId}/prompt-studio/prompt-output/?tool_id=${details?.tool_id}&prompt_id=${promptId}&profile_manager=${profile}&is_single_pass_extract=${singlePassExtractMode}`; + if (isPublicSource) { + url = publicOutputsApi(id, promptId, profile, singlePassExtractMode); + } const requestOptions = { method: "GET", - url: `/api/v1/unstract/${sessionDetails?.orgId}/prompt-studio/prompt-output/?tool_id=${details?.tool_id}&prompt_id=${promptId}&profile_manager=${profile}&is_single_pass_extract=${singlePassExtractMode}`, + url, headers: { "X-CSRFToken": sessionDetails?.csrfToken, }, }; - setIsLoading(true); axiosPrivate(requestOptions) .then((res) => { diff --git a/frontend/src/components/custom-tools/pre-and-post-amble-modal/PreAndPostAmbleModal.jsx b/frontend/src/components/custom-tools/pre-and-post-amble-modal/PreAndPostAmbleModal.jsx index 036b95ddf..4e86c641a 100644 --- a/frontend/src/components/custom-tools/pre-and-post-amble-modal/PreAndPostAmbleModal.jsx +++ b/frontend/src/components/custom-tools/pre-and-post-amble-modal/PreAndPostAmbleModal.jsx @@ -18,7 +18,7 @@ const fieldNames = { function PreAndPostAmbleModal({ type, handleUpdateTool }) { const [title, setTitle] = useState(""); const [text, setText] = useState(""); - const { details, updateCustomTool } = useCustomToolStore(); + const { details, updateCustomTool, isPublicSource } = useCustomToolStore(); const { setAlertDetails } = useAlertStore(); const handleException = useExceptionHandler(); @@ -93,13 +93,22 @@ function PreAndPostAmbleModal({ type, handleUpdateTool }) { value={text} onChange={(e) => setText(e.target.value)} /> -
- + Save diff --git a/frontend/src/components/custom-tools/prompt-card/Header.jsx b/frontend/src/components/custom-tools/prompt-card/Header.jsx index 954b5706d..119576992 100644 --- a/frontend/src/components/custom-tools/prompt-card/Header.jsx +++ b/frontend/src/components/custom-tools/prompt-card/Header.jsx @@ -41,6 +41,7 @@ function Header({ singlePassExtractMode, isSinglePassExtractLoading, indexDocs, + isPublicSource, } = useCustomToolStore(); const [isDisablePrompt, setIsDisablePrompt] = useState(promptDetails?.active); @@ -130,7 +131,8 @@ function Header({ disabled={ disableLlmOrDocChange.includes(promptDetails?.prompt_id) || isSinglePassExtractLoading || - indexDocs.includes(selectedDoc?.document_id) + indexDocs.includes(selectedDoc?.document_id) || + isPublicSource } > @@ -151,7 +153,8 @@ function Header({ updateStatus?.status === promptStudioUpdateStatus?.isUpdating) || disableLlmOrDocChange?.includes(promptDetails?.prompt_id) || - indexDocs?.includes(selectedDoc?.document_id) + indexDocs?.includes(selectedDoc?.document_id) || + isPublicSource } > @@ -168,7 +171,8 @@ function Header({ updateStatus?.status === promptStudioUpdateStatus?.isUpdating) || disableLlmOrDocChange?.includes(promptDetails?.prompt_id) || - indexDocs?.includes(selectedDoc?.document_id) + indexDocs?.includes(selectedDoc?.document_id) || + isPublicSource } > @@ -180,6 +184,7 @@ function Header({ checked={isDisablePrompt} className="prompt-card-action-button" onChange={handleDisablePrompt} + disabled={isPublicSource} /> diff --git a/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx b/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx index cb8313163..889a03d9f 100644 --- a/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx +++ b/frontend/src/components/custom-tools/prompt-card/PromptCard.jsx @@ -18,6 +18,7 @@ import useTokenUsage from "../../../hooks/useTokenUsage"; import { useTokenUsageStore } from "../../../store/token-usage-store"; import { PromptCardItems } from "./PromptCardItems"; import "./PromptCard.css"; +import { useParams } from "react-router-dom"; const EvalModal = null; const getEvalMetrics = (param1, param2) => { @@ -34,7 +35,13 @@ try { } catch { // The component will remain null of it is not available } - +let publicOutputsApi; +try { + publicOutputsApi = + require("../../../plugins/prompt-studio-public-share/helpers/PublicShareAPIs").publicOutputsApi; +} catch { + // The component will remain null of it is not available +} function PromptCard({ promptDetails, handleChange, @@ -69,6 +76,7 @@ function PromptCard({ singlePassExtractMode, isSinglePassExtractLoading, isSimplePromptStudio, + isPublicSource, } = useCustomToolStore(); const { messages } = useSocketCustomToolStore(); const { sessionDetails } = useSessionStore(); @@ -78,6 +86,7 @@ function PromptCard({ const { setPostHogCustomEvent } = usePostHogEvents(); const { tokenUsage, setTokenUsage } = useTokenUsageStore(); const { getTokenUsage } = useTokenUsage(); + const { id } = useParams(); useEffect(() => { const outputTypeData = getDropdownItems("output_type") || {}; @@ -642,6 +651,14 @@ function PromptCard({ } url = `/api/v1/unstract/${sessionDetails?.orgId}/prompt-studio/prompt-output/?tool_id=${details?.tool_id}&prompt_id=${promptDetails?.prompt_id}&is_single_pass_extract=${singlePassExtractMode}`; } + if (isPublicSource) { + url = publicOutputsApi( + id, + promptDetails?.prompt_id, + selectedLlmProfileId, + singlePassExtractMode + ); + } if (isOutput) { url += `&document_manager=${selectedDoc?.document_id}`; } @@ -656,7 +673,6 @@ function PromptCard({ "X-CSRFToken": sessionDetails?.csrfToken, }, }; - return axiosPrivate(requestOptions) .then((res) => { const data = res?.data || []; diff --git a/frontend/src/components/custom-tools/prompt-card/PromptCardItems.jsx b/frontend/src/components/custom-tools/prompt-card/PromptCardItems.jsx index e6f800183..dbd2a472c 100644 --- a/frontend/src/components/custom-tools/prompt-card/PromptCardItems.jsx +++ b/frontend/src/components/custom-tools/prompt-card/PromptCardItems.jsx @@ -76,6 +76,7 @@ function PromptCardItems({ isSinglePassExtractLoading, indexDocs, isSimplePromptStudio, + isPublicSource, adapters, } = useCustomToolStore(); const [isEditingPrompt, setIsEditingPrompt] = useState(false); @@ -301,6 +302,7 @@ function PromptCardItems({ type="link" className="display-flex-align-center prompt-card-action-button" onClick={() => setOpenOutputForDoc(true)} + disabled={isPublicSource} > {isCoverageLoading ? ( @@ -331,7 +333,8 @@ function PromptCardItems({ promptDetails?.prompt_id ) || isSinglePassExtractLoading || - indexDocs.includes(selectedDoc?.document_id) + indexDocs.includes(selectedDoc?.document_id) || + isPublicSource } onChange={(value) => handleTypeChange(value)} /> @@ -412,6 +415,7 @@ function PromptCardItems({ onChange={(checked) => handleTagChange(checked, profileId) } + disabled={isPublicSource} className={isChecked ? "checked" : "unchecked"} > {isChecked ? ( @@ -456,6 +460,7 @@ function PromptCardItems({ onChange={() => handleSelectDefaultLLM(profileId) } + disabled={isPublicSource} > Default @@ -504,7 +509,7 @@ function PromptCardItems({ disabled={ isRunLoading[ `${selectedDoc?.document_id}_${profileId}` - ] + ] || isPublicSource } > @@ -519,7 +524,7 @@ function PromptCardItems({ disabled={ isRunLoading[ `${selectedDoc?.document_id}_${profileId}` - ] + ] || isPublicSource } > diff --git a/frontend/src/components/custom-tools/tool-ide/ToolIde.jsx b/frontend/src/components/custom-tools/tool-ide/ToolIde.jsx index 7ade1571c..0dfaae6fd 100644 --- a/frontend/src/components/custom-tools/tool-ide/ToolIde.jsx +++ b/frontend/src/components/custom-tools/tool-ide/ToolIde.jsx @@ -1,6 +1,6 @@ import { FullscreenExitOutlined, FullscreenOutlined } from "@ant-design/icons"; import { Col, Collapse, Modal, Row } from "antd"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useAxiosPrivate } from "../../../hooks/useAxiosPrivate"; import { useExceptionHandler } from "../../../hooks/useExceptionHandler"; @@ -16,8 +16,11 @@ import { SettingsModal } from "../settings-modal/SettingsModal"; import { ToolsMain } from "../tools-main/ToolsMain"; import "./ToolIde.css"; import usePostHogEvents from "../../../hooks/usePostHogEvents.js"; - let OnboardMessagesModal; +let PromptShareModal; +let PromptShareLink; +let CloneTitle; +let HeaderPublic; let slides; try { OnboardMessagesModal = @@ -28,6 +31,18 @@ try { OnboardMessagesModal = null; slides = []; } +try { + PromptShareModal = + require("../../../plugins/prompt-studio-public-share/public-share-modal/PromptShareModal.jsx").PromptShareModal; + PromptShareLink = + require("../../../plugins/prompt-studio-public-share/public-link-modal/PromptShareLink.jsx").PromptShareLink; + CloneTitle = + require("../../../plugins/prompt-studio-clone/clone-title-modal/CloneTitle.jsx").CloneTitle; + HeaderPublic = + require("../../../plugins/prompt-studio-public-share/header-public/HeaderPublic.jsx").HeaderPublic; +} catch (err) { + // Do nothing if plugins are not loaded. +} function ToolIde() { const [showLogsModal, setShowLogsModal] = useState(false); @@ -42,6 +57,8 @@ function ToolIde() { indexDocs, pushIndexDoc, deleteIndexDoc, + shareId, + isPublicSource, } = useCustomToolStore(); const { sessionDetails } = useSessionStore(); const { promptOnboardingMessage } = sessionDetails; @@ -50,6 +67,10 @@ function ToolIde() { const handleException = useExceptionHandler(); const [loginModalOpen, setLoginModalOpen] = useState(true); const { setPostHogCustomEvent } = usePostHogEvents(); + const [openShareLink, setOpenShareLink] = useState(false); + const [openShareConfirmation, setOpenShareConfirmation] = useState(false); + const [openShareModal, setOpenShareModal] = useState(false); + const [openCloneModal, setOpenCloneModal] = useState(false); const openLogsModal = () => { setShowLogsModal(true); @@ -58,6 +79,17 @@ function ToolIde() { const closeLogsModal = () => { setShowLogsModal(false); }; + useEffect(() => { + if (openShareModal) { + if (shareId) { + setOpenShareConfirmation(false); + setOpenShareLink(true); + } else { + setOpenShareConfirmation(true); + setOpenShareLink(false); + } + } + }, [shareId, openShareModal]); const genExtra = () => ( + {isPublicSource && HeaderPublic && }
@@ -244,6 +279,26 @@ function ToolIde() { setOpen={setOpenSettings} handleUpdateTool={handleUpdateTool} /> + {PromptShareModal && ( + + )} + {PromptShareLink && ( + + )} + {CloneTitle && ( + + )} {!promptOnboardingMessage && OnboardMessagesModal && ( { const data = res?.data; updatedCusTool["llmProfiles"] = data; + const reqOpsShare = { + method: "GET", + url: `/api/v1/unstract/${sessionDetails?.orgId}/share-manager/tool-source/?tool_id=${id}`, + }; + return handleApiRequest(reqOpsShare); + }) + .then((res) => { + const data = res?.data; + updatedCusTool["shareId"] = data?.share_id; const reqOpsLlmProfiles = { method: "GET", url: `/api/v1/unstract/${sessionDetails?.orgId}/adapter/`, diff --git a/frontend/src/routes/Router.jsx b/frontend/src/routes/Router.jsx index d564714c0..291c39ec0 100644 --- a/frontend/src/routes/Router.jsx +++ b/frontend/src/routes/Router.jsx @@ -37,6 +37,8 @@ let ChatAppPage; let ChatAppLayout; let ManualReviewPage; let ReviewLayout; +let PublicPromptStudioHelper; + try { TrialRoutes = require("../plugins/subscription/trial-page/TrialEndPage.jsx").TrialEndPage; @@ -84,7 +86,12 @@ try { } catch (err) { // Do nothing, Not-found Page will be triggered. } - +try { + PublicPromptStudioHelper = + require("../plugins/prompt-studio-public-share/helpers/PublicPromptStudioHelper.js").PublicPromptStudioHelper; +} catch (err) { + // Do nothing, Not-found Page will be triggered. +} function Router() { return ( @@ -111,6 +118,18 @@ function Router() { } /> )} + {PublicPromptStudioHelper && ( + } + > + } /> + } + /> + + )} {/* protected routes */} diff --git a/frontend/src/store/custom-tool-store.js b/frontend/src/store/custom-tool-store.js index ba3019fa4..452989545 100644 --- a/frontend/src/store/custom-tool-store.js +++ b/frontend/src/store/custom-tool-store.js @@ -16,6 +16,8 @@ const defaultState = { singlePassExtractMode: false, isSinglePassExtractLoading: false, isSimplePromptStudio: false, + shareId: null, + isPublicSource: false, adapters: [], }; diff --git a/unstract/connectors/src/unstract/connectors/filesystems/__init__.py b/unstract/connectors/src/unstract/connectors/filesystems/__init__.py index 4826bdb25..7635b7ca1 100644 --- a/unstract/connectors/src/unstract/connectors/filesystems/__init__.py +++ b/unstract/connectors/src/unstract/connectors/filesystems/__init__.py @@ -1,5 +1,7 @@ from unstract.connectors import ConnectorDict # type: ignore from unstract.connectors.filesystems.register import register_connectors +from .local_storage.local_storage import * # noqa: F401, F403 + connectors: ConnectorDict = {} register_connectors(connectors)