[Query Copilot] Allocation of container and copilot request sent to Phoenix endpoint (#1576)
* Switch to tools federation endpoints for query copilot * Remove extra / in url * Initial allocateContainer implementation * Additional feedback modal changes * updated tests * PhoenixClient reverted to previous endpoint * Changes based on PR comments * Update based on tests * updated endpoint * Back to previous implementation and test improve * removing notebook --------- Co-authored-by: Victor Meng <vimeng@microsoft.com> Co-authored-by: Predrag Klepic <v-prklepic@microsoft.com>
This commit is contained in:
parent
19041ffedd
commit
9c1b9e6ff6
|
@ -358,6 +358,7 @@ export enum ContainerStatusType {
|
||||||
|
|
||||||
export enum PoolIdType {
|
export enum PoolIdType {
|
||||||
DefaultPoolId = "default",
|
DefaultPoolId = "default",
|
||||||
|
QueryCopilot = "query-copilot",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EmulatorMasterKey =
|
export const EmulatorMasterKey =
|
||||||
|
|
|
@ -395,7 +395,7 @@ export default class Explorer {
|
||||||
) {
|
) {
|
||||||
const provisionData: IProvisionData = {
|
const provisionData: IProvisionData = {
|
||||||
cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
|
cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
|
||||||
poolId: PoolIdType.DefaultPoolId,
|
poolId: PoolIdType.QueryCopilot,
|
||||||
};
|
};
|
||||||
const connectionStatus: ContainerConnectionInfo = {
|
const connectionStatus: ContainerConnectionInfo = {
|
||||||
status: ConnectionStatusType.Connecting,
|
status: ConnectionStatusType.Connecting,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Checkbox, ChoiceGroup, DefaultButton, IconButton, PrimaryButton, TextField } from "@fluentui/react";
|
import { Checkbox, ChoiceGroup, DefaultButton, IconButton, PrimaryButton, TextField } from "@fluentui/react";
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
||||||
import { submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
import { submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||||
import { getUserEmail } from "Utils/UserUtils";
|
import { getUserEmail } from "Utils/UserUtils";
|
||||||
|
@ -19,14 +20,14 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
it("shoud render and match snapshot", () => {
|
it("shoud render and match snapshot", () => {
|
||||||
useQueryCopilot.getState().openFeedbackModal("test query", false, "test prompt");
|
useQueryCopilot.getState().openFeedbackModal("test query", false, "test prompt");
|
||||||
|
|
||||||
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||||
|
|
||||||
expect(wrapper.props().isOpen).toBeTruthy();
|
expect(wrapper.props().isOpen).toBeTruthy();
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should close on cancel click", () => {
|
it("should close on cancel click", () => {
|
||||||
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||||
|
|
||||||
const cancelButton = wrapper.find(IconButton);
|
const cancelButton = wrapper.find(IconButton);
|
||||||
cancelButton.simulate("click");
|
cancelButton.simulate("click");
|
||||||
|
@ -37,7 +38,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should get user unput", () => {
|
it("should get user unput", () => {
|
||||||
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||||
const testUserInput = "test user input";
|
const testUserInput = "test user input";
|
||||||
|
|
||||||
const userInput = wrapper.find(TextField).first();
|
const userInput = wrapper.find(TextField).first();
|
||||||
|
@ -48,7 +49,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should record user contact choice no", () => {
|
it("should record user contact choice no", () => {
|
||||||
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||||
const contactAllowed = wrapper.find(ChoiceGroup);
|
const contactAllowed = wrapper.find(ChoiceGroup);
|
||||||
|
|
||||||
contactAllowed.simulate("change", {}, { key: "no" });
|
contactAllowed.simulate("change", {}, { key: "no" });
|
||||||
|
@ -59,7 +60,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should record user contact choice yes", () => {
|
it("should record user contact choice yes", () => {
|
||||||
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||||
const contactAllowed = wrapper.find(ChoiceGroup);
|
const contactAllowed = wrapper.find(ChoiceGroup);
|
||||||
|
|
||||||
contactAllowed.simulate("change", {}, { key: "yes" });
|
contactAllowed.simulate("change", {}, { key: "yes" });
|
||||||
|
@ -70,7 +71,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not render dont show again button", () => {
|
it("should not render dont show again button", () => {
|
||||||
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||||
|
|
||||||
const dontShowAgain = wrapper.find(Checkbox);
|
const dontShowAgain = wrapper.find(Checkbox);
|
||||||
|
|
||||||
|
@ -80,7 +81,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
|
|
||||||
it("should render dont show again button and check it ", () => {
|
it("should render dont show again button and check it ", () => {
|
||||||
useQueryCopilot.getState().openFeedbackModal("test query", true, "test prompt");
|
useQueryCopilot.getState().openFeedbackModal("test query", true, "test prompt");
|
||||||
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||||
|
|
||||||
const dontShowAgain = wrapper.find(Checkbox);
|
const dontShowAgain = wrapper.find(Checkbox);
|
||||||
dontShowAgain.simulate("change", {}, true);
|
dontShowAgain.simulate("change", {}, true);
|
||||||
|
@ -91,7 +92,7 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should cancel submission", () => {
|
it("should cancel submission", () => {
|
||||||
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={new Explorer()} />);
|
||||||
|
|
||||||
const cancelButton = wrapper.find(DefaultButton);
|
const cancelButton = wrapper.find(DefaultButton);
|
||||||
cancelButton.simulate("click");
|
cancelButton.simulate("click");
|
||||||
|
@ -102,7 +103,8 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should submit submission", () => {
|
it("should submit submission", () => {
|
||||||
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
const explorer = new Explorer();
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal explorer={explorer} />);
|
||||||
|
|
||||||
const submitButton = wrapper.find(PrimaryButton);
|
const submitButton = wrapper.find(PrimaryButton);
|
||||||
submitButton.simulate("click");
|
submitButton.simulate("click");
|
||||||
|
@ -110,11 +112,14 @@ describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
|
|
||||||
expect(submitFeedback).toHaveBeenCalledTimes(1);
|
expect(submitFeedback).toHaveBeenCalledTimes(1);
|
||||||
expect(submitFeedback).toHaveBeenCalledWith({
|
expect(submitFeedback).toHaveBeenCalledWith({
|
||||||
likeQuery: false,
|
params: {
|
||||||
generatedQuery: "",
|
likeQuery: false,
|
||||||
userPrompt: "",
|
generatedQuery: "",
|
||||||
description: "",
|
userPrompt: "",
|
||||||
contact: getUserEmail(),
|
description: "",
|
||||||
|
contact: getUserEmail(),
|
||||||
|
},
|
||||||
|
explorer: explorer,
|
||||||
});
|
});
|
||||||
expect(wrapper.props().isOpen).toBeFalsy();
|
expect(wrapper.props().isOpen).toBeFalsy();
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
|
|
@ -10,12 +10,13 @@ import {
|
||||||
Text,
|
Text,
|
||||||
TextField,
|
TextField,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
import { submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
import { submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { getUserEmail } from "../../../Utils/UserUtils";
|
import { getUserEmail } from "../../../Utils/UserUtils";
|
||||||
|
|
||||||
export const QueryCopilotFeedbackModal: React.FC = (): JSX.Element => {
|
export const QueryCopilotFeedbackModal = ({ explorer }: { explorer: Explorer }): JSX.Element => {
|
||||||
const {
|
const {
|
||||||
generatedQuery,
|
generatedQuery,
|
||||||
userPrompt,
|
userPrompt,
|
||||||
|
@ -100,7 +101,10 @@ export const QueryCopilotFeedbackModal: React.FC = (): JSX.Element => {
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
closeFeedbackModal();
|
closeFeedbackModal();
|
||||||
setHideFeedbackModalForLikedQueries(doNotShowAgainChecked);
|
setHideFeedbackModalForLikedQueries(doNotShowAgainChecked);
|
||||||
submitFeedback({ generatedQuery, likeQuery, description, userPrompt, contact });
|
submitFeedback({
|
||||||
|
params: { generatedQuery, likeQuery, description, userPrompt, contact },
|
||||||
|
explorer: explorer,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
|
|
|
@ -21,12 +21,14 @@ import { QueryCopilotSampleContainerId, QueryCopilotSampleContainerSchema } from
|
||||||
import { getErrorMessage, handleError } from "Common/ErrorHandlingUtils";
|
import { getErrorMessage, handleError } from "Common/ErrorHandlingUtils";
|
||||||
import { shouldEnableCrossPartitionKey } from "Common/HeadersUtility";
|
import { shouldEnableCrossPartitionKey } from "Common/HeadersUtility";
|
||||||
import { MinimalQueryIterator } from "Common/IteratorUtilities";
|
import { MinimalQueryIterator } from "Common/IteratorUtilities";
|
||||||
|
import { createUri } from "Common/UrlUtility";
|
||||||
import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage";
|
import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage";
|
||||||
import { QueryResults } from "Contracts/ViewModels";
|
import { QueryResults } from "Contracts/ViewModels";
|
||||||
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
|
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import { SaveQueryPane } from "Explorer/Panes/SaveQueryPane/SaveQueryPane";
|
import { SaveQueryPane } from "Explorer/Panes/SaveQueryPane/SaveQueryPane";
|
||||||
import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
|
import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
|
||||||
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
|
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
|
||||||
|
@ -115,6 +117,8 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({ explorer }: Qu
|
||||||
setShowErrorMessageBar,
|
setShowErrorMessageBar,
|
||||||
generatedQueryComments,
|
generatedQueryComments,
|
||||||
setGeneratedQueryComments,
|
setGeneratedQueryComments,
|
||||||
|
shouldAllocateContainer,
|
||||||
|
setShouldAllocateContainer,
|
||||||
} = useQueryCopilot();
|
} = useQueryCopilot();
|
||||||
|
|
||||||
const sampleProps: SamplePromptsProps = {
|
const sampleProps: SamplePromptsProps = {
|
||||||
|
@ -182,6 +186,11 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({ explorer }: Qu
|
||||||
|
|
||||||
const generateSQLQuery = async (): Promise<void> => {
|
const generateSQLQuery = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
|
if (shouldAllocateContainer) {
|
||||||
|
await explorer.allocateContainer();
|
||||||
|
setShouldAllocateContainer(false);
|
||||||
|
}
|
||||||
|
|
||||||
setIsGeneratingQuery(true);
|
setIsGeneratingQuery(true);
|
||||||
useTabs.getState().setIsTabExecuting(true);
|
useTabs.getState().setIsTabExecuting(true);
|
||||||
useTabs.getState().setIsQueryErrorThrown(false);
|
useTabs.getState().setIsQueryErrorThrown(false);
|
||||||
|
@ -191,7 +200,9 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({ explorer }: Qu
|
||||||
};
|
};
|
||||||
setShowDeletePopup(false);
|
setShowDeletePopup(false);
|
||||||
useQueryCopilot.getState().refreshCorrelationId();
|
useQueryCopilot.getState().refreshCorrelationId();
|
||||||
const response = await fetch("https://copilotorchestrater.azurewebsites.net/generateSQLQuery", {
|
const serverInfo = useNotebook.getState().notebookServerInfo;
|
||||||
|
const queryUri = createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery");
|
||||||
|
const response = await fetch(queryUri, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
|
@ -201,6 +212,9 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({ explorer }: Qu
|
||||||
});
|
});
|
||||||
|
|
||||||
const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json();
|
const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json();
|
||||||
|
if (response.status === 404) {
|
||||||
|
setShouldAllocateContainer(true);
|
||||||
|
}
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
if (generateSQLQueryResponse?.sql) {
|
if (generateSQLQueryResponse?.sql) {
|
||||||
let query = `-- **Prompt:** ${userPrompt}\r\n`;
|
let query = `-- **Prompt:** ${userPrompt}\r\n`;
|
||||||
|
@ -533,10 +547,13 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({ explorer }: Qu
|
||||||
onDismiss={() => {
|
onDismiss={() => {
|
||||||
setShowCallout(false);
|
setShowCallout(false);
|
||||||
submitFeedback({
|
submitFeedback({
|
||||||
generatedQuery: generatedQuery,
|
params: {
|
||||||
likeQuery: likeQuery,
|
generatedQuery: generatedQuery,
|
||||||
description: "",
|
likeQuery: likeQuery,
|
||||||
userPrompt: userPrompt,
|
description: "",
|
||||||
|
userPrompt: userPrompt,
|
||||||
|
},
|
||||||
|
explorer: explorer,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
directionalHint={DirectionalHint.topCenter}
|
directionalHint={DirectionalHint.topCenter}
|
||||||
|
|
|
@ -3,9 +3,11 @@ import { QueryCopilotSampleContainerSchema } from "Common/Constants";
|
||||||
import { handleError } from "Common/ErrorHandlingUtils";
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
import { sampleDataClient } from "Common/SampleDataClient";
|
import { sampleDataClient } from "Common/SampleDataClient";
|
||||||
import * as commonUtils from "Common/dataAccess/queryDocuments";
|
import * as commonUtils from "Common/dataAccess/queryDocuments";
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
|
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import DocumentId from "Explorer/Tree/DocumentId";
|
import DocumentId from "Explorer/Tree/DocumentId";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
|
||||||
import { querySampleDocuments, readSampleDocument, submitFeedback } from "./QueryCopilotUtilities";
|
import { querySampleDocuments, readSampleDocument, submitFeedback } from "./QueryCopilotUtilities";
|
||||||
|
|
||||||
jest.mock("Explorer/Tree/DocumentId", () => {
|
jest.mock("Explorer/Tree/DocumentId", () => {
|
||||||
return jest.fn().mockImplementation(() => {
|
return jest.fn().mockImplementation(() => {
|
||||||
return {
|
return {
|
||||||
|
@ -16,23 +18,21 @@ jest.mock("Explorer/Tree/DocumentId", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mock("Utils/NotificationConsoleUtils", () => ({
|
jest.mock("Utils/NotificationConsoleUtils", () => ({
|
||||||
logConsoleProgress: jest.fn(),
|
logConsoleProgress: jest.fn().mockReturnValue((): void => undefined),
|
||||||
logConsoleError: jest.fn(),
|
logConsoleError: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("@azure/cosmos", () => ({
|
jest.mock("@azure/cosmos", () => ({
|
||||||
FeedOptions: jest.fn(),
|
FeedOptions: jest.fn(),
|
||||||
QueryIterator: jest.fn(),
|
QueryIterator: jest.fn(),
|
||||||
|
Constants: {
|
||||||
|
HttpHeaders: {},
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("Common/ErrorHandlingUtils", () => ({
|
jest.mock("Common/ErrorHandlingUtils", () => ({
|
||||||
handleError: jest.fn(),
|
handleError: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("Utils/NotificationConsoleUtils", () => ({
|
|
||||||
logConsoleProgress: jest.fn().mockReturnValue((): void => undefined),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock("Common/dataAccess/queryDocuments", () => ({
|
jest.mock("Common/dataAccess/queryDocuments", () => ({
|
||||||
getCommonQueryOptions: jest.fn((options) => options),
|
getCommonQueryOptions: jest.fn((options) => options),
|
||||||
}));
|
}));
|
||||||
|
@ -41,8 +41,28 @@ jest.mock("Common/SampleDataClient");
|
||||||
|
|
||||||
jest.mock("node-fetch");
|
jest.mock("node-fetch");
|
||||||
|
|
||||||
|
jest.mock("Explorer/Explorer", () => {
|
||||||
|
class MockExplorer {
|
||||||
|
allocateContainer = jest.fn().mockResolvedValueOnce({});
|
||||||
|
}
|
||||||
|
return MockExplorer;
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock("hooks/useQueryCopilot", () => {
|
||||||
|
const mockQueryCopilotStore = {
|
||||||
|
shouldAllocateContainer: true,
|
||||||
|
setShouldAllocateContainer: jest.fn(),
|
||||||
|
correlationId: "mocked-correlation-id",
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
useQueryCopilot: jest.fn(() => mockQueryCopilotStore),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
describe("QueryCopilotUtilities", () => {
|
describe("QueryCopilotUtilities", () => {
|
||||||
beforeEach(() => jest.clearAllMocks());
|
beforeEach(() => jest.clearAllMocks());
|
||||||
|
|
||||||
describe("submitFeedback", () => {
|
describe("submitFeedback", () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
like: "like",
|
like: "like",
|
||||||
|
@ -53,28 +73,37 @@ describe("QueryCopilotUtilities", () => {
|
||||||
containerSchema: QueryCopilotSampleContainerSchema,
|
containerSchema: QueryCopilotSampleContainerSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockStore = useNotebook.getState();
|
||||||
|
beforeEach(() => {
|
||||||
|
mockStore.notebookServerInfo = {
|
||||||
|
notebookServerEndpoint: "mocked-endpoint",
|
||||||
|
authToken: "mocked-token",
|
||||||
|
forwardingId: "mocked-forwarding-id",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
it("should call fetch with the payload with like", async () => {
|
it("should call fetch with the payload with like", async () => {
|
||||||
const mockFetch = jest.fn().mockResolvedValueOnce({});
|
const mockFetch = jest.fn().mockResolvedValueOnce({});
|
||||||
|
|
||||||
globalThis.fetch = mockFetch;
|
globalThis.fetch = mockFetch;
|
||||||
useQueryCopilot.getState().refreshCorrelationId();
|
|
||||||
|
|
||||||
await submitFeedback({
|
await submitFeedback({
|
||||||
likeQuery: true,
|
params: {
|
||||||
generatedQuery: "GeneratedQuery",
|
likeQuery: true,
|
||||||
userPrompt: "UserPrompt",
|
generatedQuery: "GeneratedQuery",
|
||||||
description: "Description",
|
userPrompt: "UserPrompt",
|
||||||
contact: "Contact",
|
description: "Description",
|
||||||
|
contact: "Contact",
|
||||||
|
},
|
||||||
|
explorer: new Explorer(),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockFetch).toHaveBeenCalledWith(
|
expect(mockFetch).toHaveBeenCalledWith(
|
||||||
"https://copilotorchestrater.azurewebsites.net/feedback",
|
"mocked-endpoint/feedback",
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
method: "POST",
|
headers: expect.objectContaining({
|
||||||
headers: {
|
"x-ms-correlationid": "mocked-correlation-id",
|
||||||
"content-type": "application/json",
|
}),
|
||||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -89,23 +118,25 @@ describe("QueryCopilotUtilities", () => {
|
||||||
const mockFetch = jest.fn().mockResolvedValueOnce({});
|
const mockFetch = jest.fn().mockResolvedValueOnce({});
|
||||||
|
|
||||||
globalThis.fetch = mockFetch;
|
globalThis.fetch = mockFetch;
|
||||||
useQueryCopilot.getState().refreshCorrelationId();
|
|
||||||
|
|
||||||
await submitFeedback({
|
await submitFeedback({
|
||||||
likeQuery: false,
|
params: {
|
||||||
generatedQuery: "GeneratedQuery",
|
likeQuery: false,
|
||||||
userPrompt: "UserPrompt",
|
generatedQuery: "GeneratedQuery",
|
||||||
description: undefined,
|
userPrompt: "UserPrompt",
|
||||||
contact: undefined,
|
description: undefined,
|
||||||
|
contact: undefined,
|
||||||
|
},
|
||||||
|
explorer: new Explorer(),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockFetch).toHaveBeenCalledWith(
|
expect(mockFetch).toHaveBeenCalledWith(
|
||||||
"https://copilotorchestrater.azurewebsites.net/feedback",
|
"mocked-endpoint/feedback",
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
"x-ms-correlationid": "mocked-correlation-id",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -118,11 +149,14 @@ describe("QueryCopilotUtilities", () => {
|
||||||
globalThis.fetch = jest.fn().mockRejectedValueOnce(new Error("Mock error"));
|
globalThis.fetch = jest.fn().mockRejectedValueOnce(new Error("Mock error"));
|
||||||
|
|
||||||
await submitFeedback({
|
await submitFeedback({
|
||||||
likeQuery: true,
|
params: {
|
||||||
generatedQuery: "GeneratedQuery",
|
likeQuery: true,
|
||||||
userPrompt: "UserPrompt",
|
generatedQuery: "GeneratedQuery",
|
||||||
description: "Description",
|
userPrompt: "UserPrompt",
|
||||||
contact: "Contact",
|
description: "Description",
|
||||||
|
contact: "Contact",
|
||||||
|
},
|
||||||
|
explorer: new Explorer(),
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
expect(error.message).toEqual("Mock error");
|
expect(error.message).toEqual("Mock error");
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,8 +6,11 @@ import {
|
||||||
} from "Common/Constants";
|
} from "Common/Constants";
|
||||||
import { handleError } from "Common/ErrorHandlingUtils";
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
import { sampleDataClient } from "Common/SampleDataClient";
|
import { sampleDataClient } from "Common/SampleDataClient";
|
||||||
|
import { createUri } from "Common/UrlUtility";
|
||||||
import { getPartitionKeyValue } from "Common/dataAccess/getPartitionKeyValue";
|
import { getPartitionKeyValue } from "Common/dataAccess/getPartitionKeyValue";
|
||||||
import { getCommonQueryOptions } from "Common/dataAccess/queryDocuments";
|
import { getCommonQueryOptions } from "Common/dataAccess/queryDocuments";
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
|
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import DocumentId from "Explorer/Tree/DocumentId";
|
import DocumentId from "Explorer/Tree/DocumentId";
|
||||||
import { logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
import { logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
|
@ -20,9 +23,16 @@ interface FeedbackParams {
|
||||||
contact?: string;
|
contact?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const submitFeedback = async (params: FeedbackParams): Promise<void> => {
|
export const submitFeedback = async ({
|
||||||
|
params,
|
||||||
|
explorer,
|
||||||
|
}: {
|
||||||
|
params: FeedbackParams;
|
||||||
|
explorer: Explorer;
|
||||||
|
}): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { likeQuery, generatedQuery, userPrompt, description, contact } = params;
|
const { likeQuery, generatedQuery, userPrompt, description, contact } = params;
|
||||||
|
const { correlationId, shouldAllocateContainer, setShouldAllocateContainer } = useQueryCopilot();
|
||||||
const payload = {
|
const payload = {
|
||||||
containerSchema: QueryCopilotSampleContainerSchema,
|
containerSchema: QueryCopilotSampleContainerSchema,
|
||||||
like: likeQuery ? "like" : "dislike",
|
like: likeQuery ? "like" : "dislike",
|
||||||
|
@ -31,15 +41,23 @@ export const submitFeedback = async (params: FeedbackParams): Promise<void> => {
|
||||||
description: description || "",
|
description: description || "",
|
||||||
contact: contact || "",
|
contact: contact || "",
|
||||||
};
|
};
|
||||||
|
if (shouldAllocateContainer) {
|
||||||
await fetch("https://copilotorchestrater.azurewebsites.net/feedback", {
|
await explorer.allocateContainer();
|
||||||
|
setShouldAllocateContainer(false);
|
||||||
|
}
|
||||||
|
const serverInfo = useNotebook.getState().notebookServerInfo;
|
||||||
|
const feedbackUri = createUri(serverInfo.notebookServerEndpoint, "feedback");
|
||||||
|
const response = await fetch(feedbackUri, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
"x-ms-correlationid": correlationId,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
|
if (response.status === 404) {
|
||||||
|
setShouldAllocateContainer(true);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, "copilotSubmitFeedback");
|
handleError(error, "copilotSubmitFeedback");
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ const App: React.FunctionComponent = () => {
|
||||||
{<SQLQuickstartTutorial />}
|
{<SQLQuickstartTutorial />}
|
||||||
{<MongoQuickstartTutorial />}
|
{<MongoQuickstartTutorial />}
|
||||||
{<QueryCopilotCarousel isOpen={isCopilotCarouselOpen} explorer={explorer} />}
|
{<QueryCopilotCarousel isOpen={isCopilotCarouselOpen} explorer={explorer} />}
|
||||||
{shouldShowModal && <QueryCopilotFeedbackModal />}
|
{shouldShowModal && <QueryCopilotFeedbackModal explorer={explorer} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { configContext } from "ConfigContext";
|
||||||
import { useDialog } from "Explorer/Controls/Dialog";
|
import { useDialog } from "Explorer/Controls/Dialog";
|
||||||
import promiseRetry, { AbortError } from "p-retry";
|
|
||||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
import { allowedJunoOrigins, validateEndpoint } from "Utils/EndpointValidation";
|
import { allowedJunoOrigins, validateEndpoint } from "Utils/EndpointValidation";
|
||||||
|
import promiseRetry, { AbortError } from "p-retry";
|
||||||
import {
|
import {
|
||||||
Areas,
|
Areas,
|
||||||
ConnectionStatusType,
|
ConnectionStatusType,
|
||||||
|
@ -12,7 +14,6 @@ import {
|
||||||
} from "../Common/Constants";
|
} from "../Common/Constants";
|
||||||
import { getErrorMessage, getErrorStack } from "../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "../Common/ErrorHandlingUtils";
|
||||||
import * as Logger from "../Common/Logger";
|
import * as Logger from "../Common/Logger";
|
||||||
import { configContext } from "../ConfigContext";
|
|
||||||
import {
|
import {
|
||||||
ContainerConnectionInfo,
|
ContainerConnectionInfo,
|
||||||
ContainerInfo,
|
ContainerInfo,
|
||||||
|
@ -28,7 +29,6 @@ import {
|
||||||
} from "../Contracts/DataModels";
|
} from "../Contracts/DataModels";
|
||||||
import { useNotebook } from "../Explorer/Notebook/useNotebook";
|
import { useNotebook } from "../Explorer/Notebook/useNotebook";
|
||||||
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "../UserContext";
|
|
||||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||||
|
|
||||||
export class PhoenixClient {
|
export class PhoenixClient {
|
||||||
|
|
|
@ -30,6 +30,7 @@ export interface QueryCopilotState {
|
||||||
showWelcomeSidebar: boolean;
|
showWelcomeSidebar: boolean;
|
||||||
showCopilotSidebar: boolean;
|
showCopilotSidebar: boolean;
|
||||||
chatMessages: string[];
|
chatMessages: string[];
|
||||||
|
shouldAllocateContainer: boolean;
|
||||||
|
|
||||||
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void;
|
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void;
|
||||||
closeFeedbackModal: () => void;
|
closeFeedbackModal: () => void;
|
||||||
|
@ -59,6 +60,8 @@ export interface QueryCopilotState {
|
||||||
setShowCopilotSidebar: (showCopilotSidebar: boolean) => void;
|
setShowCopilotSidebar: (showCopilotSidebar: boolean) => void;
|
||||||
setChatMessages: (chatMessages: string[]) => void;
|
setChatMessages: (chatMessages: string[]) => void;
|
||||||
|
|
||||||
|
setShouldAllocateContainer: (shouldAllocateContainer: boolean) => void;
|
||||||
|
|
||||||
resetQueryCopilotStates: () => void;
|
resetQueryCopilotStates: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +94,7 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
||||||
showWelcomeSidebar: true,
|
showWelcomeSidebar: true,
|
||||||
showCopilotSidebar: false,
|
showCopilotSidebar: false,
|
||||||
chatMessages: [],
|
chatMessages: [],
|
||||||
|
shouldAllocateContainer: true,
|
||||||
|
|
||||||
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) =>
|
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) =>
|
||||||
set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }),
|
set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }),
|
||||||
|
@ -121,6 +125,7 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
||||||
setShowWelcomeSidebar: (showWelcomeSidebar: boolean) => set({ showWelcomeSidebar }),
|
setShowWelcomeSidebar: (showWelcomeSidebar: boolean) => set({ showWelcomeSidebar }),
|
||||||
setShowCopilotSidebar: (showCopilotSidebar: boolean) => set({ showCopilotSidebar }),
|
setShowCopilotSidebar: (showCopilotSidebar: boolean) => set({ showCopilotSidebar }),
|
||||||
setChatMessages: (chatMessages: string[]) => set({ chatMessages }),
|
setChatMessages: (chatMessages: string[]) => set({ chatMessages }),
|
||||||
|
setShouldAllocateContainer: (shouldAllocateContainer: boolean) => set({ shouldAllocateContainer }),
|
||||||
|
|
||||||
resetQueryCopilotStates: () => {
|
resetQueryCopilotStates: () => {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
|
@ -150,6 +155,7 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
||||||
wasCopilotUsed: false,
|
wasCopilotUsed: false,
|
||||||
showCopilotSidebar: false,
|
showCopilotSidebar: false,
|
||||||
chatMessages: [],
|
chatMessages: [],
|
||||||
|
shouldAllocateContainer: true,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
Loading…
Reference in New Issue