Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
5b709bc
added streamimg to observability ai insights
yuliia-fryshko Dec 29, 2025
f5b7701
added streamimg to error ai insights
yuliia-fryshko Dec 30, 2025
adfa0d8
added streamimg to alert ai insights
yuliia-fryshko Dec 30, 2025
2b61974
removed unused type and code
yuliia-fryshko Dec 30, 2025
be99599
Merge remote-tracking branch 'upstream/main' into add-streamimg-to-ai…
yuliia-fryshko Jan 2, 2026
d9dda81
Changes from node scripts/lint_ts_projects --fix
kibanamachine Jan 2, 2026
d3826a5
Changes from node scripts/regenerate_moon_projects.js --update
kibanamachine Jan 2, 2026
d86255f
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine Jan 2, 2026
0fba3a9
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 4, 2026
a3ae8ff
use apiclient, rxjs instead of manually handling abort operations
yuliia-fryshko Jan 4, 2026
762e2c7
Changes from node scripts/lint_ts_projects --fix
kibanamachine Jan 4, 2026
43a3347
Changes from node scripts/regenerate_moon_projects.js --update
kibanamachine Jan 4, 2026
a65342d
added hook for handling streaming operations
yuliia-fryshko Jan 4, 2026
d073803
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine Jan 4, 2026
b9100db
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 8, 2026
14613f2
added cursor loader
yuliia-fryshko Jan 8, 2026
42d1a76
added regenerate next to start conversation
yuliia-fryshko Jan 8, 2026
fd3fa36
added a hook use_api_client
yuliia-fryshko Jan 8, 2026
06f3188
fixed merge conflicts
yuliia-fryshko Jan 8, 2026
eb0cdf1
renamed to InsightResponse and InsightStreamEvent
yuliia-fryshko Jan 8, 2026
3693afb
refactor streamimg hook
yuliia-fryshko Jan 8, 2026
5164de5
exported getRequestAbortedSignal from Inference Plugin
yuliia-fryshko Jan 8, 2026
33d14d3
fixed check_types
yuliia-fryshko Jan 8, 2026
92ee2ef
fixed check type
yuliia-fryshko Jan 8, 2026
9dfda7a
Changes from node scripts/lint_ts_projects --fix
kibanamachine Jan 8, 2026
1e8bdcf
Changes from node scripts/regenerate_moon_projects.js --update
kibanamachine Jan 8, 2026
d1247cf
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine Jan 8, 2026
13a658a
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 12, 2026
48e647c
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 12, 2026
fad07af
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 13, 2026
56559bd
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 19, 2026
ba6ed9f
added tests
yuliia-fryshko Jan 19, 2026
314e190
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine Jan 19, 2026
08625df
fixed review comments
yuliia-fryshko Jan 20, 2026
8f62336
removed markdown AST manipulation
yuliia-fryshko Jan 20, 2026
8e2f16a
addressed review comments
yuliia-fryshko Jan 20, 2026
5b2a8eb
fixed merge conflicts
yuliia-fryshko Jan 21, 2026
e14f2d6
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 21, 2026
005bf85
Merge branch 'main' into add-streamimg-to-ai-insight
viduni94 Jan 21, 2026
ea2958c
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 22, 2026
b579083
Start Conversation btn is not available is summary is empty
yuliia-fryshko Jan 22, 2026
43e59c9
fixed tests
yuliia-fryshko Jan 23, 2026
13faa71
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 23, 2026
563b593
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 23, 2026
acd099c
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 23, 2026
ff45961
Merge branch 'main' into add-streamimg-to-ai-insight
yuliia-fryshko Jan 23, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"licensing"
],
"optionalPlugins": ["ml", "spaces"],
"requiredBundles": ["kibanaReact"],
"requiredBundles": ["kibanaReact", "kibanaUtils"],
"extraPublicDirs": []
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ dependsOn:
- '@kbn/licensing-types'
- '@kbn/licensing-plugin'
- '@kbn/observability-nav-icons'
- '@kbn/server-route-repository-client'
- '@kbn/agent-builder-browser'
- '@kbn/sse-utils-server'
- '@kbn/kibana-utils-plugin'
- '@kbn/server-route-repository-client'
tags:
- plugin
- prod
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { EuiThemeProvider } from '@elastic/eui';
import { AIChatExperience } from '@kbn/ai-assistant-common';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { AiInsight } from './ai_insight';
import { useKibana } from '../../hooks/use_kibana';
import { useLicense } from '../../hooks/use_license';
import { useGenAIConnectors } from '../../hooks/use_genai_connectors';
import { useStreamingAiInsight } from '../../hooks/use_streaming_ai_insight';

jest.mock('@kbn/kibana-react-plugin/public', () => ({
useUiSetting$: jest.fn(),
}));

jest.mock('../../hooks/use_kibana');
jest.mock('../../hooks/use_license');
jest.mock('../../hooks/use_genai_connectors');
jest.mock('../../hooks/use_streaming_ai_insight');

const mockUseUiSetting$ = useUiSetting$ as jest.Mock;
const mockUseKibana = useKibana as jest.Mock;
const mockUseLicense = useLicense as jest.Mock;
const mockUseGenAIConnectors = useGenAIConnectors as jest.Mock;
const mockUseStreamingAiInsight = useStreamingAiInsight as jest.Mock;
const mockCreateStream = jest.fn();
const AiInsightTest = AiInsight as React.ComponentType<any>;

const renderComponent = () =>
render(
<EuiThemeProvider>
<AiInsightTest
title="AI Insight"
createStream={mockCreateStream}
buildAttachments={jest.fn().mockReturnValue([])}
/>
</EuiThemeProvider>
);

const mockOpenConversationFlyout = jest.fn();

const baseStreamingState = () => ({
isLoading: false,
error: undefined as string | undefined,
summary: '',
context: '',
wasStopped: false,
fetch: jest.fn(),
stop: jest.fn(),
regenerate: jest.fn(),
});

const createStreamingState = (overrides: Partial<ReturnType<typeof baseStreamingState>> = {}) => ({
...baseStreamingState(),
...overrides,
});

describe('AiInsight', () => {
beforeEach(() => {
jest.clearAllMocks();

mockUseUiSetting$.mockReturnValue([AIChatExperience.Agent]);
mockUseKibana.mockReturnValue({
services: {
agentBuilder: {
openConversationFlyout: mockOpenConversationFlyout,
},
application: {
capabilities: {
agentBuilder: { show: true },
},
},
},
});
mockUseLicense.mockReturnValue({
getLicense: () => ({
hasAtLeast: () => true,
}),
});
mockUseGenAIConnectors.mockReturnValue({
hasConnectors: true,
});
mockUseStreamingAiInsight.mockReturnValue(createStreamingState());
});

it('fetches insights when the accordion is opened', () => {
const fetch = jest.fn();
mockUseStreamingAiInsight.mockReturnValue(createStreamingState({ fetch }));

const { container, unmount } = renderComponent();
const toggle = container.querySelector('[data-test-subj="agentBuilderAiInsight"]');

expect(toggle).toBeTruthy();
fireEvent.click(toggle!);

expect(fetch).toHaveBeenCalledTimes(1);
unmount();
});

it('shows error banner and retries on click', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
it('shows error banner and retries on click', () => {
it('displays an error banner with a retry button when an error occurs that refetches insights when clicked', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OR you could consider splitting this into 2 tests and wrap in a describe block.

Example:

describe('when an error occurs', () => {
  it('displays an error banner', () => { ... });
  it('refetches insights when retry button is clicked', () => { ... });
});

const fetch = jest.fn();
mockUseStreamingAiInsight.mockReturnValue(createStreamingState({ error: 'Boom', fetch }));

const { container, unmount } = renderComponent();
const toggle = container.querySelector('[data-test-subj="agentBuilderAiInsight"]');
fireEvent.click(toggle!);

const retryButton = container.querySelector(
'[data-test-subj="AiInsightErrorBannerRetryButton"]'
);

expect(retryButton).toBeTruthy();
fireEvent.click(retryButton!);

expect(fetch).toHaveBeenCalledTimes(1);
unmount();
});

it('renders regenerate and start conversation actions when summary exists', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This test is testing too many things in a single test. Consider splitting is as follows:

Example:

describe('when a summary has been generated', () => {
  it('displays regenerate and start conversation buttons', () => {
    // Just check buttons exist
  });

  it('calls regenerate when regenerate button is clicked', () => {
    // Test regenerate click
  });

  it('opens the conversation flyout with correct attachments when start conversation is clicked', () => {
    // Test start conversation click
  });
});

const regenerate = jest.fn();
const buildAttachments = jest.fn().mockReturnValue([{ type: 'test', data: {} }]);
mockUseStreamingAiInsight.mockReturnValue(
createStreamingState({ summary: 'Hello world', context: 'context', regenerate })
);

const { container, unmount } = render(
<EuiThemeProvider>
<AiInsightTest
title="AI Insight"
createStream={mockCreateStream}
buildAttachments={buildAttachments}
/>
</EuiThemeProvider>
);

const toggle = container.querySelector('[data-test-subj="agentBuilderAiInsight"]');
fireEvent.click(toggle!);

const regenerateButton = container.querySelector(
'[data-test-subj="observabilityAgentBuilderRegenerateButton"]'
);
fireEvent.click(regenerateButton!);
expect(regenerate).toHaveBeenCalledTimes(1);

const startConversationButton = container.querySelector(
'[data-test-subj="aiAgentStartConversationButton"]'
);
fireEvent.click(startConversationButton!);

expect(buildAttachments).toHaveBeenCalledWith('Hello world', 'context');
expect(mockOpenConversationFlyout).toHaveBeenCalledWith({
newConversation: true,
attachments: [{ type: 'test', data: {} }],
});
unmount();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,26 @@ import {
EuiPanel,
EuiSpacer,
EuiText,
EuiSkeletonText,
EuiMarkdownFormat,
EuiButtonEmpty,
EuiHorizontalRule,
useEuiTheme,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { AIChatExperience } from '@kbn/ai-assistant-common';
import { AI_CHAT_EXPERIENCE_TYPE } from '@kbn/management-settings-ids';
import { EuiMarkdownFormat } from '@elastic/eui';
import type { Observable } from 'rxjs';
import { useKibana } from '../../hooks/use_kibana';
import { useLicense } from '../../hooks/use_license';
import {
useStreamingAiInsight,
type InsightStreamEvent,
} from '../../hooks/use_streaming_ai_insight';
import { useGenAIConnectors } from '../../hooks/use_genai_connectors';
import { StartConversationButton } from './start_conversation_button';
import { AiInsightErrorBanner } from './ai_insight_error_banner';

export interface AiInsightResponse {
summary: string;
context: string;
}
import { useMarkdownPluginsWithCursor, CURSOR } from './loading_cursor';

export interface AiInsightAttachment {
type: string;
Expand All @@ -41,17 +43,13 @@ export interface AiInsightAttachment {

export interface AiInsightProps {
title: string;
fetchInsight: () => Promise<AiInsightResponse>;
createStream: (signal: AbortSignal) => Observable<InsightStreamEvent>;
buildAttachments: (summary: string, context: string) => AiInsightAttachment[];
}

export function AiInsight({ title, fetchInsight, buildAttachments }: AiInsightProps) {
export function AiInsight({ title, createStream, buildAttachments }: AiInsightProps) {
const { euiTheme } = useEuiTheme();
const [isOpen, setIsOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | undefined>(undefined);
const [summary, setSummary] = useState('');
const [context, setContext] = useState('');

const {
services: { agentBuilder, application },
Expand All @@ -68,19 +66,12 @@ export function AiInsight({ title, fetchInsight, buildAttachments }: AiInsightPr
const hasEnterpriseLicense = license?.hasAtLeast('enterprise');
const hasAgentBuilderAccess = application?.capabilities.agentBuilder?.show === true;

const handleFetchInsight = useCallback(async () => {
setIsLoading(true);
setError(undefined);
try {
const response = await fetchInsight();
setSummary(response.summary);
setContext(response.context);
} catch (e) {
setError(e instanceof Error ? e.message : 'Failed to load AI insight');
} finally {
setIsLoading(false);
}
}, [fetchInsight]);
const { isLoading, error, summary, context, wasStopped, fetch, stop, regenerate } =
useStreamingAiInsight(createStream);

const { parsingPluginList, processingPluginList } = useMarkdownPluginsWithCursor(
euiTheme.colors.text
);

const handleStartConversation = useCallback(() => {
if (!agentBuilder?.openConversationFlyout) return;
Expand Down Expand Up @@ -142,31 +133,98 @@ export function AiInsight({ title, fetchInsight, buildAttachments }: AiInsightPr
onToggle={(open) => {
setIsOpen(open);
if (open && !error && !summary && !isLoading) {
handleFetchInsight();
fetch();
}
}}
>
<EuiSpacer size="m" />
<EuiPanel color="subdued">
{isLoading ? (
<EuiSkeletonText lines={3} />
) : error ? (
<AiInsightErrorBanner error={error} onRetry={handleFetchInsight} />
{error ? (
<AiInsightErrorBanner error={error} onRetry={fetch} />
) : (
<EuiMarkdownFormat textSize="s">{summary}</EuiMarkdownFormat>
<EuiText size="s">
<EuiMarkdownFormat
textSize="s"
parsingPluginList={parsingPluginList}
processingPluginList={processingPluginList}
>
{`${summary}${isLoading ? CURSOR : ''}`}
</EuiMarkdownFormat>
</EuiText>
)}
</EuiPanel>

{!isLoading && Boolean(summary && summary.trim()) ? (
<>
<EuiSpacer size="m" />
<EuiFlexGroup justifyContent="flexEnd" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<StartConversationButton onClick={handleStartConversation} />
</EuiFlexItem>
</EuiFlexGroup>
</>
) : null}
{isLoading ? (
<>
<EuiSpacer size="m" />
<EuiHorizontalRule margin="none" />
<EuiSpacer size="s" />
<EuiFlexGroup justifyContent="flexStart" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="observabilityAgentBuilderStopGeneratingButton"
color="text"
iconType="stop"
size="s"
onClick={stop}
>
{i18n.translate(
'xpack.observabilityAgentBuilder.aiInsight.stopGeneratingButton',
{
defaultMessage: 'Stop generating',
}
)}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</>
) : wasStopped ? (
<>
<EuiSpacer size="m" />
<EuiHorizontalRule margin="none" />
<EuiSpacer size="s" />
<EuiFlexGroup justifyContent="flexEnd" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="observabilityAgentBuilderRegenerateButton"
size="s"
iconType="sparkles"
onClick={regenerate}
>
{i18n.translate('xpack.observabilityAgentBuilder.aiInsight.regenerateButton', {
defaultMessage: 'Regenerate',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<StartConversationButton onClick={handleStartConversation} />
</EuiFlexItem>
</EuiFlexGroup>
</>
) : Boolean(summary && summary.trim()) ? (
<>
<EuiSpacer size="m" />
<EuiHorizontalRule margin="none" />
<EuiSpacer size="s" />
<EuiFlexGroup justifyContent="flexEnd" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
Comment on lines +154 to +209

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All the conditions here makes it very hard to read this component.
Can we improve it with something like:

const hasSummary = Boolean(summary?.trim());

const renderFooter = () => {
    if (isLoading) {
      return (
        <EuiFlexGroup justifyContent="flexStart" gutterSize="s" responsive={false}>
          <EuiFlexItem grow={false}>
            <EuiButtonEmpty
              data-test-subj="observabilityAgentBuilderStopGeneratingButton"
              color="text"
              iconType="stop"
              size="s"
              onClick={stop}
            >
              {i18n.translate('xpack.observabilityAgentBuilder.aiInsight.stopGeneratingButton', {
                defaultMessage: 'Stop generating',
              })}
            </EuiButtonEmpty>
          </EuiFlexItem>
        </EuiFlexGroup>
      );
    }

    if (wasStopped) {
      return (
        <EuiFlexGroup justifyContent="flexEnd" gutterSize="s" responsive={false}>
          <EuiFlexItem grow={false}>
            <EuiButtonEmpty
              data-test-subj="observabilityAgentBuilderRegenerateButton"
              size="s"
              iconType="sparkles"
              onClick={regenerate}
            >
              {i18n.translate('xpack.observabilityAgentBuilder.aiInsight.regenerateButton', {
                defaultMessage: 'Regenerate',
              })}
            </EuiButtonEmpty>
          </EuiFlexItem>
          {hasSummary && (
            <EuiFlexItem grow={false}>
              <StartConversationButton onClick={handleStartConversation} />
            </EuiFlexItem>
          )}
        </EuiFlexGroup>
      );
    }

    if (hasSummary) {
      return (
        <EuiFlexGroup justifyContent="flexEnd" gutterSize="s" responsive={false}>
          <EuiFlexItem grow={false}>
            <StartConversationButton onClick={handleStartConversation} />
          </EuiFlexItem>
        </EuiFlexGroup>
      );
    }

    return null;
  };

// already exists
if (
    !hasConnectors ||
    !agentBuilder ||
    !isAgentChatExperienceEnabled ||
    !hasAgentBuilderAccess ||
    !hasEnterpriseLicense
  ) {
    return null;
  }

const footer = renderFooter();

Then in the JSX:

<EuiPanel color="subdued">
          {error ? (
            <AiInsightErrorBanner error={error} onRetry={fetch} />
          ) : (
            <EuiText size="s">
              <EuiMarkdownFormat textSize="s">{summary}</EuiMarkdownFormat>
              {isLoading && <LoadingCursor />}
            </EuiText>
          )}

          {footer && (
            <>
              <EuiSpacer size="m" />
              <EuiHorizontalRule margin="none" />
              <EuiSpacer size="s" />
              {footer}
            </>
          )}
        </EuiPanel>

WDYT?

<EuiButtonEmpty
data-test-subj="observabilityAgentBuilderRegenerateButton"
size="s"
iconType="sparkles"
onClick={regenerate}
>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We decided not to show the Regenerate button if there is no error yeah?
(Because we can't edit the prompt, there is no point in regenerating as the response will most likely be the same. Ideally it should only be shown if the stream was stopped mid response - and not show when the stream completes)

Did that decision change? (Maybe I missed something)

Image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I’m fine with removing it. my understanding was that we wouldn’t want to add it as a separate ticket, because it would make sense to handle it within the prompt edit. But since we’re implementing the stop functionality, it wouldn’t hurt to keep it there anyway - we know that the LLM can give slightly different answers each time we call it.

Sorry if this was my misunderstanding. I will remove it, and thank you for pointing it out.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we know that the LLM can give slightly different answers each time we call it

I don't think a slight difference adds any value without being able to edit the prompt. If we decide to add the prompt editing functionality, we can introduce the Regenerate button at that point. For now, I think it's sufficient to show only when the stream is stopped mid-generation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I removed the Regenerate btn in case we have received a full response

{i18n.translate('xpack.observabilityAgentBuilder.aiInsight.regenerateButton', {
defaultMessage: 'Regenerate',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<StartConversationButton onClick={handleStartConversation} />

@viduni94 viduni94 Jan 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I can start a conversation when the summary is emtpy

  • Click on the insight
  • Immediately click on Stop Generation --> The content is empty
  • "Start conversation" button is shown --> Click that and continue the conversation

Notice how an empty string is passed as the summary

image
Screen.Recording.2026-01-21.at.5.22.07.PM.mov

We shouldn't allow starting a conversation when the summary is empty.

@yuliia-fryshko yuliia-fryshko Jan 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch, @viduni94 ! So, when the user clicked "stop conversation" we should show only "Regenerate " btn (without "Start conversation"), right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should definitely not show "Start conversation" when the summary is empty.

When there's a partial result:

  • Old contextual results show the "Start conversation" button
  • However, I'm not sure whether there's any value in starting a conversation with a partial summary - maybe there is.
image

Maybe worth checking with @isaclfreire

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I definitely think that if the summary is empty, it doesn’t make sense to start the conversation. But when the summary is partial, I think it depends on which parts are available. I don’t have a strong opinion on this.

I’d propose removing the “Start conversation” button when the summary is empty, and keeping it when the summary is partial. That said, I’m flexible and happy to change this now or later.

@viduni94

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Screen.Recording.2026-01-22.at.20.02.37.mov

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sounds good! Let's leave it this way and update later if it comes up.

</EuiFlexItem>
</EuiFlexGroup>
</>
) : null}
</EuiPanel>
</EuiAccordion>
</EuiPanel>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@
* 2.0.
*/

export {
AiInsight,
type AiInsightProps,
type AiInsightResponse,
type AiInsightAttachment,
} from './ai_insight';
export { AiInsight, type AiInsightProps, type AiInsightAttachment } from './ai_insight';
export { AiInsightErrorBanner, type AiInsightErrorBannerProps } from './ai_insight_error_banner';
export { StartConversationButton } from './start_conversation_button';
Loading