diff --git a/backend/backend/public_urls.py b/backend/backend/public_urls.py index 0afee2ead..971ccfcec 100644 --- a/backend/backend/public_urls.py +++ b/backend/backend/public_urls.py @@ -54,6 +54,34 @@ except ImportError: pass +try: + import pluggable_apps.simple_prompt_studio.sps_document.urls # noqa # pylint: disable=unused-import + import pluggable_apps.simple_prompt_studio.sps_project.urls # noqa # pylint: disable=unused-import + import pluggable_apps.simple_prompt_studio.sps_prompt.urls # noqa # pylint: disable=unused-import + import pluggable_apps.simple_prompt_studio.sps_prompt_output.urls # noqa # pylint: disable=unused-import + + simple_prompt_studio_path_prefix = settings.SIMPLE_PROMPT_STUDIO_PATH_PREFIX + urlpatterns += [ + path( + f"{path_prefix}/{simple_prompt_studio_path_prefix}/", + include("pluggable_apps.simple_prompt_studio.sps_project.urls"), + ), + path( + f"{path_prefix}/{simple_prompt_studio_path_prefix}/", + include("pluggable_apps.simple_prompt_studio.sps_document.urls"), + ), + path( + f"{path_prefix}/{simple_prompt_studio_path_prefix}/", + include("pluggable_apps.simple_prompt_studio.sps_prompt.urls"), + ), + path( + f"{path_prefix}/{simple_prompt_studio_path_prefix}/", + include("pluggable_apps.simple_prompt_studio.sps_prompt_output.urls"), + ), + ] +except ImportError: + pass + try: import pluggable_apps.public_shares.share_controller.urls # noqa # pylint: disable=unused-import diff --git a/backend/prompt_studio/prompt_studio/controller.py b/backend/prompt_studio/prompt_studio/controller.py index 4a0280200..e3de0dd30 100644 --- a/backend/prompt_studio/prompt_studio/controller.py +++ b/backend/prompt_studio/prompt_studio/controller.py @@ -1,5 +1,6 @@ import logging +from django.db import models from prompt_studio.prompt_studio.constants import ToolStudioPromptKeys from prompt_studio.prompt_studio.helper import PromptStudioHelper from prompt_studio.prompt_studio.models import ToolStudioPrompt @@ -12,7 +13,7 @@ class PromptStudioController: - def reorder_prompts(self, request: Request) -> Response: + def reorder_prompts(self, request: Request, prompt_model: models.Model) -> Response: """Reorder the sequence of prompts based on the start and end sequence numbers. @@ -46,6 +47,7 @@ def reorder_prompts(self, request: Request) -> Response: prompt_id=prompt_id, start_sequence_number=start_sequence_number, end_sequence_number=end_sequence_number, + prompt_model=prompt_model, ) logger.info("Re-ordering completed successfully.") diff --git a/backend/prompt_studio/prompt_studio/helper.py b/backend/prompt_studio/prompt_studio/helper.py index f362104c2..8d70802f2 100644 --- a/backend/prompt_studio/prompt_studio/helper.py +++ b/backend/prompt_studio/prompt_studio/helper.py @@ -1,6 +1,6 @@ import logging -from prompt_studio.prompt_studio.models import ToolStudioPrompt +from django.db import models logger = logging.getLogger(__name__) @@ -8,7 +8,10 @@ class PromptStudioHelper: @staticmethod def reorder_prompts_helper( - prompt_id: str, start_sequence_number: int, end_sequence_number: int + prompt_id: str, + start_sequence_number: int, + end_sequence_number: int, + prompt_model: models.Model, ) -> list[dict[str, int]]: """Helper method to reorder prompts based on sequence numbers. @@ -16,17 +19,18 @@ def reorder_prompts_helper( prompt_id (str): The ID of the prompt to be reordered. start_sequence_number (int): The initial sequence number of the prompt. end_sequence_number (int): The new sequence number of the prompt. + is_sps (bool): Flag to determine the prompt model to use. Returns: list[dict[str, int]]: A list of updated prompt data with their IDs and new sequence numbers. """ - prompt_instance: ToolStudioPrompt = ToolStudioPrompt.objects.get(pk=prompt_id) - filtered_prompts_data = [] + + prompt_instance = prompt_model.objects.get(pk=prompt_id) tool_id = prompt_instance.tool_id - # Determine the direction of sequence adjustment based on start - # and end sequence numbers + # Determine the direction of sequence adjustment based on start and + # end sequence numbers if start_sequence_number < end_sequence_number: logger.info( "Start sequence number is less than end sequence number. " @@ -38,7 +42,6 @@ def reorder_prompts_helper( "tool_id": tool_id, } increment = False - elif start_sequence_number > end_sequence_number: logger.info( "Start sequence number is greater than end sequence number. " @@ -51,9 +54,9 @@ def reorder_prompts_helper( } increment = True - # Call helper method to update sequence numbers and get filtered prompt data + # Update sequence numbers and get filtered prompt data filtered_prompts_data = PromptStudioHelper.update_sequence_numbers( - filters, increment + filters, increment, prompt_model ) # Update the sequence number of the moved prompt @@ -71,7 +74,9 @@ def reorder_prompts_helper( return filtered_prompts_data @staticmethod - def update_sequence_numbers(filters: dict, increment: bool) -> list[dict[str, int]]: + def update_sequence_numbers( + filters: dict, increment: bool, prompt_model: models.Model + ) -> list[dict[str, int]]: """Update the sequence numbers for prompts based on the provided filters and increment flag. @@ -79,23 +84,21 @@ def update_sequence_numbers(filters: dict, increment: bool) -> list[dict[str, in filters (dict): The filter criteria for selecting prompts. increment (bool): Whether to increment (True) or decrement (False) the sequence numbers. + prompt_model: The model class for the prompts + (either ToolStudioPrompt or SPSPrompt). Returns: list[dict[str, int]]: A list of updated prompt data with their IDs and new sequence numbers. """ - # Filter prompts based on the provided filters - filtered_prompts = ToolStudioPrompt.objects.filter(**filters) + filtered_prompts = prompt_model.objects.filter(**filters) # List to hold updated prompt data filtered_prompts_data = [] # Prepare updates and collect data for prompt in filtered_prompts: - if increment: - prompt.sequence_number += 1 - else: - prompt.sequence_number -= 1 + prompt.sequence_number += 1 if increment else -1 # Append prompt data to the list filtered_prompts_data.append( @@ -106,6 +109,6 @@ def update_sequence_numbers(filters: dict, increment: bool) -> list[dict[str, in ) # Bulk update the sequence numbers - ToolStudioPrompt.objects.bulk_update(filtered_prompts, ["sequence_number"]) + prompt_model.objects.bulk_update(filtered_prompts, ["sequence_number"]) return filtered_prompts_data diff --git a/backend/prompt_studio/prompt_studio/urls.py b/backend/prompt_studio/prompt_studio/urls.py index 3d8c3d74c..23e5f0243 100644 --- a/backend/prompt_studio/prompt_studio/urls.py +++ b/backend/prompt_studio/prompt_studio/urls.py @@ -22,7 +22,7 @@ name="tool-studio-prompt-detail", ), path( - "prompt/reorder", + "prompt/reorder/", reorder_prompts, name="reorder_prompts", ), diff --git a/backend/prompt_studio/prompt_studio/views.py b/backend/prompt_studio/prompt_studio/views.py index ef68a13a5..a7d734f94 100644 --- a/backend/prompt_studio/prompt_studio/views.py +++ b/backend/prompt_studio/prompt_studio/views.py @@ -53,4 +53,4 @@ def reorder_prompts(self, request: Request) -> Response: Response: The HTTP response indicating the status of the reorder operation. """ prompt_studio_controller = PromptStudioController() - return prompt_studio_controller.reorder_prompts(request) + return prompt_studio_controller.reorder_prompts(request, ToolStudioPrompt) diff --git a/docker/dockerfiles/backend.Dockerfile b/docker/dockerfiles/backend.Dockerfile index 340c02a10..2522113c8 100644 --- a/docker/dockerfiles/backend.Dockerfile +++ b/docker/dockerfiles/backend.Dockerfile @@ -37,7 +37,7 @@ COPY ${BUILD_PACKAGES_PATH}/ /unstract RUN set -e; \ \ - rm -rf .venv .pdm* .python* requirements.txt 2>/dev/null; \ + rm -rf .venv .pdm* .python* 2>/dev/null; \ \ pdm venv create -w virtualenv --with-pip; \ # source command may not be availble in sh diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d7661da1f..d2c4d022f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -43,6 +43,7 @@ "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", + "react-google-recaptcha": "^3.1.0", "react-gtm-module": "^2.0.11", "react-js-cron": "^5.0.1", "react-product-fruits": "^2.2.6", @@ -17031,6 +17032,18 @@ "node": ">=14" } }, + "node_modules/react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -17127,6 +17140,18 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-google-recaptcha": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz", + "integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==", + "dependencies": { + "prop-types": "^15.5.0", + "react-async-script": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-gtm-module": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/react-gtm-module/-/react-gtm-module-2.0.11.tgz", @@ -32660,6 +32685,15 @@ "whatwg-fetch": "^3.6.2" } }, + "react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "requires": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + } + }, "react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -32732,6 +32766,15 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "react-google-recaptcha": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz", + "integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==", + "requires": { + "prop-types": "^15.5.0", + "react-async-script": "^1.2.0" + } + }, "react-gtm-module": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/react-gtm-module/-/react-gtm-module-2.0.11.tgz", diff --git a/frontend/package.json b/frontend/package.json index 80516d443..9b2e384cd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,6 +38,7 @@ "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", + "react-google-recaptcha": "^3.1.0", "react-gtm-module": "^2.0.11", "react-js-cron": "^5.0.1", "react-product-fruits": "^2.2.6", diff --git a/frontend/src/components/custom-tools/document-manager/DocumentManager.jsx b/frontend/src/components/custom-tools/document-manager/DocumentManager.jsx index 0b2a27fa3..a7e8c9ad2 100644 --- a/frontend/src/components/custom-tools/document-manager/DocumentManager.jsx +++ b/frontend/src/components/custom-tools/document-manager/DocumentManager.jsx @@ -21,9 +21,8 @@ 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 = [ +let items = [ { key: "1", label: "PDF View", @@ -97,26 +96,27 @@ function DocumentManager({ generateIndex, handleUpdateTool, handleDocChange }) { const { sessionDetails } = useSessionStore(); const axiosPrivate = useAxiosPrivate(); const { setPostHogCustomEvent } = usePostHogEvents(); - const { id } = useParams(); useEffect(() => { if (isSimplePromptStudio) { - items[0] = { - key: "1", - label: ( - - - - ), - }; - items[1] = { - key: "2", - label: ( - - - - ), - }; + items = [ + { + key: "1", + label: ( + + + + ), + }, + { + key: "2", + label: ( + + + + ), + }, + ]; } }, []); @@ -181,7 +181,7 @@ function DocumentManager({ generateIndex, handleUpdateTool, handleDocChange }) { }; const handleGetDocumentsReq = (getDocsFunc, viewType) => { - getDocsFunc(viewType) + getDocsFunc(details?.tool_id, selectedDoc?.document_id, viewType) .then((res) => { const data = res?.data?.data || ""; processGetDocsResponse(data, viewType); @@ -194,11 +194,12 @@ 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}`; + const getDocuments = async (toolId, docId, viewType) => { + let url = `/api/v1/unstract/${sessionDetails?.orgId}/prompt-studio/file/${toolId}?document_id=${docId}&view_type=${viewType}`; if (isPublicSource) { - url = publicDocumentApi(id, selectedDoc?.document_id, viewType); + url = publicDocumentApi(toolId, docId, viewType); } + const requestOptions = { url, method: "GET", diff --git a/frontend/src/components/custom-tools/document-parser/DocumentParser.jsx b/frontend/src/components/custom-tools/document-parser/DocumentParser.jsx index 69951a46e..e84e3342c 100644 --- a/frontend/src/components/custom-tools/document-parser/DocumentParser.jsx +++ b/frontend/src/components/custom-tools/document-parser/DocumentParser.jsx @@ -17,9 +17,15 @@ import { useExceptionHandler } from "../../../hooks/useExceptionHandler"; import { PromptDnd } from "../prompt-card/PrompDnd"; let promptPatchApiSps; +let promptReorderApiSps; +let SpsPromptsEmptyState; try { promptPatchApiSps = require("../../../plugins/simple-prompt-studio/helper").promptPatchApiSps; + promptReorderApiSps = + require("../../../plugins/simple-prompt-studio/helper").promptReorderApiSps; + SpsPromptsEmptyState = + require("../../../plugins/simple-prompt-studio/SpsPromptsEmptyState").SpsPromptsEmptyState; } catch { // The component will remain null of it is not available } @@ -193,9 +199,13 @@ function DocumentParser({ }; const handleDelete = (promptId) => { + let url = promptUrl(promptId + "/"); + if (isSimplePromptStudio) { + url = promptPatchApiSps(promptId); + } const requestOptions = { method: "DELETE", - url: promptUrl(promptId + "/"), + url, headers: { "X-CSRFToken": sessionDetails?.csrfToken, }, @@ -242,9 +252,14 @@ function DocumentParser({ prompt_id: details.prompts[startIndex]?.prompt_id, }; + let url = promptUrl("reorder/"); + if (isSimplePromptStudio) { + url = promptReorderApiSps; + } + const requestOptions = { method: "POST", - url: promptUrl("reorder"), + url, headers: { "X-CSRFToken": sessionDetails?.csrfToken, "Content-Type": "application/json", @@ -288,6 +303,10 @@ function DocumentParser({ }; if (!details?.prompts?.length) { + if (isSimplePromptStudio && SpsPromptsEmptyState) { + return ; + } + return ( - {isDisablePrompt ? "Enabled" : "Disabled"} - - ), - key: "enable", - }, - { - label: ( - handleDelete(promptDetails?.prompt_id)} - content="The prompt will be permanently deleted." - > - Delete - - ), - key: "delete", - disabled: - disableLlmOrDocChange?.includes(promptDetails?.prompt_id) || - isSinglePassExtractLoading || - indexDocs?.includes(selectedDoc?.document_id) || - isPublicSource, - }, - ]; + useEffect(() => { + const dropdownItems = [ + { + label: ( + + {isDisablePrompt ? "Enabled" : "Disabled"} + + ), + key: "enable", + }, + { + label: ( + handleDelete(promptDetails?.prompt_id)} + content="The prompt will be permanently deleted." + > + Delete + + ), + key: "delete", + disabled: + disableLlmOrDocChange?.includes(promptDetails?.prompt_id) || + isSinglePassExtractLoading || + indexDocs?.includes(selectedDoc?.document_id) || + isPublicSource, + }, + ]; + if (isSimplePromptStudio) { + dropdownItems.splice(0, 1); + } + + setItems(dropdownItems); + }, []); return ( @@ -147,7 +167,7 @@ function Header({ )} )} - {!singlePassExtractMode && ( + {!singlePassExtractMode && !isSimplePromptStudio && ( <>