Skip to content

Commit

Permalink
Merge branch 'main' into surface-phoenix-version
Browse files Browse the repository at this point in the history
* main:
  fix: display point-cloud errors in the UI for troubleshooting (#525)
  • Loading branch information
Francisco Castillo committed Apr 6, 2023
2 parents ed3fa7b + 456fef7 commit 3b178c8
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 18 deletions.
33 changes: 19 additions & 14 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import { ThemeProvider } from "@emotion/react";
import { Provider, theme } from "@arizeai/components";

import { AppRootQuery } from "./__generated__/AppRootQuery.graphql";
import { TimeRangeProvider } from "./contexts/TimeRangeContext";
import { DatasetsProvider } from "./contexts";
import {
DatasetsProvider,
NotificationProvider,
TimeRangeProvider,
} from "./contexts";
import { GlobalStyles } from "./GlobalStyles";
import RelayEnvironment from "./RelayEnvironment";
import { AppRoutes } from "./Routes";
Expand Down Expand Up @@ -46,19 +49,21 @@ function App(props: AppProps) {
} = usePreloadedQuery(RootQuery, props.preloadedQuery);

return (
<DatasetsProvider
primaryDataset={primaryDataset}
referenceDataset={referenceDataset ?? null}
>
<TimeRangeProvider
timeRangeBounds={{
start: new Date(primaryDataset.startTime),
end: new Date(primaryDataset.endTime),
}}
<NotificationProvider>
<DatasetsProvider
primaryDataset={primaryDataset}
referenceDataset={referenceDataset ?? null}
>
<AppRoutes />
</TimeRangeProvider>
</DatasetsProvider>
<TimeRangeProvider
timeRangeBounds={{
start: new Date(primaryDataset.startTime),
end: new Date(primaryDataset.endTime),
}}
>
<AppRoutes />
</TimeRangeProvider>
</DatasetsProvider>
</NotificationProvider>
);
}

Expand Down
86 changes: 86 additions & 0 deletions app/src/contexts/NotificationContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { createContext, useCallback, useContext } from "react";

import { NoticeFn, useNotification } from "@arizeai/components";

// Extract the first argument of notify
type NoticeConfig = Parameters<NoticeFn>[0];
type NoticeConfigWithoutVariant = Omit<NoticeConfig, "variant">;
type NotificationContextType = {
/**
* Send a notification that is visible in any part of the UI
*/
notify: NoticeFn;
/**
* Convenience function to notify of an error
*/
notifyError: (notice: NoticeConfigWithoutVariant) => void;
/**
* Convenience function to notify of a success
*/
notifySuccess: (notice: NoticeConfigWithoutVariant) => void;
};

const NotificationContext = createContext<NotificationContextType | null>(null);

export function NotificationProvider({
children,
}: {
children: React.ReactNode;
}) {
const [notify, holder] = useNotification();

const notifyError = useCallback(
(notice: NoticeConfigWithoutVariant) => {
notify({
variant: "danger",
...notice,
});
},
[notify]
);

const notifySuccess = useCallback(
(notice: NoticeConfigWithoutVariant) => {
notify({
variant: "success",
...notice,
});
},
[notify]
);

return (
<NotificationContext.Provider
value={{ notify, notifyError, notifySuccess }}
>
{children}
{holder}
</NotificationContext.Provider>
);
}

export function useGlobalNotification() {
const context = useContext(NotificationContext);
if (context === null) {
throw new Error(
"useGlobalNotification must be used within a NotificationProvider"
);
}
return context;
}

/**
* Convenience hook to display an error at the global app level
*/
export function useNotifyError() {
const context = useGlobalNotification();
return context.notifyError;
}

/**
* Convenience hook to display a success at the global app level
*/
export function useNotifySuccess() {
const context = useGlobalNotification();
return context.notifySuccess;
}
1 change: 1 addition & 0 deletions app/src/contexts/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./DatasetsContext";
export * from "./PointCloudContext";
export * from "./TimeRangeContext";
export * from "./NotificationContext";
32 changes: 31 additions & 1 deletion app/src/pages/embedding/Embedding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ import {
compactResizeHandleCSS,
resizeHandleCSS,
} from "@phoenix/components/resize/styles";
import { PointCloudProvider, usePointCloudContext } from "@phoenix/contexts";
import {
PointCloudProvider,
useGlobalNotification,
usePointCloudContext,
} from "@phoenix/contexts";
import { useDatasets } from "@phoenix/contexts";
import { useTimeRange } from "@phoenix/contexts/TimeRangeContext";
import {
Expand Down Expand Up @@ -224,6 +228,7 @@ function EmbeddingMain() {
background-color: ${theme.colors.gray900};
`}
>
<PointCloudNotifications />
<Toolbar
extra={
<Switch
Expand Down Expand Up @@ -584,3 +589,28 @@ function ClustersPanelContents({
</Tabs>
);
}

function PointCloudNotifications() {
const { notifyError } = useGlobalNotification();
const errorMessage = usePointCloudContext((state) => state.errorMessage);
const setErrorMessage = usePointCloudContext(
(state) => state.setErrorMessage
);

useEffect(() => {
if (errorMessage !== null) {
notifyError({
title: "An error occurred",
message: errorMessage,
action: {
text: "Dismiss",
onClick: () => {
setErrorMessage(null);
},
},
});
}
}, [errorMessage, notifyError, setErrorMessage]);

return null;
}
22 changes: 20 additions & 2 deletions app/src/store/pointCloudStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ export interface PointCloudProps {
* The clustering / HDBSCAN parameters
*/
hdbscanParameters: HDBSCANParameters;
/**
* An error message if anything occurs during point-cloud data loads
*/
errorMessage: string | null;
}

export interface PointCloudState extends PointCloudProps {
Expand Down Expand Up @@ -273,6 +277,10 @@ export interface PointCloudState extends PointCloudProps {
* Done when the point cloud is re-loaded
*/
reset: () => void;
/**
* Set the error message
*/
setErrorMessage: (message: string | null) => void;
}

/**
Expand Down Expand Up @@ -311,6 +319,7 @@ export type PointCloudStore = ReturnType<typeof createPointCloudStore>;
export const createPointCloudStore = (initProps?: Partial<PointCloudProps>) => {
// The default props irrespective of the number of datasets
const defaultProps: PointCloudProps = {
errorMessage: null,
points: [],
pointData: null,
selectedEventIds: new Set(),
Expand Down Expand Up @@ -365,7 +374,11 @@ export const createPointCloudStore = (initProps?: Partial<PointCloudProps>) => {
});

// Re-compute the point coloring once the granular data is loaded
const pointData = await fetchPointEvents(points.map((p) => p.eventId));
const pointData = await fetchPointEvents(
points.map((p) => p.eventId)
).catch(() => set({ errorMessage: "Failed to load the point events" }));

if (!pointData) return; // The error occurred above

set({
pointData,
Expand Down Expand Up @@ -481,7 +494,11 @@ export const createPointCloudStore = (initProps?: Partial<PointCloudProps>) => {
setDimension: async (dimension) => {
const pointCloudState = get();
set({ dimension, dimensionMetadata: null });
const dimensionMetadata = await fetchDimensionMetadata(dimension);
const dimensionMetadata = await fetchDimensionMetadata(dimension).catch(
() => set({ errorMessage: "Failed to load the dimension metadata" })
);
if (!dimensionMetadata) return; // The error occurred above

set({ dimensionMetadata });
if (dimensionMetadata.categories && dimensionMetadata.categories.length) {
const numCategories = dimensionMetadata.categories.length;
Expand Down Expand Up @@ -558,6 +575,7 @@ export const createPointCloudStore = (initProps?: Partial<PointCloudProps>) => {
setDimensionMetadata: (dimensionMetadata) => set({ dimensionMetadata }),
setUMAPParameters: (umapParameters) => set({ umapParameters }),
setHDBSCANParameters: (hdbscanParameters) => set({ hdbscanParameters }),
setErrorMessage: (errorMessage) => set({ errorMessage }),
});

return create<PointCloudState>()(devtools(pointCloudStore));
Expand Down
2 changes: 1 addition & 1 deletion src/phoenix/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .datasets import Dataset, EmbeddingColumnNames, ExampleDatasets, Schema, load_example
from .session import Session, active_session, close_app, launch_app

__version__ = "0.0.8"
__version__ = "0.0.9"

# module level doc-string
__doc__ = """
Expand Down

0 comments on commit 3b178c8

Please sign in to comment.