Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/state/notebook_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface NotebookStateValue {
runningMemory?: AgenticMemeory;
historyMemory?: AgenticMemeory;
investigationError?: string;
isNotebookOwner: boolean;
isNotebookReadonly: boolean;
}

export class NotebookState extends ObservableState<NotebookStateValue> {
Expand Down
25 changes: 19 additions & 6 deletions public/components/notebooks/components/agentic_notebook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,16 @@

function NotebookComponent({ showPageHeader }: NotebookComponentProps) {
const {
services: { notifications, findingService, chrome, chat, uiSettings, contextProvider },
services: {
notifications,
findingService,
chrome,
chat,
uiSettings,
contextProvider,
workspaces,
},
} = useOpenSearchDashboards<NoteBookServices>();

const [isModalVisible, setIsModalVisible] = useState(false);
const [isReinvestigateModalVisible, setIsReinvestigateModalVisible] = useState(false);
const [modalLayout, setModalLayout] = useState<React.ReactNode>(<EuiOverlayMask />);
Expand All @@ -81,7 +88,7 @@
id: openedNoteId,
paragraphs: paragraphsStates,
isLoading,
isNotebookOwner,
isNotebookReadonly,
} = useObservable(notebookContext.state.getValue$(), notebookContext.state.value);
const paraDivRefs = useRef<Array<HTMLDivElement | null>>([]);

Expand All @@ -107,6 +114,13 @@
closeModal();
};

useEffect(() => {
const subscription = workspaces.currentWorkspace$.subscribe((currentWorkspace) => {
notebookContext.state.updateValue({ isNotebookReadonly: currentWorkspace?.readonly });
});
return () => subscription.unsubscribe();
}, [workspaces.currentWorkspace$, notebookContext.state]);

// Initialize finding integration for automatic UI updates when findings are added
useNotebookFindingIntegration({
findingService,
Expand Down Expand Up @@ -159,7 +173,6 @@
hypotheses: res.hypotheses,
runningMemory: res.runningMemory,
historyMemory: res.historyMemory,
isNotebookOwner: res.isNotebookOwner,
});

// Check if there's an ongoing investigation to continue BEFORE calling start
Expand All @@ -179,7 +192,7 @@
await start({
context: notebookContext.state.value.context.value,
paragraphs: res.paragraphs,
hypotheses: hasOngoingInvestigation ? [{ id: 'placeholder' } as any] : res.hypotheses,

Check warning on line 195 in public/components/notebooks/components/agentic_notebook.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
doInvestigate,
});
})
Expand All @@ -202,9 +215,9 @@
loadNotebook();

// TODO: remove the optional chain after each method
(chrome as any).setIsNavDrawerLocked?.(false);

Check warning on line 218 in public/components/notebooks/components/agentic_notebook.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
const rafId = window.requestAnimationFrame(() => {
chat?.openWindow?.();
(chat as any)?.openWindow?.();

Check warning on line 220 in public/components/notebooks/components/agentic_notebook.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
});
return () => {
window.cancelAnimationFrame(rafId);
Expand Down Expand Up @@ -346,7 +359,7 @@
})
: null}

{!isLoading && !isInvestigating && isNotebookOwner && (
{!isLoading && !isInvestigating && !isNotebookReadonly && (
<>
<EuiSpacer size="s" />
<EuiFlexGroup alignItems="center" gutterSize="none">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
runningMemory,
historyMemory,
investigationError,
isNotebookOwner,
isNotebookReadonly,
} = useObservable(notebookContext.state.getValue$(), notebookContext.state.value);
const history = useHistory();
const [showSteps, setShowSteps] = useState(false);
Expand All @@ -66,7 +66,7 @@
}, [isInvestigating, runningMemory, historyMemory]);

const PERAgentServices = useMemo(() => {
if (!activeMemory?.executorMemoryId || !activeMemory?.memoryContainerId || !isNotebookOwner) {
if (!activeMemory?.executorMemoryId || !activeMemory?.memoryContainerId || isNotebookReadonly) {
return null;
}

Expand All @@ -80,7 +80,7 @@
if (!isInvestigating && activeMemory) {
return false;
}
return !messageService.getMessageValue()?.hits?.hits?.[0]?._source?.structured_data
return !(messageService.getMessageValue() as any)?.hits?.hits?.[0]?._source?.structured_data

Check warning on line 83 in public/components/notebooks/components/hypothesis/hypotheses_panel.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
?.response;
},
activeMemory.memoryContainerId
Expand All @@ -90,10 +90,10 @@
message: messageService,
executorMemory: executorMemoryService,
};
}, [http, activeMemory, isInvestigating, isNotebookOwner]);
}, [http, activeMemory, isInvestigating, isNotebookReadonly]);

const executorMessages$ = useMemo(
() => PERAgentServices?.executorMemory.getMessages$() ?? new BehaviorSubject<any[]>([]),

Check warning on line 96 in public/components/notebooks/components/hypothesis/hypotheses_panel.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
[PERAgentServices]
);

Expand Down Expand Up @@ -173,7 +173,7 @@
return null;
}

const investigationSteps = PERAgentServices && isNotebookOwner && (
const investigationSteps = PERAgentServices && !isNotebookReadonly && (
<EuiAccordion
id="investigation-steps"
buttonContent="Investigation Steps"
Expand Down Expand Up @@ -257,7 +257,7 @@
AI Agent continuously evaluates and ranks hypotheses based on evidence
</EuiText>
</EuiFlexGroup>
{hypotheses?.length && !isInvestigating ? (
{hypotheses?.length && !isInvestigating && !isNotebookReadonly ? (
<HypothesesFeedback
appName={appName}
usageCollection={usageCollection}
Expand All @@ -276,7 +276,7 @@
}}
dataSourceId={context.value.dataSourceId}
currentExecutorMemoryId={activeMemory?.executorMemoryId}
memoryContainerId={activeMemory?.memoryContainerId}
memoryContainerId={activeMemory?.memoryContainerId as string}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const Paragraph = (props: ParagraphProps) => {
const isFindingParagraph =
notebookType !== NotebookType.CLASSIC && paragraph.value.input.inputType === 'MARKDOWN';
let isActionVisible = isClassicNotebook;
if (!isClassicNotebook && isFindingParagraph && context.state.value.isNotebookOwner) {
if (!isClassicNotebook && isFindingParagraph && !context.state.value.isNotebookReadonly) {
isActionVisible = true;
}

Expand Down
4 changes: 2 additions & 2 deletions public/components/notebooks/components/summary_card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const SummaryCard: React.FC<SummaryCardProps> = ({
});
};

const { isNotebookOwner } = useObservable(
const { isNotebookReadonly } = useObservable(
notebookContext.state.getValue$(),
notebookContext.state.value
);
Expand Down Expand Up @@ -93,7 +93,7 @@ export const SummaryCard: React.FC<SummaryCardProps> = ({
<EuiTitle>
<h2 style={{ paddingLeft: '20px' }}>Issue summary and impact</h2>
</EuiTitle>
{isNotebookOwner ? (
{!isNotebookReadonly ? (
<EuiButton
style={{ marginRight: '20px' }}
onClick={() => openReinvestigateModal()}
Expand Down
6 changes: 4 additions & 2 deletions public/hooks/use_investigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@
});

if (batchResult?.paragraphs) {
batchResult.paragraphs.forEach((paragraph: any, index: number) => {

Check warning on line 112 in public/hooks/use_investigation.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
findingId2ParagraphId[sortedFindings[index].id] = paragraph.id;
});

// Run the created paragraphs
const paragraphIds = batchResult.paragraphs.map((p: any) => p.id);

Check warning on line 117 in public/hooks/use_investigation.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
await batchRunParagraphs({ paragraphIds });
}
} catch (e) {
Expand All @@ -137,7 +137,7 @@
}))
.sort((a, b) => b.likelihood - a.likelihood);
try {
await updateHypotheses([...(newHypotheses as any)], true);

Check warning on line 140 in public/hooks/use_investigation.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
} catch (e) {
console.error('Failed to update investigation result', e);
}
Expand Down Expand Up @@ -319,8 +319,10 @@
context.state.updateValue({ investigationError: undefined });

try {
if (!context.state.value.isNotebookOwner) {
throw new Error('Only owner of this notebook can start the investigation');
if (context.state.value.isNotebookReadonly) {
throw new Error(
'Only user with write permission of this notebook can start the investigation'
);
}

if (context.state.value.context.value.initialGoal !== question) {
Expand Down
82 changes: 40 additions & 42 deletions public/hooks/use_notebook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,53 +88,51 @@ export const useNotebook = () => {
isLoading: true,
});

const promise = http
.get<NotebookBackendType & { isNotebookOwner: boolean }>(route)
.then(async (res) => {
let contextPayload = {
...res.context,
};

// try to convert relative time in ppl query to absolute time as
if (res.context?.variables?.pplQuery) {
const pplWithAbsoluteTime = parsePPLQuery(
res.context.variables.pplQuery,
res.context.currentTime
).pplWithAbsoluteTime;
if (pplWithAbsoluteTime !== res.context.variables.pplQuery) {
contextPayload = {
...contextPayload,
variables: {
...contextPayload.variables,
pplQuery: pplWithAbsoluteTime,
},
};
}
}

if (
!res.context?.indexInsight &&
res.context?.index &&
res.context?.timeField &&
res.context?.timeRange
) {
let indexInsight;
try {
indexInsight = await fetchIndexInsights(res.context.index, res.context?.dataSourceId);
} catch (error) {
console.error('Failed to load index insight:', error);
}
const promise = http.get<NotebookBackendType>(route).then(async (res) => {
let contextPayload = {
...res.context,
};

// try to convert relative time in ppl query to absolute time as
if (res.context?.variables?.pplQuery) {
const pplWithAbsoluteTime = parsePPLQuery(
res.context.variables.pplQuery,
res.context.currentTime
).pplWithAbsoluteTime;
if (pplWithAbsoluteTime !== res.context.variables.pplQuery) {
contextPayload = {
...contextPayload,
indexInsight,
variables: {
...contextPayload.variables,
pplQuery: pplWithAbsoluteTime,
},
};
}
return {
...res,
vizPrefix: res.vizPrefix || '',
context: contextPayload,
}

if (
!res.context?.indexInsight &&
res.context?.index &&
res.context?.timeField &&
res.context?.timeRange
) {
let indexInsight;
try {
indexInsight = await fetchIndexInsights(res.context.index, res.context?.dataSourceId);
} catch (error) {
console.error('Failed to load index insight:', error);
}
contextPayload = {
...contextPayload,
indexInsight,
};
});
}
return {
...res,
vizPrefix: res.vizPrefix || '',
context: contextPayload,
};
});

promise.finally(() => {
context.state.updateValue({
Expand Down
1 change: 1 addition & 0 deletions public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export class InvestigationPlugin
findingService,
usageCollection: depsStart.usageCollection,
overlay: depsStart.overlay,
workspaces: coreStart.workspaces,
};
return services;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
return notebooks;
}

export function createNotebook(notebookName: { name: string; context?: any }, userName?: string) {

Check warning on line 43 in server/adaptors/notebooks/saved_objects_notebooks_router.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
const noteObject: NotebookBackendType = {
dateCreated: new Date().toISOString(),
name: notebookName.name,
Expand Down Expand Up @@ -69,6 +69,7 @@
backend: 'kibana_1.0',
paragraphs: fetchedNotebook.paragraphs,
path: name,
owner: fetchedNotebook.owner,
};

return {
Expand Down
13 changes: 4 additions & 9 deletions server/routes/notebooks/notebook_router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
context,
request,
response
): Promise<IOpenSearchDashboardsResponse<any | ResponseError>> => {

Check warning on line 47 in server/routes/notebooks/notebook_router.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
const opensearchNotebooksClient: SavedObjectsClientContract =
context.core.savedObjects.client;
try {
Expand Down Expand Up @@ -219,7 +219,7 @@
request.body.notebookId,
opensearchNotebooksClient
);
noteBookInfo.attributes.savedNotebook = {
(noteBookInfo.attributes as any).savedNotebook = {
...noteBookInfo.attributes.savedNotebook,
...noteObject,
};
Expand Down Expand Up @@ -265,14 +265,9 @@
NOTEBOOK_SAVED_OBJECT,
request.params.noteId
);
const savedNotebook = notebookinfo.attributes.savedNotebook as NotebookBackendType;
const savedNotebook = (notebookinfo as any).attributes.savedNotebook as NotebookBackendType;
return response.ok({
body: {
...savedNotebook,
isNotebookOwner: savedNotebook.owner
? savedNotebook.owner === getUserName(request)
: true,
},
body: savedNotebook,
});
} catch (error) {
return response.custom({
Expand Down Expand Up @@ -306,7 +301,7 @@
request.body.noteId
);
const createCloneNotebook = cloneNotebook(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe an existing issue, do we need to add the owner to the cloned notebook?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

getNotebook.attributes.savedNotebook,
(getNotebook as any).attributes.savedNotebook,
request.body.name
);
const createdNotebook = await opensearchNotebooksClient.create(
Expand Down
Loading