[Query Copilot] Phoenix container implementation (#1594)

* Phoenix implementation

* removing comments

---------

Co-authored-by: Predrag Klepic <v-prklepic@microsoft.com>
This commit is contained in:
Predrag Klepic 2023-08-30 16:50:55 +02:00 committed by GitHub
parent b992742e20
commit c8e7e69aa5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 158 additions and 95 deletions

View File

@ -5,6 +5,7 @@ import { Platform } from "ConfigContext";
import { MessageTypes } from "Contracts/ExplorerContracts"; import { MessageTypes } from "Contracts/ExplorerContracts";
import { IGalleryItem } from "Juno/JunoClient"; import { IGalleryItem } from "Juno/JunoClient";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation"; import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import * as ko from "knockout"; import * as ko from "knockout";
import React from "react"; import React from "react";
import _ from "underscore"; import _ from "underscore";
@ -386,8 +387,14 @@ export default class Explorer {
} }
public async allocateContainer(poolId: PoolIdType): Promise<void> { public async allocateContainer(poolId: PoolIdType): Promise<void> {
const notebookServerInfo = useNotebook.getState().notebookServerInfo; const shouldUseNotebookStates = poolId === PoolIdType.DefaultPoolId ? true : false;
const isAllocating = useNotebook.getState().isAllocating; const notebookServerInfo = shouldUseNotebookStates
? useNotebook.getState().notebookServerInfo
: useQueryCopilot.getState().notebookServerInfo;
const isAllocating = shouldUseNotebookStates
? useNotebook.getState().isAllocating
: useQueryCopilot.getState().isAllocatingContainer;
if ( if (
isAllocating === false && isAllocating === false &&
(notebookServerInfo === undefined || (notebookServerInfo === undefined ||
@ -395,23 +402,28 @@ export default class Explorer {
) { ) {
const provisionData: IProvisionData = { const provisionData: IProvisionData = {
cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint, cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
poolId: poolId === PoolIdType.DefaultPoolId ? undefined : poolId, poolId: shouldUseNotebookStates ? undefined : poolId,
}; };
const connectionStatus: ContainerConnectionInfo = { const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Connecting, status: ConnectionStatusType.Connecting,
}; };
useNotebook.getState().setConnectionInfo(connectionStatus);
shouldUseNotebookStates && useNotebook.getState().setConnectionInfo(connectionStatus);
let connectionInfo; let connectionInfo;
try { try {
TelemetryProcessor.traceStart(Action.PhoenixConnection, { TelemetryProcessor.traceStart(Action.PhoenixConnection, {
dataExplorerArea: Areas.Notebook, dataExplorerArea: Areas.Notebook,
}); });
useNotebook.getState().setIsAllocating(true); shouldUseNotebookStates
? useNotebook.getState().setIsAllocating(true)
: useQueryCopilot.getState().setIsAllocatingContainer(true);
connectionInfo = await this.phoenixClient.allocateContainer(provisionData); connectionInfo = await this.phoenixClient.allocateContainer(provisionData);
if (!connectionInfo?.data?.phoenixServiceUrl) { if (!connectionInfo?.data?.phoenixServiceUrl) {
throw new Error(`PhoenixServiceUrl is invalid!`); throw new Error(`PhoenixServiceUrl is invalid!`);
} }
await this.setNotebookInfo(connectionInfo, connectionStatus); await this.setNotebookInfo(shouldUseNotebookStates, connectionInfo, connectionStatus);
TelemetryProcessor.traceSuccess(Action.PhoenixConnection, { TelemetryProcessor.traceSuccess(Action.PhoenixConnection, {
dataExplorerArea: Areas.Notebook, dataExplorerArea: Areas.Notebook,
}); });
@ -423,7 +435,9 @@ export default class Explorer {
errorStack: getErrorStack(error), errorStack: getErrorStack(error),
}); });
connectionStatus.status = ConnectionStatusType.Failed; connectionStatus.status = ConnectionStatusType.Failed;
useNotebook.getState().resetContainerConnection(connectionStatus); shouldUseNotebookStates
? useNotebook.getState().resetContainerConnection(connectionStatus)
: useQueryCopilot.getState().resetContainerConnection();
if (error?.status === HttpStatusCodes.Forbidden && error.message) { if (error?.status === HttpStatusCodes.Forbidden && error.message) {
useDialog.getState().showOkModalDialog("Connection Failed", `${error.message}`); useDialog.getState().showOkModalDialog("Connection Failed", `${error.message}`);
} else { } else {
@ -436,7 +450,9 @@ export default class Explorer {
} }
throw error; throw error;
} finally { } finally {
useNotebook.getState().setIsAllocating(false); shouldUseNotebookStates
? useNotebook.getState().setIsAllocating(false)
: useQueryCopilot.getState().setIsAllocatingContainer(false);
this.refreshCommandBarButtons(); this.refreshCommandBarButtons();
this.refreshNotebookList(); this.refreshNotebookList();
this._isInitializingNotebooks = false; this._isInitializingNotebooks = false;
@ -445,6 +461,7 @@ export default class Explorer {
} }
private async setNotebookInfo( private async setNotebookInfo(
shouldUseNotebookStates: boolean,
connectionInfo: IResponse<IPhoenixServiceInfo>, connectionInfo: IResponse<IPhoenixServiceInfo>,
connectionStatus: DataModels.ContainerConnectionInfo connectionStatus: DataModels.ContainerConnectionInfo
) { ) {
@ -452,21 +469,26 @@ export default class Explorer {
forwardingId: connectionInfo.data.forwardingId, forwardingId: connectionInfo.data.forwardingId,
dbAccountName: userContext.databaseAccount.name, dbAccountName: userContext.databaseAccount.name,
}; };
await this.phoenixClient.initiateContainerHeartBeat(containerData); await this.phoenixClient.initiateContainerHeartBeat(shouldUseNotebookStates, containerData);
connectionStatus.status = ConnectionStatusType.Connected; connectionStatus.status = ConnectionStatusType.Connected;
useNotebook.getState().setConnectionInfo(connectionStatus); shouldUseNotebookStates && useNotebook.getState().setConnectionInfo(connectionStatus);
useNotebook.getState().setNotebookServerInfo({
const noteBookServerInfo = {
notebookServerEndpoint: notebookServerEndpoint:
(validateEndpoint(userContext.features.notebookServerUrl, allowedNotebookServerUrls) && (validateEndpoint(userContext.features.notebookServerUrl, allowedNotebookServerUrls) &&
userContext.features.notebookServerUrl) || userContext.features.notebookServerUrl) ||
connectionInfo.data.phoenixServiceUrl, connectionInfo.data.phoenixServiceUrl,
authToken: userContext.features.notebookServerToken || connectionInfo.data.authToken, authToken: userContext.features.notebookServerToken || connectionInfo.data.authToken,
forwardingId: connectionInfo.data.forwardingId, forwardingId: connectionInfo.data.forwardingId,
}); };
this.notebookManager?.notebookClient shouldUseNotebookStates
.getMemoryUsage() ? useNotebook.getState().setNotebookServerInfo(noteBookServerInfo)
.then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo)); : useQueryCopilot.getState().setNotebookServerInfo(noteBookServerInfo);
shouldUseNotebookStates &&
this.notebookManager?.notebookClient
.getMemoryUsage()
.then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo));
} }
public resetNotebookWorkspace(): void { public resetNotebookWorkspace(): void {
@ -540,7 +562,7 @@ export default class Explorer {
throw new Error(`Reset Workspace: PhoenixServiceUrl is invalid!`); throw new Error(`Reset Workspace: PhoenixServiceUrl is invalid!`);
} }
if (useNotebook.getState().isPhoenixNotebooks) { if (useNotebook.getState().isPhoenixNotebooks) {
await this.setNotebookInfo(connectionInfo, connectionStatus); await this.setNotebookInfo(true, connectionInfo, connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed); useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
} }
logConsoleInfo("Successfully reset notebook workspace"); logConsoleInfo("Successfully reset notebook workspace");

View File

@ -18,6 +18,7 @@ import {
} from "@fluentui/react"; } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks"; import { useBoolean } from "@fluentui/react-hooks";
import { import {
ContainerStatusType,
PoolIdType, PoolIdType,
QueryCopilotSampleContainerId, QueryCopilotSampleContainerId,
QueryCopilotSampleContainerSchema, QueryCopilotSampleContainerSchema,
@ -32,7 +33,6 @@ 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 { 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";
@ -111,8 +111,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
setShowErrorMessageBar, setShowErrorMessageBar,
generatedQueryComments, generatedQueryComments,
setGeneratedQueryComments, setGeneratedQueryComments,
shouldAllocateContainer,
setShouldAllocateContainer,
} = useQueryCopilot(); } = useQueryCopilot();
const sampleProps: SamplePromptsProps = { const sampleProps: SamplePromptsProps = {
@ -180,23 +178,24 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
const generateSQLQuery = async (): Promise<void> => { const generateSQLQuery = async (): Promise<void> => {
try { try {
if (shouldAllocateContainer && userContext.features.enableCopilotPhoenixGateaway) {
await explorer.allocateContainer(PoolIdType.QueryCopilot);
setShouldAllocateContainer(false);
}
setIsGeneratingQuery(true); setIsGeneratingQuery(true);
setShowDeletePopup(false);
useTabs.getState().setIsTabExecuting(true); useTabs.getState().setIsTabExecuting(true);
useTabs.getState().setIsQueryErrorThrown(false); useTabs.getState().setIsQueryErrorThrown(false);
if (
useQueryCopilot.getState().containerStatus.status !== ContainerStatusType.Active &&
userContext.features.enableCopilotPhoenixGateaway
) {
await explorer.allocateContainer(PoolIdType.QueryCopilot);
}
const payload = { const payload = {
containerSchema: userContext.features.enableCopilotFullSchema containerSchema: userContext.features.enableCopilotFullSchema
? QueryCopilotSampleContainerSchema ? QueryCopilotSampleContainerSchema
: ShortenedQueryCopilotSampleContainerSchema, : ShortenedQueryCopilotSampleContainerSchema,
userPrompt: userPrompt, userPrompt: userPrompt,
}; };
setShowDeletePopup(false);
useQueryCopilot.getState().refreshCorrelationId(); useQueryCopilot.getState().refreshCorrelationId();
const serverInfo = useNotebook.getState().notebookServerInfo; const serverInfo = useQueryCopilot.getState().notebookServerInfo;
const queryUri = userContext.features.enableCopilotPhoenixGateaway const queryUri = userContext.features.enableCopilotPhoenixGateaway
? createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery") ? createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery")
: createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery"); : createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery");
@ -210,9 +209,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
}); });
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`;

View File

@ -2,9 +2,9 @@ import { QueryCopilotSampleContainerSchema, ShortenedQueryCopilotSampleContainer
import { handleError } from "Common/ErrorHandlingUtils"; import { handleError } from "Common/ErrorHandlingUtils";
import { createUri } from "Common/UrlUtility"; import { createUri } from "Common/UrlUtility";
import Explorer from "Explorer/Explorer"; import Explorer from "Explorer/Explorer";
import { useNotebook } from "Explorer/Notebook/useNotebook";
import { SubmitFeedback } from "Explorer/QueryCopilot/Shared/QueryCopilotClient"; import { SubmitFeedback } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { useQueryCopilot } from "hooks/useQueryCopilot";
jest.mock("@azure/cosmos", () => ({ jest.mock("@azure/cosmos", () => ({
Constants: { Constants: {
@ -27,22 +27,10 @@ jest.mock("Explorer/Explorer", () => {
return MockExplorer; return MockExplorer;
}); });
jest.mock("hooks/useQueryCopilot", () => {
const mockQueryCopilotStore = {
shouldAllocateContainer: true,
setShouldAllocateContainer: jest.fn(),
correlationId: "mocked-correlation-id",
};
return {
useQueryCopilot: jest.fn(() => mockQueryCopilotStore),
};
});
describe("Query Copilot Client", () => { describe("Query Copilot Client", () => {
beforeEach(() => jest.clearAllMocks()); beforeEach(() => jest.clearAllMocks());
describe("submitFeedback", () => { describe("SubmitFeedback", () => {
const payload = { const payload = {
like: "like", like: "like",
generatedSql: "GeneratedQuery", generatedSql: "GeneratedQuery",
@ -54,17 +42,16 @@ describe("Query Copilot Client", () => {
: ShortenedQueryCopilotSampleContainerSchema, : ShortenedQueryCopilotSampleContainerSchema,
}; };
const mockStore = useNotebook.getState(); const mockStore = useQueryCopilot.getState();
beforeEach(() => { mockStore.correlationId = "mocked-correlation-id";
mockStore.notebookServerInfo = { mockStore.notebookServerInfo = {
notebookServerEndpoint: "mocked-endpoint", notebookServerEndpoint: "mocked-endpoint",
authToken: "mocked-token", authToken: "mocked-token",
forwardingId: "mocked-forwarding-id", forwardingId: "mocked-forwarding-id",
}; };
});
const feedbackUri = userContext.features.enableCopilotPhoenixGateaway const feedbackUri = userContext.features.enableCopilotPhoenixGateaway
? createUri(useNotebook.getState().notebookServerInfo.notebookServerEndpoint, "feedback") ? createUri(useQueryCopilot.getState().notebookServerInfo.notebookServerEndpoint, "feedback")
: createUri("https://copilotorchestrater.azurewebsites.net/", "feedback"); : createUri("https://copilotorchestrater.azurewebsites.net/", "feedback");
it("should call fetch with the payload with like", async () => { it("should call fetch with the payload with like", async () => {

View File

@ -1,4 +1,5 @@
import { import {
ContainerStatusType,
PoolIdType, PoolIdType,
QueryCopilotSampleContainerSchema, QueryCopilotSampleContainerSchema,
ShortenedQueryCopilotSampleContainerSchema, ShortenedQueryCopilotSampleContainerSchema,
@ -6,7 +7,6 @@ import {
import { handleError } from "Common/ErrorHandlingUtils"; import { handleError } from "Common/ErrorHandlingUtils";
import { createUri } from "Common/UrlUtility"; import { createUri } from "Common/UrlUtility";
import Explorer from "Explorer/Explorer"; import Explorer from "Explorer/Explorer";
import { useNotebook } from "Explorer/Notebook/useNotebook";
import { FeedbackParams, GenerateSQLQueryResponse } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces"; import { FeedbackParams, GenerateSQLQueryResponse } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { useQueryCopilot } from "hooks/useQueryCopilot"; import { useQueryCopilot } from "hooks/useQueryCopilot";
@ -30,13 +30,15 @@ export const SendQueryRequest = async ({
.getState() .getState()
.setChatMessages([...useQueryCopilot.getState().chatMessages, { source: 0, message: userPrompt }]); .setChatMessages([...useQueryCopilot.getState().chatMessages, { source: 0, message: userPrompt }]);
try { try {
if (useQueryCopilot.getState().shouldAllocateContainer && userContext.features.enableCopilotPhoenixGateaway) { if (
await explorer.allocateContainer(PoolIdType.DefaultPoolId); useQueryCopilot.getState().containerStatus.status !== ContainerStatusType.Active &&
useQueryCopilot.getState().setShouldAllocateContainer(false); userContext.features.enableCopilotPhoenixGateaway
) {
await explorer.allocateContainer(PoolIdType.QueryCopilot);
} }
useQueryCopilot.getState().refreshCorrelationId(); useQueryCopilot.getState().refreshCorrelationId();
const serverInfo = useNotebook.getState().notebookServerInfo; const serverInfo = useQueryCopilot.getState().notebookServerInfo;
const queryUri = userContext.features.enableCopilotPhoenixGateaway const queryUri = userContext.features.enableCopilotPhoenixGateaway
? createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery") ? createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery")
@ -58,9 +60,6 @@ export const SendQueryRequest = async ({
}); });
const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json(); const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json();
if (response.status === 404) {
useQueryCopilot.getState().setShouldAllocateContainer(true);
}
if (response.ok) { if (response.ok) {
if (generateSQLQueryResponse?.sql) { if (generateSQLQueryResponse?.sql) {
let query = `Here is a query which will help you with provided prompt.\r\n **Prompt:** ${userPrompt}`; let query = `Here is a query which will help you with provided prompt.\r\n **Prompt:** ${userPrompt}`;
@ -102,7 +101,6 @@ export const SubmitFeedback = async ({
}): Promise<void> => { }): 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: userContext.features.enableCopilotFullSchema containerSchema: userContext.features.enableCopilotFullSchema
? QueryCopilotSampleContainerSchema ? QueryCopilotSampleContainerSchema
@ -113,25 +111,24 @@ export const SubmitFeedback = async ({
description: description || "", description: description || "",
contact: contact || "", contact: contact || "",
}; };
if (shouldAllocateContainer && userContext.features.enableCopilotPhoenixGateaway) { if (
await explorer.allocateContainer(PoolIdType.DefaultPoolId); useQueryCopilot.getState().containerStatus.status !== ContainerStatusType.Active &&
setShouldAllocateContainer(false); userContext.features.enableCopilotPhoenixGateaway
) {
await explorer.allocateContainer(PoolIdType.QueryCopilot);
} }
const serverInfo = useNotebook.getState().notebookServerInfo; const serverInfo = useQueryCopilot.getState().notebookServerInfo;
const feedbackUri = userContext.features.enableCopilotPhoenixGateaway const feedbackUri = userContext.features.enableCopilotPhoenixGateaway
? createUri(serverInfo.notebookServerEndpoint, "feedback") ? createUri(serverInfo.notebookServerEndpoint, "feedback")
: createUri("https://copilotorchestrater.azurewebsites.net/", "feedback"); : createUri("https://copilotorchestrater.azurewebsites.net/", "feedback");
const response = await fetch(feedbackUri, { await fetch(feedbackUri, {
method: "POST", method: "POST",
headers: { headers: {
"content-type": "application/json", "content-type": "application/json",
"x-ms-correlationid": correlationId, "x-ms-correlationid": useQueryCopilot.getState().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");
} }

View File

@ -3,6 +3,7 @@ import { useDialog } from "Explorer/Controls/Dialog";
import { Action } from "Shared/Telemetry/TelemetryConstants"; import { Action } from "Shared/Telemetry/TelemetryConstants";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { allowedJunoOrigins, validateEndpoint } from "Utils/EndpointValidation"; import { allowedJunoOrigins, validateEndpoint } from "Utils/EndpointValidation";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import promiseRetry, { AbortError } from "p-retry"; import promiseRetry, { AbortError } from "p-retry";
import { import {
Areas, Areas,
@ -96,20 +97,27 @@ export class PhoenixClient {
} }
} }
public async initiateContainerHeartBeat(containerData: IContainerData) { public async initiateContainerHeartBeat(shouldUseNotebookStates: boolean, containerData: IContainerData) {
if (this.containerHealthHandler) { if (this.containerHealthHandler) {
clearTimeout(this.containerHealthHandler); clearTimeout(this.containerHealthHandler);
} }
await this.getContainerHealth(Notebook.containerStatusHeartbeatDelayMs, containerData); await this.getContainerHealth(shouldUseNotebookStates, Notebook.containerStatusHeartbeatDelayMs, containerData);
} }
private scheduleContainerHeartbeat(delayMs: number, containerData: IContainerData): void { private scheduleContainerHeartbeat(
shouldUseNotebookStates: boolean,
delayMs: number,
containerData: IContainerData
): void {
this.containerHealthHandler = setTimeout(async () => { this.containerHealthHandler = setTimeout(async () => {
await this.getContainerHealth(delayMs, containerData); await this.getContainerHealth(shouldUseNotebookStates, delayMs, containerData);
}, delayMs); }, delayMs);
} }
private async getContainerStatusAsync(containerData: IContainerData): Promise<ContainerInfo> { private async getContainerStatusAsync(
shouldUseNotebookStates: boolean,
containerData: IContainerData
): Promise<ContainerInfo> {
try { try {
const runContainerStatusAsync = async () => { const runContainerStatusAsync = async () => {
const response = await window.fetch( const response = await window.fetch(
@ -136,14 +144,17 @@ export class PhoenixClient {
dataExplorerArea: Areas.Notebook, dataExplorerArea: Areas.Notebook,
message: getErrorMessage(error), message: getErrorMessage(error),
}); });
useNotebook.getState().resetContainerConnection(connectionStatus); shouldUseNotebookStates
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed); ? useNotebook.getState().resetContainerConnection(connectionStatus)
useDialog : useQueryCopilot.getState().resetContainerConnection();
.getState() shouldUseNotebookStates && useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
.showOkModalDialog( shouldUseNotebookStates &&
"Disconnected", useDialog
"Disconnected from temporary workspace. Please click on connect button to connect to temporary workspace." .getState()
); .showOkModalDialog(
"Disconnected",
"Disconnected from temporary workspace. Please click on connect button to connect to temporary workspace."
);
throw new AbortError(response.statusText); throw new AbortError(response.statusText);
} else if (response?.status === HttpStatusCodes.Forbidden) { } else if (response?.status === HttpStatusCodes.Forbidden) {
const validationMessage = this.ConvertToForbiddenErrorString(await response.json()); const validationMessage = this.ConvertToForbiddenErrorString(await response.json());
@ -163,8 +174,10 @@ export class PhoenixClient {
const connectionStatus: ContainerConnectionInfo = { const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Failed, status: ConnectionStatusType.Failed,
}; };
useNotebook.getState().resetContainerConnection(connectionStatus); shouldUseNotebookStates
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed); ? useNotebook.getState().resetContainerConnection(connectionStatus)
: useQueryCopilot.getState().resetContainerConnection();
shouldUseNotebookStates && useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
return { return {
durationLeftInMinutes: undefined, durationLeftInMinutes: undefined,
phoenixServerInfo: undefined, phoenixServerInfo: undefined,
@ -173,11 +186,17 @@ export class PhoenixClient {
} }
} }
private async getContainerHealth(delayMs: number, containerData: IContainerData) { private async getContainerHealth(shouldUseNotebookStates: boolean, delayMs: number, containerData: IContainerData) {
const containerInfo = await this.getContainerStatusAsync(containerData); const containerInfo = await this.getContainerStatusAsync(shouldUseNotebookStates, containerData);
useNotebook.getState().setContainerStatus(containerInfo); shouldUseNotebookStates
if (useNotebook.getState().containerStatus?.status === ContainerStatusType.Active) { ? useNotebook.getState().setContainerStatus(containerInfo)
this.scheduleContainerHeartbeat(delayMs, containerData); : useQueryCopilot.getState().setContainerStatus(containerInfo);
const containerStatus = shouldUseNotebookStates
? useNotebook.getState().containerStatus?.status
: useQueryCopilot.getState().containerStatus?.status;
if (containerStatus === ContainerStatusType.Active) {
this.scheduleContainerHeartbeat(shouldUseNotebookStates, delayMs, containerData);
} }
} }

View File

@ -2,7 +2,10 @@ import { MinimalQueryIterator } from "Common/IteratorUtilities";
import { QueryResults } from "Contracts/ViewModels"; import { QueryResults } from "Contracts/ViewModels";
import { CopilotMessage } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces"; import { CopilotMessage } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
import { guid } from "Explorer/Tables/Utilities"; import { guid } from "Explorer/Tables/Utilities";
import { useTabs } from "hooks/useTabs";
import create, { UseStore } from "zustand"; import create, { UseStore } from "zustand";
import * as DataModels from "../Contracts/DataModels";
import { ContainerInfo } from "../Contracts/DataModels";
export interface QueryCopilotState { export interface QueryCopilotState {
generatedQuery: string; generatedQuery: string;
@ -32,10 +35,12 @@ export interface QueryCopilotState {
showWelcomeSidebar: boolean; showWelcomeSidebar: boolean;
showCopilotSidebar: boolean; showCopilotSidebar: boolean;
chatMessages: CopilotMessage[]; chatMessages: CopilotMessage[];
shouldAllocateContainer: boolean;
shouldIncludeInMessages: boolean; shouldIncludeInMessages: boolean;
showExplanationBubble: boolean; showExplanationBubble: boolean;
showQueryExplanation: boolean; showQueryExplanation: boolean;
notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo;
containerStatus: ContainerInfo;
isAllocatingContainer: boolean;
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void; openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void;
closeFeedbackModal: () => void; closeFeedbackModal: () => void;
@ -65,11 +70,14 @@ export interface QueryCopilotState {
setShowWelcomeSidebar: (showWelcomeSidebar: boolean) => void; setShowWelcomeSidebar: (showWelcomeSidebar: boolean) => void;
setShowCopilotSidebar: (showCopilotSidebar: boolean) => void; setShowCopilotSidebar: (showCopilotSidebar: boolean) => void;
setChatMessages: (chatMessages: CopilotMessage[]) => void; setChatMessages: (chatMessages: CopilotMessage[]) => void;
setShouldAllocateContainer: (shouldAllocateContainer: boolean) => void;
setShouldIncludeInMessages: (shouldIncludeInMessages: boolean) => void; setShouldIncludeInMessages: (shouldIncludeInMessages: boolean) => void;
setShowExplanationBubble: (showExplanationBubble: boolean) => void; setShowExplanationBubble: (showExplanationBubble: boolean) => void;
setShowQueryExplanation: (showQueryExplanation: boolean) => void; setShowQueryExplanation: (showQueryExplanation: boolean) => void;
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) => void;
setContainerStatus: (containerStatus: ContainerInfo) => void;
setIsAllocatingContainer: (isAllocatingContainer: boolean) => void;
resetContainerConnection: () => void;
resetQueryCopilotStates: () => void; resetQueryCopilotStates: () => void;
} }
@ -103,10 +111,20 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
showWelcomeSidebar: true, showWelcomeSidebar: true,
showCopilotSidebar: false, showCopilotSidebar: false,
chatMessages: [], chatMessages: [],
shouldAllocateContainer: true,
shouldIncludeInMessages: true, shouldIncludeInMessages: true,
showExplanationBubble: false, showExplanationBubble: false,
showQueryExplanation: false, showQueryExplanation: false,
notebookServerInfo: {
notebookServerEndpoint: undefined,
authToken: undefined,
forwardingId: undefined,
},
containerStatus: {
status: undefined,
durationLeftInMinutes: undefined,
phoenixServerInfo: undefined,
},
isAllocatingContainer: false,
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 }),
@ -138,10 +156,24 @@ 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: CopilotMessage[]) => set({ chatMessages }), setChatMessages: (chatMessages: CopilotMessage[]) => set({ chatMessages }),
setShouldAllocateContainer: (shouldAllocateContainer: boolean) => set({ shouldAllocateContainer }),
setShouldIncludeInMessages: (shouldIncludeInMessages: boolean) => set({ shouldIncludeInMessages }), setShouldIncludeInMessages: (shouldIncludeInMessages: boolean) => set({ shouldIncludeInMessages }),
setShowExplanationBubble: (showExplanationBubble: boolean) => set({ showExplanationBubble }), setShowExplanationBubble: (showExplanationBubble: boolean) => set({ showExplanationBubble }),
setShowQueryExplanation: (showQueryExplanation: boolean) => set({ showQueryExplanation }), setShowQueryExplanation: (showQueryExplanation: boolean) => set({ showQueryExplanation }),
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) =>
set({ notebookServerInfo }),
setContainerStatus: (containerStatus: ContainerInfo) => set({ containerStatus }),
setIsAllocatingContainer: (isAllocatingContainer: boolean) => set({ isAllocatingContainer }),
resetContainerConnection: (): void => {
useTabs.getState().closeAllNotebookTabs(true);
useQueryCopilot.getState().setNotebookServerInfo(undefined);
useQueryCopilot.getState().setIsAllocatingContainer(false);
useQueryCopilot.getState().setContainerStatus({
status: undefined,
durationLeftInMinutes: undefined,
phoenixServerInfo: undefined,
});
},
resetQueryCopilotStates: () => { resetQueryCopilotStates: () => {
set((state) => ({ set((state) => ({
@ -172,10 +204,20 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
wasCopilotUsed: false, wasCopilotUsed: false,
showCopilotSidebar: false, showCopilotSidebar: false,
chatMessages: [], chatMessages: [],
shouldAllocateContainer: true,
shouldIncludeInMessages: true, shouldIncludeInMessages: true,
showExplanationBubble: false, showExplanationBubble: false,
showQueryExplanation: false, showQueryExplanation: false,
notebookServerInfo: {
notebookServerEndpoint: undefined,
authToken: undefined,
forwardingId: undefined,
},
containerStatus: {
status: undefined,
durationLeftInMinutes: undefined,
phoenixServerInfo: undefined,
},
isAllocatingContainer: false,
})); }));
}, },
})); }));