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
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ export function ChatBody({
const { conversationCalloutDismissed, tourCalloutDismissed } = useElasticLlmCalloutsStatus(false);

const showElasticLlmCalloutInChat =
elasticManagedLlm &&
!!elasticManagedLlm &&
connectors.selectedConnector === elasticManagedLlm.id &&
!conversationCalloutDismissed &&
tourCalloutDismissed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ export const getElasticManagedLlmConnector = (
connectors: UseGenAIConnectorsResult['connectors'] | undefined
) => {
if (!Array.isArray(connectors) || connectors.length === 0) {
return false;
return undefined;
}

return connectors.filter(
return connectors.find(
(connector) =>
connector.actionTypeId === INFERENCE_CONNECTOR_ACTION_TYPE_ID &&
connector.isPreconfigured &&
connector.config?.provider === 'elastic'
)[0];
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useBoolean } from '@kbn/react-hooks';
import { FormattedMessage } from '@kbn/i18n-react';
import { AIFeatures } from './use_ai_features';
import { useKibana } from '../../../../../hooks/use_kibana';

Expand Down Expand Up @@ -140,17 +141,47 @@ export const GeneratePatternButton = ({
</EuiFlexItem>
)}
</EuiFlexGroup>
{aiFeatures.isManagedAIConnector && !aiFeatures.hasAcknowledgedAdditionalCharges && (
<EuiCallOut onDismiss={() => aiFeatures.acknowledgeAdditionalCharges(true)}>
{i18n.translate(
'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.managedConnectorTooltip',
{
defaultMessage:
'Generating patterns is powered by a preconfigured LLM. Additional charges apply',
}
)}
</EuiCallOut>
)}
</>
);
};

export interface AdditionalChargesCalloutProps {
aiFeatures: AIFeatures;
}

export const AdditionalChargesCallout = ({ aiFeatures }: AdditionalChargesCalloutProps) => {
const {
core: { docLinks },
} = useKibana();

return (
<EuiCallOut onDismiss={() => aiFeatures.acknowledgeAdditionalCharges(true)}>
<FormattedMessage
id="xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.managedConnectorTooltip"
defaultMessage="Elastic Managed LLM is the new default for generating patterns and incurs <costLink>additional charges</costLink>. Other LLM connectors remain available. <learnMoreLink>Learn more</learnMoreLink>"
values={{
costLink: (...chunks: React.ReactNode[]) => (
<EuiLink
href={docLinks?.links?.observability?.elasticManagedLlmUsageCost}
target="_blank"
rel="noopener noreferrer"
external
>
{chunks}
</EuiLink>
),
learnMoreLink: (...chunks: React.ReactNode[]) => (
<EuiLink
href={docLinks?.links?.observability?.elasticManagedLlm}
target="_blank"
rel="noopener noreferrer"
external
>
{chunks}
</EuiLink>
),
}}
/>
</EuiCallOut>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
EuiIcon,
EuiButtonIcon,
EuiFlexItem,
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DraftGrokExpression, GrokCollection } from '@kbn/grok-ui';
Expand All @@ -36,7 +37,7 @@ import useObservable from 'react-use/lib/useObservable';
import { useStreamsEnrichmentSelector } from '../../state_management/stream_enrichment_state_machine';
import { SortableList } from '../../sortable_list';
import { GrokPatternSuggestion } from './grok_pattern_suggestion';
import { GeneratePatternButton } from './generate_pattern_button';
import { GeneratePatternButton, AdditionalChargesCallout } from './generate_pattern_button';
import { useGrokPatternSuggestion } from './use_grok_pattern_suggestion';
import { useSimulatorSelector } from '../../state_management/stream_enrichment_state_machine';
import { selectPreviewDocuments } from '../../state_management/simulation_state_machine/selectors';
Expand Down Expand Up @@ -145,39 +146,49 @@ export const GrokPatternsEditor = () => {
onDismiss={() => refreshSuggestions(null)}
/>
) : (
<EuiFlexGroup gutterSize="l" alignItems="center">
{aiFeatures && (
<>
<EuiFlexGroup gutterSize="l" alignItems="center">
{aiFeatures && (
<EuiFlexItem grow={false}>
<GeneratePatternButton
aiFeatures={aiFeatures}
onClick={(connectorId) =>
refreshSuggestions({
connectorId,
streamName: stream.name,
samples: previewDocuments,
fieldName: fieldValue,
})
}
isLoading={suggestionsState.loading}
isDisabled={!isValidField}
/>
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<GeneratePatternButton
aiFeatures={aiFeatures}
onClick={(connectorId) =>
refreshSuggestions({
connectorId,
streamName: stream.name,
samples: previewDocuments,
fieldName: fieldValue,
})
}
isLoading={suggestionsState.loading}
isDisabled={!isValidField}
/>
<EuiButtonEmpty
data-test-subj="streamsAppGrokPatternsEditorAddPatternButton"
onClick={handleAddPattern}
flush="left"
size="s"
isDisabled={suggestionsState.loading}
>
{i18n.translate(
'xpack.streams.streamDetailView.managementTab.enrichment.processor.grokEditor.addPattern',
{ defaultMessage: 'Add pattern' }
)}
</EuiButtonEmpty>
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="streamsAppGrokPatternsEditorAddPatternButton"
onClick={handleAddPattern}
flush="left"
size="s"
isDisabled={suggestionsState.loading}
>
{i18n.translate(
'xpack.streams.streamDetailView.managementTab.enrichment.processor.grokEditor.addPattern',
{ defaultMessage: 'Add pattern' }
)}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexGroup>
{aiFeatures &&
aiFeatures.isManagedAIConnector &&
!aiFeatures.hasAcknowledgedAdditionalCharges && (
<>
<EuiSpacer size="s" />
<AdditionalChargesCallout aiFeatures={aiFeatures} />
</>
)}
</>
)}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

import useObservable from 'react-use/lib/useObservable';
import { isEmpty } from 'lodash';
import useLocalStorage from 'react-use/lib/useLocalStorage';
import {
ElasticLlmCalloutKey,
useElasticLlmCalloutDismissed,
getElasticManagedLlmConnector,
} from '@kbn/observability-ai-assistant-plugin/public';
import { useKibana } from '../../../../../hooks/use_kibana';

const INTERNAL_INFERENCE_CONNECTORS = ['Elastic-Managed-LLM'];

export function useAIFeatures() {
const {
dependencies: {
Expand All @@ -21,10 +23,10 @@ export function useAIFeatures() {
} = useKibana();
const genAiConnectors = observabilityAIAssistant.useGenAIConnectors();
const license = useObservable(licensing.license$);
const [hasAcknowledgedAdditionalCharges, acknowledgeAdditionalCharges] = useLocalStorage(
'streams:additionalChargesAcknowledged',
false
const [tourCalloutDismissed, setTourCalloutDismissed] = useElasticLlmCalloutDismissed(
ElasticLlmCalloutKey.TOUR_CALLOUT
);
const elasticManagedLlmConnector = getElasticManagedLlmConnector(genAiConnectors.connectors);

if (genAiConnectors.loading) {
return undefined;
Expand All @@ -35,17 +37,17 @@ export function useAIFeatures() {
const couldBeEnabled = Boolean(
license?.hasAtLeast('enterprise') && core.application.capabilities.actions?.save
);
const isManagedAIConnector = genAiConnectors.selectedConnector
? INTERNAL_INFERENCE_CONNECTORS.includes(genAiConnectors.selectedConnector)
const isManagedAIConnector = elasticManagedLlmConnector
? elasticManagedLlmConnector.id === genAiConnectors.selectedConnector
: false;

return {
enabled,
couldBeEnabled,
genAiConnectors,
isManagedAIConnector,
hasAcknowledgedAdditionalCharges,
acknowledgeAdditionalCharges,
hasAcknowledgedAdditionalCharges: tourCalloutDismissed,
acknowledgeAdditionalCharges: setTourCalloutDismissed,
};
}

Expand Down