/* eslint-disable no-console */ import { FeedOptions } from "@azure/cosmos"; import { Callout, CommandBarButton, DefaultButton, DirectionalHint, IButtonStyles, IconButton, Image, Link, Separator, Spinner, Stack, Text, TextField, } from "@fluentui/react"; import { QueryCopilotSampleContainerId, QueryCopilotSampleContainerSchema, QueryCopilotSampleDatabaseId, } from "Common/Constants"; import { getErrorMessage, handleError } from "Common/ErrorHandlingUtils"; import { shouldEnableCrossPartitionKey } from "Common/HeadersUtility"; import { MinimalQueryIterator } from "Common/IteratorUtilities"; import { queryDocuments } from "Common/dataAccess/queryDocuments"; import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage"; import { QueryResults } from "Contracts/ViewModels"; import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent"; import { EditorReact } from "Explorer/Controls/Editor/EditorReact"; import Explorer from "Explorer/Explorer"; import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter"; import { SaveQueryPane } from "Explorer/Panes/SaveQueryPane/SaveQueryPane"; import { submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities"; import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection"; import { userContext } from "UserContext"; import { queryPagesUntilContentPresent } from "Utils/QueryUtils"; import { useQueryCopilot } from "hooks/useQueryCopilot"; import { useSidePanel } from "hooks/useSidePanel"; import React, { useState } from "react"; import SplitterLayout from "react-splitter-layout"; import CopilotIcon from "../../../images/Copilot.svg"; import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg"; import HintIcon from "../../../images/Hint.svg"; import RecentIcon from "../../../images/Recent.svg"; import SaveQueryIcon from "../../../images/save-cosmos.svg"; import { useTabs } from "../../hooks/useTabs"; interface QueryCopilotTabProps { initialInput: string; explorer: Explorer; } interface GenerateSQLQueryResponse { apiVersion: string; sql: string; explanation: string; generateStart: string; generateEnd: string; } const promptStyles: IButtonStyles = { root: { border: 0, selectors: { ":hover": { outline: "1px dashed #605e5c" } } }, label: { fontWeight: 400, textAlign: "left", paddingLeft: 8 }, }; export const QueryCopilotTab: React.FC = ({ initialInput, explorer, }: QueryCopilotTabProps): JSX.Element => { const hideFeedbackModalForLikedQueries = useQueryCopilot((state) => state.hideFeedbackModalForLikedQueries); const [userPrompt, setUserPrompt] = useState(initialInput || ""); const [generatedQuery, setGeneratedQuery] = useState(""); const [query, setQuery] = useState(""); const [selectedQuery, setSelectedQuery] = useState(""); const [isGeneratingQuery, setIsGeneratingQuery] = useState(false); const [isExecuting, setIsExecuting] = useState(false); const [likeQuery, setLikeQuery] = useState(); const [showCallout, setShowCallout] = useState(false); const [showSamplePrompts, setShowSamplePrompts] = useState(false); const [queryIterator, setQueryIterator] = useState(); const [queryResults, setQueryResults] = useState(); const [errorMessage, setErrorMessage] = useState(""); const cachedHistoriesString = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotHistories`); const cachedHistories = cachedHistoriesString?.split(","); const [histories, setHistories] = useState(cachedHistories || []); const updateHistories = (): void => { const newHistories = histories.length < 3 ? [userPrompt, ...histories] : [userPrompt, histories[1], histories[2]]; setHistories(newHistories); localStorage.setItem(`${userContext.databaseAccount.id}-queryCopilotHistories`, newHistories.join(",")); }; const generateSQLQuery = async (): Promise => { try { setIsGeneratingQuery(true); useTabs.getState().setIsTabExecuting(true); const payload = { containerSchema: QueryCopilotSampleContainerSchema, userPrompt: userPrompt, }; const response = await fetch("https://copilotorchestrater.azurewebsites.net/generateSQLQuery", { method: "POST", headers: { "content-type": "application/json", }, body: JSON.stringify(payload), }); const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json(); if (generateSQLQueryResponse?.sql) { let query = `-- **Prompt:** ${userPrompt}\r\n`; if (generateSQLQueryResponse.explanation) { query += `-- **Explanation of query:** ${generateSQLQueryResponse.explanation}\r\n`; } query += generateSQLQueryResponse.sql; setQuery(query); setGeneratedQuery(generateSQLQueryResponse.sql); } } catch (error) { handleError(error, "executeNaturalLanguageQuery"); throw error; } finally { setIsGeneratingQuery(false); useTabs.getState().setIsTabExecuting(false); } }; const onExecuteQueryClick = async (): Promise => { const queryToExecute = selectedQuery || query; const queryIterator = queryDocuments(QueryCopilotSampleDatabaseId, QueryCopilotSampleContainerId, queryToExecute, { enableCrossPartitionQuery: shouldEnableCrossPartitionKey(), } as FeedOptions); setQueryIterator(queryIterator); setTimeout(async () => { await queryDocumentsPerPage(0, queryIterator); }, 100); }; const queryDocumentsPerPage = async (firstItemIndex: number, queryIterator: MinimalQueryIterator): Promise => { try { setIsExecuting(true); useTabs.getState().setIsTabExecuting(true); const queryResults: QueryResults = await queryPagesUntilContentPresent( firstItemIndex, async (firstItemIndex: number) => queryDocumentsPage(QueryCopilotSampleContainerId, queryIterator, firstItemIndex) ); setQueryResults(queryResults); setErrorMessage(""); } catch (error) { const errorMessage = getErrorMessage(error); setErrorMessage(errorMessage); handleError(errorMessage, "executeQueryCopilotTab"); } finally { setIsExecuting(false); useTabs.getState().setIsTabExecuting(false); } }; const getCommandbarButtons = (): CommandButtonComponentProps[] => { const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query"; const executeQueryBtn = { iconSrc: ExecuteQueryIcon, iconAlt: executeQueryBtnLabel, onCommandClick: () => onExecuteQueryClick(), commandButtonLabel: executeQueryBtnLabel, ariaLabel: executeQueryBtnLabel, hasPopup: false, }; const saveQueryBtn = { iconSrc: SaveQueryIcon, iconAlt: "Save Query", onCommandClick: () => useSidePanel.getState().openSidePanel("Save Query", ), commandButtonLabel: "Save Query", ariaLabel: "Save Query", hasPopup: false, }; return [executeQueryBtn, saveQueryBtn]; }; React.useEffect(() => { useCommandBar.getState().setContextButtons(getCommandbarButtons()); }, [query, selectedQuery]); return ( Copilot setUserPrompt(newValue)} style={{ lineHeight: 30 }} styles={{ root: { width: "95%" } }} disabled={isGeneratingQuery} onClick={() => setShowSamplePrompts(true)} /> { updateHistories(); generateSQLQuery(); }} /> {isGeneratingQuery && } {showSamplePrompts && ( setShowSamplePrompts(false)} directionalHint={DirectionalHint.bottomLeftEdge} > {histories?.length > 0 && ( Recent {histories.map((history, i) => ( { setUserPrompt(history); setShowSamplePrompts(false); }} onRenderIcon={() => } styles={promptStyles} > {history} ))} )} Suggested Prompts { setUserPrompt("Give me all customers whose names start with C"); setShowSamplePrompts(false); }} onRenderIcon={() => } styles={promptStyles} > Give me all customers whose names start with C { setUserPrompt("Show me all customers"); setShowSamplePrompts(false); }} onRenderIcon={() => } styles={promptStyles} > Show me all customers { setUserPrompt("Show me all customers who bought a bike in 2019"); setShowSamplePrompts(false); }} onRenderIcon={() => } styles={promptStyles} > Show me all customers who bought a bike in 2019 Learn about{" "} writing effective prompts )} AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "} Read preview terms Provide feedback on the query generated {showCallout && !hideFeedbackModalForLikedQueries && ( { setShowCallout(false); submitFeedback({ generatedQuery, likeQuery, description: "", userPrompt: userPrompt }); }} directionalHint={DirectionalHint.topCenter} > Thank you. Need to give{" "} { setShowCallout(false); useQueryCopilot.getState().openFeedbackModal(generatedQuery, true, userPrompt); }} > more feedback? )} { setLikeQuery(true); setShowCallout(true); }} /> { setLikeQuery(false); setShowCallout(false); useQueryCopilot.getState().openFeedbackModal(generatedQuery, false, userPrompt); }} /> Copy code Delete code setQuery(newQuery)} onContentSelected={(selectedQuery: string) => setSelectedQuery(selectedQuery)} /> queryDocumentsPerPage(firstItemIndex, queryIterator)} /> ); };