Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add generated caution text to the messages with corresponding keywords #5298

Merged
merged 5 commits into from
Sep 16, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
- Moved from `[email protected]` to `@redux/[email protected]`, in PR [#5292](https://github.com/microsoft/BotFramework-WebChat/pull/5292), by [@compulim](https://github.com/compulim)
- Enhanced the visual presentation of the Fluent theme copilot variant, in PR [#5293](https://github.com/microsoft/BotFramework-WebChat/pull/5293), by [@OEvgeny](https://github.com/OEvgeny)
- Refactored spacing and layout for copilot variant in Fluent theme, improving visual consistency, in PR [#5296](https://github.com/microsoft/BotFramework-WebChat/pull/5296), by [@OEvgeny](https://github.com/OEvgeny)
- Added a content generated badge to AI-generated messages, in PR [#5298](https://github.com/microsoft/BotFramework-WebChat/pull/5298), by [@OEvgeny](https://github.com/OEvgeny)

### Fixed

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 9 additions & 8 deletions __tests__/html/fluentTheme/side-by-side.wide.dark.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
const timestamp = () => new Date(timestampStart += 100).toISOString();

const [leftTranscriptIndex = 0, rightTranscriptIndex = 1] = new URLSearchParams(location.search).getAll('transcript');
const [leftVariant = 'fluent', rightVariant = 'copilot'] = new URLSearchParams(location.search).getAll('variant');

const sendBoxIndexes = new URLSearchParams(location.search).getAll('focus');

Expand All @@ -97,7 +98,6 @@
{
timestamp: timestamp(),
from: { role: 'bot' },
entities: [aiMessageEntity],
id: '1.0',
text: `Great! Your copilot will maintain a polite, friendly, professional, and fun while assisting team members with onboarding tasks.
Are there any topics or tasks this copilot shouldn’t help with or talk about?`,
Expand All @@ -114,7 +114,6 @@
{
timestamp: timestamp(),
from: { role: 'bot' },
entities: [aiMessageEntity],
id: '2.0',
text: 'Is there a website or datasource that has knowledge that will be necessary for Onboarding Buddy?',
type: 'message'
Expand All @@ -130,7 +129,6 @@
{
timestamp: timestamp(),
from: { role: 'bot' },
entities: [aiMessageEntity],
id: '3.0',
text: `https://sharepoint.contoso.com/team/ has been added as a Knowledge source.
You can preview your extension at any time.`,
Expand Down Expand Up @@ -271,8 +269,9 @@
from: { role: 'bot' },
entities: [aiMessageEntity],
id: "1.1",
text: "Welcome to the team! I'd be happy to assist you with your onboarding. Let's start with the essentials. Have you received your welcome package yet?",
type: "message"
text: "Welcome to the team! \n\n I'd be happy to assist you with your onboarding. Let's start with the essentials. Have you received your welcome package yet?",
type: "message",
textFormat: "plain"
},
{
timestamp: timestamp(),
Expand Down Expand Up @@ -550,7 +549,7 @@
colorNeutralForeground5: '#424242'
}}>
<div className="webchat-left">
<FluentThemeProvider variant="fluent">
<FluentThemeProvider variant={leftVariant}>
<ReactWebChat
directLine={leftDirectLine}
store={leftStore}
Expand All @@ -564,7 +563,7 @@
</FluentThemeProvider>
</div>
<div className="webchat-right">
<FluentThemeProvider variant="copilot">
<FluentThemeProvider variant={rightVariant}>
<ReactWebChat
directLine={rightDirectLine}
store={rightStore}
Expand Down Expand Up @@ -599,7 +598,9 @@
await host.snapshot();
await host.sendShiftTab();
await host.snapshot();
await host.sendKeys('ARROW_UP');
await host.sendKeys('HOME');
await host.snapshot();
await host.sendKeys('END');
await host.snapshot();
await host.sendKeys('ARROW_UP');
await host.snapshot();
Expand Down
2 changes: 2 additions & 0 deletions __tests__/html/fluentTheme/side-by-side.wide.dark.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ describe('Fluent theme applied', () => {
runHTML('fluentTheme/side-by-side.wide.dark?transcript=0&transcript=3'));
test('side by side left - transcript, right - streaming', () =>
runHTML('fluentTheme/side-by-side.wide.dark?transcript=0&transcript=4'));
test('side by side left - fluent, right - fluent', () =>
runHTML('fluentTheme/side-by-side.wide.dark?transcript=0&transcript=2&focus=1&variant=fluent&variant=fluent'));
});
});
18 changes: 9 additions & 9 deletions __tests__/html/fluentTheme/side-by-side.wide.html
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
const timestamp = () => new Date(timestampStart += 100).toISOString();

const [leftTranscriptIndex = 0, rightTranscriptIndex = 1] = new URLSearchParams(location.search).getAll('transcript');
const [leftVariant = 'fluent', rightVariant = 'copilot'] = new URLSearchParams(location.search).getAll('variant');

const sendBoxIndexes = new URLSearchParams(location.search).getAll('focus');

Expand All @@ -106,7 +107,6 @@
{
timestamp: timestamp(),
from: { role: 'bot' },
entities: [aiMessageEntity],
id: '1.0',
text: `Great! Your copilot will maintain a polite, friendly, professional, and fun while assisting team members with onboarding tasks.
Are there any topics or tasks this copilot shouldn’t help with or talk about?`,
Expand All @@ -123,7 +123,6 @@
{
timestamp: timestamp(),
from: { role: 'bot' },
entities: [aiMessageEntity],
id: '2.0',
text: 'Is there a website or datasource that has knowledge that will be necessary for Onboarding Buddy?',
type: 'message'
Expand All @@ -139,7 +138,6 @@
{
timestamp: timestamp(),
from: { role: 'bot' },
entities: [aiMessageEntity],
id: '3.0',
text: `https://sharepoint.contoso.com/team/ has been added as a Knowledge source.
You can preview your extension at any time.`,
Expand All @@ -165,7 +163,6 @@
{
timestamp: timestamp(),
from: { role: 'bot' },
entities: [aiMessageEntity],
suggestedActions: {
actions: [
{
Expand Down Expand Up @@ -281,8 +278,9 @@
from: { role: 'bot' },
entities: [aiMessageEntity],
id: "1.1",
text: "Welcome to the team! I'd be happy to assist you with your onboarding. Let's start with the essentials. Have you received your welcome package yet?",
type: "message"
text: "Welcome to the team! \n\n I'd be happy to assist you with your onboarding. Let's start with the essentials. Have you received your welcome package yet?",
type: "message",
textFormat: "plain"
},
{
timestamp: timestamp(),
Expand Down Expand Up @@ -532,7 +530,7 @@

const App = () => <>
<div className="webchat-left">
<FluentThemeProvider variant="fluent">
<FluentThemeProvider variant={leftVariant}>
<ReactWebChat
directLine={leftDirectLine}
store={leftStore}
Expand All @@ -546,7 +544,7 @@
</FluentThemeProvider>
</div>
<div className="webchat-right">
<FluentThemeProvider variant="copilot">
<FluentThemeProvider variant={rightVariant}>
<ReactWebChat
directLine={rightDirectLine}
store={rightStore}
Expand Down Expand Up @@ -582,7 +580,9 @@
await host.snapshot();
await host.sendShiftTab();
await host.snapshot();
await host.sendKeys('ARROW_UP');
await host.sendKeys('HOME');
await host.snapshot();
await host.sendKeys('END');
await host.snapshot();
await host.sendKeys('ARROW_UP');
await host.snapshot();
Expand Down
2 changes: 2 additions & 0 deletions __tests__/html/fluentTheme/side-by-side.wide.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ describe('Fluent theme applied', () => {
runHTML('fluentTheme/side-by-side.wide?transcript=0&transcript=3'));
test('side by side left - transcript, right - streaming', () =>
runHTML('fluentTheme/side-by-side.wide?transcript=0&transcript=4'));
test('side by side left - fluent, right - fluent', () =>
runHTML('fluentTheme/side-by-side.wide?transcript=0&transcript=2&focus=1&variant=fluent&variant=fluent'));
});
38 changes: 32 additions & 6 deletions packages/component/src/Attachment/Text/TextContent.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,56 @@
import React, { type FC, memo } from 'react';
import React, { memo, useMemo } from 'react';
import classNames from 'classnames';
import { hooks } from 'botframework-webchat-api';
import { type WebChatActivity } from 'botframework-webchat-core';

import isAIGeneratedActivity from './private/isAIGeneratedActivity';
import MarkdownTextContent from './private/MarkdownTextContent';
import PlainTextContent from './private/PlainTextContent';
import CustomPropertyNames from '../../Styles/CustomPropertyNames';
import useStyleToEmotionObject from '../../hooks/internal/useStyleToEmotionObject';
import useRenderMarkdownAsHTML from '../../hooks/useRenderMarkdownAsHTML';

import { type WebChatActivity } from 'botframework-webchat-core';
const { useLocalizer } = hooks;

type Props = Readonly<{
activity: WebChatActivity;
contentType?: string;
text: string;
}>;

const TextContent: FC<Props> = memo(({ activity, contentType = 'text/plain', text }: Props) => {
const generatedBadgeStyle = {
'&.webchat__text-content__generated-badge': {
color: `var(${CustomPropertyNames.ColorSubtle})`,
fontSize: `var(${CustomPropertyNames.FontSizeSmall})`
}
};

const TextContent = memo(({ activity, contentType = 'text/plain', text }: Props) => {
const supportMarkdown = !!useRenderMarkdownAsHTML('message activity');
const localize = useLocalizer();
const generatedBadgeClassName = useStyleToEmotionObject()(generatedBadgeStyle) + '';

const generatedBadge = useMemo(
() =>
isAIGeneratedActivity(activity) && (
<div className={classNames('webchat__text-content__generated-badge', generatedBadgeClassName)}>
{localize('ACTIVITY_CONTENT_CAUTION')}
</div>
),
[activity, generatedBadgeClassName, localize]
);

return text ? (
contentType === 'text/markdown' && supportMarkdown ? (
<MarkdownTextContent activity={activity} markdown={text} />
<MarkdownTextContent activity={activity} markdown={text}>
{generatedBadge}
</MarkdownTextContent>
) : (
<PlainTextContent text={text} />
<PlainTextContent text={text}>{generatedBadge}</PlainTextContent>
)
OEvgeny marked this conversation as resolved.
Show resolved Hide resolved
) : null;
});

TextContent.defaultProps = { contentType: 'text/plain' };
TextContent.displayName = 'TextContent';

export default TextContent;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import classNames from 'classnames';
import type { Definition } from 'mdast';
import { fromMarkdown } from 'mdast-util-from-markdown';
import React, { memo, useCallback, useMemo, type MouseEventHandler } from 'react';
import React, { memo, useCallback, useMemo, type MouseEventHandler, type ReactNode } from 'react';
import { useRefFrom } from 'use-ref-from';

import { LinkDefinitionItem, LinkDefinitions } from '../../../LinkDefinition/index';
Expand All @@ -35,14 +35,15 @@ type Entry = {

type Props = Readonly<{
activity: WebChatActivity;
children?: ReactNode | undefined;
markdown: string;
}>;

function isCitationURL(url: string): boolean {
return onErrorResumeNext(() => new URL(url))?.protocol === 'cite:';
}

const MarkdownTextContent = memo(({ activity, markdown }: Props) => {
const MarkdownTextContent = memo(({ activity, children, markdown }: Props) => {
const [
{
citationModalDialog: citationModalDialogStyleSet,
Expand Down Expand Up @@ -212,6 +213,7 @@ const MarkdownTextContent = memo(({ activity, markdown }: Props) => {
dangerouslySetInnerHTML={dangerouslySetInnerHTML}
onClick={handleClick}
/>
{children}
{!!entries.length && (
<LinkDefinitions<MessageSensitivityLabelProps>
accessoryComponentType={messageSensitivityLabelProps && MessageSensitivityLabel}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import classNames from 'classnames';
import React, { type FC, Fragment, memo } from 'react';
import React, { Fragment, memo, type ReactNode } from 'react';

import useStyleSet from '../../../hooks/useStyleSet';

type Props = Readonly<{ text: string }>;
type Props = Readonly<{ children?: ReactNode | undefined; text: string }>;

const PlainTextContent: FC<Props> = memo(({ text }: Props) => {
const PlainTextContent = memo(({ children, text }: Props) => {
const [{ textContent: textContentStyleSet }] = useStyleSet();

return (
Expand All @@ -18,6 +18,13 @@ const PlainTextContent: FC<Props> = memo(({ text }: Props) => {
{line.trim()}
</p>
))}
{children && (
<div
className={classNames('webchat__text-content', 'webchat__text-content--children', textContentStyleSet + '')}
>
{children}
</div>
)}
</Fragment>
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { getOrgSchemaMessage, type WebChatActivity } from 'botframework-webchat-core';

export default function isAIGeneratedActivity(activity: undefined | WebChatActivity) {
return !!(activity && getOrgSchemaMessage(activity?.entities || [])?.keywords?.includes('AIGeneratedContent'));
OEvgeny marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
&:has(:global(.webchat__bubble--from-user)) :global(.webchat__bubble) {
margin-block-end: var(--webchat-spacingVerticalM);
}

/* Hide generated badge as it is in the copilot header */
:global(.webchat__bubble .webchat__text-content .webchat__text-content__generated-badge) {
display: none;
}
}

/* Decorator copilot variant which has bot message */
Expand Down Expand Up @@ -147,6 +152,30 @@
min-height: auto;
padding-block: var(--webchat__bubble--block-padding);
padding-inline: var(--webchat__bubble--inline-padding);

&:empty {
padding-block-end: 0;
}

+:global(.webchat__text-content) {
margin-top: calc(var(--webchat__bubble--block-padding) * -1);
}
}

/* Message bubble text content generated badge */
:global(.webchat-fluent) .activity-decorator :global(.webchat__stacked-layout .webchat__bubble .webchat__text-content__generated-badge) {
align-items: center;
align-self: flex-start;
background-color: var(--webchat-colorNeutralBackground5);
border-radius: var(--webchat-borderRadiusMedium);
box-sizing: border-box;
color: var(--webchat-colorNeutralForeground3);
cursor: default;
display: inline-flex;
font-size: var(--webchat-fontSizeBase100);
height: 16px;
line-height: var(--webchat-lineHeightBase100);
padding-inline: var(--webchat-spacingHorizontalXS);
}

/* Message bubble attachment content */
Expand Down
Loading