From d8c17d563843880be350a26b3f75053b49cdfc3f Mon Sep 17 00:00:00 2001 From: Alex Acebo Date: Tue, 12 Nov 2024 12:51:52 -0800 Subject: [PATCH 1/6] update types --- js/packages/teams-ai/src/Application.ts | 6 ++- js/packages/teams-ai/src/StreamingResponse.ts | 2 +- js/packages/teams-ai/src/Utilities.spec.ts | 12 ++--- .../teams-ai/src/actions/SayCommand.spec.ts | 2 +- .../teams-ai/src/actions/SayCommand.ts | 2 +- .../teams-ai/src/types/ClientCitation.ts | 48 +++++++++++++++---- 6 files changed, 53 insertions(+), 19 deletions(-) diff --git a/js/packages/teams-ai/src/Application.ts b/js/packages/teams-ai/src/Application.ts index 2dd9b59f0..a338fcd44 100644 --- a/js/packages/teams-ai/src/Application.ts +++ b/js/packages/teams-ai/src/Application.ts @@ -149,12 +149,14 @@ export interface FeedbackLoopData { /** * 'like' or 'dislike' */ - reaction: string; + reaction: 'like' | 'dislike'; + /** * The response the user provides when prompted with "What did you like/dislike?" after pressing one of the feedback buttons. */ - feedback: string; + feedback: string | Record; }; + /** * The activity ID that the feedback was provided on. */ diff --git a/js/packages/teams-ai/src/StreamingResponse.ts b/js/packages/teams-ai/src/StreamingResponse.ts index 467685933..0d42a76f5 100644 --- a/js/packages/teams-ai/src/StreamingResponse.ts +++ b/js/packages/teams-ai/src/StreamingResponse.ts @@ -119,7 +119,7 @@ export class StreamingResponse { for (const citation of citations) { const clientCitation: ClientCitation = { '@type': 'Claim', - position: `${currPos + 1}`, + position: currPos + 1, appearance: { '@type': 'DigitalDocument', name: citation.title || `Document #${currPos + 1}`, diff --git a/js/packages/teams-ai/src/Utilities.spec.ts b/js/packages/teams-ai/src/Utilities.spec.ts index f019d6d6d..cce713c66 100644 --- a/js/packages/teams-ai/src/Utilities.spec.ts +++ b/js/packages/teams-ai/src/Utilities.spec.ts @@ -87,7 +87,7 @@ describe('Utilities', () => { const citations = [ { '@type': 'Claim', - position: '1', + position: 1, appearance: { '@type': 'DigitalDocument', name: 'the title', @@ -96,7 +96,7 @@ describe('Utilities', () => { }, { '@type': 'Claim', - position: '2', + position: 2, appearance: { '@type': 'DigitalDocument', name: 'the title', @@ -113,7 +113,7 @@ describe('Utilities', () => { const citations = [ { '@type': 'Claim', - position: '1', + position: 1, appearance: { '@type': 'DigitalDocument', name: 'the title', @@ -122,7 +122,7 @@ describe('Utilities', () => { }, { '@type': 'Claim', - position: '2', + position: 2, appearance: { '@type': 'DigitalDocument', name: 'the title', @@ -131,7 +131,7 @@ describe('Utilities', () => { }, { '@type': 'Claim', - position: '3', + position: 3, appearance: { '@type': 'DigitalDocument', name: 'the title', @@ -140,7 +140,7 @@ describe('Utilities', () => { }, { '@type': 'Claim', - position: '4', + position: 4, appearance: { '@type': 'DigitalDocument', name: 'the title', diff --git a/js/packages/teams-ai/src/actions/SayCommand.spec.ts b/js/packages/teams-ai/src/actions/SayCommand.spec.ts index ea70ecbc9..7292b7179 100644 --- a/js/packages/teams-ai/src/actions/SayCommand.spec.ts +++ b/js/packages/teams-ai/src/actions/SayCommand.spec.ts @@ -188,7 +188,7 @@ describe('actions.sayCommand', () => { citation: [ { '@type': 'Claim', - position: '1', + position: 1, appearance: { '@type': 'DigitalDocument', name: 'the title', diff --git a/js/packages/teams-ai/src/actions/SayCommand.ts b/js/packages/teams-ai/src/actions/SayCommand.ts index d16e232e7..78f3dcd74 100644 --- a/js/packages/teams-ai/src/actions/SayCommand.ts +++ b/js/packages/teams-ai/src/actions/SayCommand.ts @@ -37,7 +37,7 @@ export function sayCommand(feedbackLoopEna citations = data.response.context!.citations.map((citation, i) => { const clientCitation: ClientCitation = { '@type': 'Claim', - position: `${i + 1}`, + position: i + 1, appearance: { '@type': 'DigitalDocument', name: citation.title || `Document #${i + 1}`, diff --git a/js/packages/teams-ai/src/types/ClientCitation.ts b/js/packages/teams-ai/src/types/ClientCitation.ts index cea3af835..682a95196 100644 --- a/js/packages/teams-ai/src/types/ClientCitation.ts +++ b/js/packages/teams-ai/src/types/ClientCitation.ts @@ -8,6 +8,29 @@ import { SensitivityUsageInfo } from './SensitivityUsageInfo'; +export type ClientCitationIconName = + 'Microsoft Word' | + 'Microsoft Excel' | + 'Microsoft PowerPoint' | + 'Microsoft OneNote' | + 'Microsoft SharePoint' | + 'Microsoft Visio' | + 'Microsoft Loop' | + 'Microsoft Whiteboard' | + 'Adobe Illustrator' | + 'Adobe Photoshop' | + 'Adobe InDesign' | + 'Adobe Flash' | + 'Sketch' | + 'Source Code' | + 'Image' | + 'GIF' | + 'Video' | + 'Sound' | + 'ZIP' | + 'Text' | + 'PDF'; + /** * Represents a Teams client citation to be included in a message. See Bot messages with AI-generated content for more details. * https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bot-messages-ai-generated-content?tabs=before%2Cbotmessage @@ -21,7 +44,7 @@ export interface ClientCitation { /** * Required. Number and position of the citation. */ - position: string; + position: number; appearance: { /** * Required; Must be 'DigitalDocument' @@ -29,12 +52,13 @@ export interface ClientCitation { '@type': 'DigitalDocument'; /** - * Name of the document. + * Name of the document. (max length 80) */ name: string; /** - * Optional; ignored in Teams + * Stringified adaptive card with additional information about the citation. + * It is rendered within the modal. */ text?: string; @@ -44,22 +68,30 @@ export interface ClientCitation { url?: string; /** - * Content of the citation. Must be clipped if longer than 480 characters. + * Content of the citation. (max length 160) + * Must be clipped if longer than 480 characters. */ abstract: string; /** * Used for icon; for now it is ignored. */ - encodingFormat?: 'text/html'; + encodingFormat?: 'application/vnd.microsoft.card.adaptive'; /** - * For now ignored, later used for icon + * Information about the citation’s icon. */ - image?: string; + image?: { + '@type': 'ImageObject'; + + /** + * The image/icon name + */ + name: ClientCitationIconName; + }; /** - * Optional; set by developer + * Optional; set by developer. (max length 3) (max keyword length 28) */ keywords?: string[]; From bf05ad34de536dc7fb2233c3ae6becfc05189bd4 Mon Sep 17 00:00:00 2001 From: Alex Acebo Date: Tue, 12 Nov 2024 12:53:31 -0800 Subject: [PATCH 2/6] run eslint --- js/packages/teams-ai/src/AI.ts | 2 +- js/packages/teams-ai/src/Application.ts | 2 +- .../teams-ai/src/types/ClientCitation.ts | 42 +++++++++---------- .../f.chatModeration/src/bot.ts | 2 +- .../i.teamsChefBot-streaming/src/index.ts | 8 ++-- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/js/packages/teams-ai/src/AI.ts b/js/packages/teams-ai/src/AI.ts index d04bb3682..ab7114f42 100644 --- a/js/packages/teams-ai/src/AI.ts +++ b/js/packages/teams-ai/src/AI.ts @@ -121,7 +121,7 @@ export class AI { * A text string that can be returned from an action to stop the AI system from continuing * to execute the current plan. * @remarks - * This command is incompatible and should not be used with `tools` augmentation + * This command is incompatible and should not be used with `tools` augmentation */ public static readonly StopCommandName = actions.StopCommandName; diff --git a/js/packages/teams-ai/src/Application.ts b/js/packages/teams-ai/src/Application.ts index a338fcd44..0dcbf58a7 100644 --- a/js/packages/teams-ai/src/Application.ts +++ b/js/packages/teams-ai/src/Application.ts @@ -156,7 +156,7 @@ export interface FeedbackLoopData { */ feedback: string | Record; }; - + /** * The activity ID that the feedback was provided on. */ diff --git a/js/packages/teams-ai/src/types/ClientCitation.ts b/js/packages/teams-ai/src/types/ClientCitation.ts index 682a95196..1c7202c39 100644 --- a/js/packages/teams-ai/src/types/ClientCitation.ts +++ b/js/packages/teams-ai/src/types/ClientCitation.ts @@ -9,27 +9,27 @@ import { SensitivityUsageInfo } from './SensitivityUsageInfo'; export type ClientCitationIconName = - 'Microsoft Word' | - 'Microsoft Excel' | - 'Microsoft PowerPoint' | - 'Microsoft OneNote' | - 'Microsoft SharePoint' | - 'Microsoft Visio' | - 'Microsoft Loop' | - 'Microsoft Whiteboard' | - 'Adobe Illustrator' | - 'Adobe Photoshop' | - 'Adobe InDesign' | - 'Adobe Flash' | - 'Sketch' | - 'Source Code' | - 'Image' | - 'GIF' | - 'Video' | - 'Sound' | - 'ZIP' | - 'Text' | - 'PDF'; + | 'Microsoft Word' + | 'Microsoft Excel' + | 'Microsoft PowerPoint' + | 'Microsoft OneNote' + | 'Microsoft SharePoint' + | 'Microsoft Visio' + | 'Microsoft Loop' + | 'Microsoft Whiteboard' + | 'Adobe Illustrator' + | 'Adobe Photoshop' + | 'Adobe InDesign' + | 'Adobe Flash' + | 'Sketch' + | 'Source Code' + | 'Image' + | 'GIF' + | 'Video' + | 'Sound' + | 'ZIP' + | 'Text' + | 'PDF'; /** * Represents a Teams client citation to be included in a message. See Bot messages with AI-generated content for more details. diff --git a/js/samples/03.ai-concepts/f.chatModeration/src/bot.ts b/js/samples/03.ai-concepts/f.chatModeration/src/bot.ts index beaa4e908..1c8244e75 100644 --- a/js/samples/03.ai-concepts/f.chatModeration/src/bot.ts +++ b/js/samples/03.ai-concepts/f.chatModeration/src/bot.ts @@ -68,7 +68,7 @@ if (process.env.OPENAI_KEY) { category: 'Violence', severity: ModerationSeverity.High } - ], + ] // haltOnBlocklistHit: true, // blocklistNames: [] // Text blocklist Name. Only support following characters: 0-9 A-Z a-z - . _ ~. You could attach multiple lists name here. }); diff --git a/js/samples/04.ai-apps/i.teamsChefBot-streaming/src/index.ts b/js/samples/04.ai-apps/i.teamsChefBot-streaming/src/index.ts index 7ef8ff0f4..0139657e4 100644 --- a/js/samples/04.ai-apps/i.teamsChefBot-streaming/src/index.ts +++ b/js/samples/04.ai-apps/i.teamsChefBot-streaming/src/index.ts @@ -142,8 +142,8 @@ const app = new Application({ storage, ai: { planner, - enable_feedback_loop: true, - }, + enable_feedback_loop: true + } }); // Register your data source with planner @@ -175,8 +175,8 @@ app.ai.action(AI.FlaggedOutputActionName, async (context: TurnContext, state: Ap }); app.feedbackLoop(async (context, state, feedbackLoopData) => { - console.log("Feedback loop triggered"); - }); + console.log('Feedback loop triggered'); +}); // Listen for incoming server requests. server.post('/api/messages', async (req, res) => { From 8dbb1fcc988cab17c9fa154e5f29f8628e30ee36 Mon Sep 17 00:00:00 2001 From: Alex Acebo Date: Tue, 12 Nov 2024 13:59:55 -0800 Subject: [PATCH 3/6] add messages invoke handler --- js/packages/teams-ai/src/Application.ts | 11 +++ js/packages/teams-ai/src/Messages.spec.ts | 56 ++++++++++++ js/packages/teams-ai/src/Messages.ts | 100 ++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 js/packages/teams-ai/src/Messages.spec.ts create mode 100644 js/packages/teams-ai/src/Messages.ts diff --git a/js/packages/teams-ai/src/Application.ts b/js/packages/teams-ai/src/Application.ts index 0dcbf58a7..d8512ec8b 100644 --- a/js/packages/teams-ai/src/Application.ts +++ b/js/packages/teams-ai/src/Application.ts @@ -23,6 +23,7 @@ import { ReadReceiptInfo } from 'botframework-connector'; import { AdaptiveCards, AdaptiveCardsOptions } from './AdaptiveCards'; import { AI, AIOptions } from './AI'; import { Meetings } from './Meetings'; +import { Messages } from './Messages'; import { MessageExtensions } from './MessageExtensions'; import { TaskModules, TaskModulesOptions } from './TaskModules'; import { AuthenticationManager, AuthenticationOptions } from './authentication/Authentication'; @@ -243,6 +244,7 @@ export class Application { private readonly _invokeRoutes: AppRoute[] = []; private readonly _adaptiveCards: AdaptiveCards; private readonly _meetings: Meetings; + private readonly _messages: Messages; private readonly _messageExtensions: MessageExtensions; private readonly _taskModules: TaskModules; private readonly _ai?: AI; @@ -285,6 +287,7 @@ export class Application { } this._adaptiveCards = new AdaptiveCards(this); + this._messages = new Messages(this); this._messageExtensions = new MessageExtensions(this); this._meetings = new Meetings(this); this._taskModules = new TaskModules(this); @@ -352,6 +355,14 @@ export class Application { return this._authentication; } + /** + * Fluent interface for accessing Messages specific features. + * @returns {Messages} The Messages instance. + */ + public get messages(): Messages { + return this._messages; + } + /** * Fluent interface for accessing Message Extensions' specific features. * @returns {MessageExtensions} The MessageExtensions instance. diff --git a/js/packages/teams-ai/src/Messages.spec.ts b/js/packages/teams-ai/src/Messages.spec.ts new file mode 100644 index 000000000..dc92ba5c5 --- /dev/null +++ b/js/packages/teams-ai/src/Messages.spec.ts @@ -0,0 +1,56 @@ +import sinon from 'sinon'; +import { strict as assert } from 'assert'; +import { ActivityTypes, Channels, INVOKE_RESPONSE_KEY, TestAdapter } from 'botbuilder'; + +import { Application } from './Application'; +import { createTestInvoke } from './internals/testing/TestUtilities'; +import { MessageInvokeNames, Messages } from './Messages'; + +describe('Messages', () => { + const adapter = new TestAdapter(); + let mockApp: Application; + + beforeEach(() => { + mockApp = new Application(); + sinon.stub(mockApp, 'adapter').get(() => adapter); + }); + + it('should exist when Application is instantiated', () => { + assert.notEqual(mockApp.messages, undefined); + assert.equal(mockApp.messages instanceof Messages, true); + }); + + describe(MessageInvokeNames.FETCH_INVOKE_NAME, () => { + it('fetch() with custom RouteSelector handler result is falsy', async () => { + const activity = createTestInvoke(MessageInvokeNames.FETCH_INVOKE_NAME, {}); + activity.channelId = Channels.Msteams; + mockApp.messages.fetch(async (_context, _state, _data) => { + return {}; + }); + + await adapter.processActivity(activity, async (context) => { + await mockApp.run(context); + const response = context.turnState.get(INVOKE_RESPONSE_KEY); + assert.deepEqual(response.value, { + status: 200, + body: { task: { type: 'continue', value: {} } } + }); + }); + }); + + it('fetch() with custom RouteSelector unhappy path', async () => { + const activity = { channelId: Channels.Msteams, type: ActivityTypes.Invoke, name: 'incorrectName' }; + const spy = sinon.spy(async (context, _state, _data) => { + return Promise.resolve(''); + }); + + mockApp.messages.fetch(spy); + + await adapter.processActivity(activity, async (context) => { + await mockApp.run(context); + }); + + assert.equal(spy.called, false); + }); + }); +}); diff --git a/js/packages/teams-ai/src/Messages.ts b/js/packages/teams-ai/src/Messages.ts new file mode 100644 index 000000000..1e00ccd25 --- /dev/null +++ b/js/packages/teams-ai/src/Messages.ts @@ -0,0 +1,100 @@ +/** + * @module teams-ai + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import { + ActivityTypes, + Channels, + INVOKE_RESPONSE_KEY, + InvokeResponse, + TaskModuleResponse, + TaskModuleTaskInfo, + TurnContext +} from 'botbuilder'; +import { Application } from './Application'; +import { TurnState } from './TurnState'; + +export enum MessageInvokeNames { + FETCH_INVOKE_NAME = `message/fetchTask` +} + +/** + * TaskModules class to enable fluent style registration of handlers related to Task Modules. + * @template TState Type of the turn state object being persisted. + */ +export class Messages { + private readonly _app: Application; + + /** + * Creates a new instance of the TaskModules class. + * @param {Application} app Top level application class to register handlers with. + */ + public constructor(app: Application) { + this._app = app; + } + + /** + * Registers a handler to process the initial fetch of the task module. + * @remarks + * Handlers should respond with either an initial TaskInfo object or a string containing + * a message to display to the user. + * @template TData Optional. Type of the data object being passed to the handler. + * @param {(context: TurnContext, state: TState, data: TData) => Promise} handler - Function to call when the handler is triggered. + * @param {TurnContext} handler.context - Context for the current turn of conversation with the user. + * @param {TState} handler.state - Current state of the turn. + * @param {TData} handler.data - Data object passed to the handler. + * @returns {Application} The application for chaining purposes. + */ + public fetch = Record>( + handler: (context: TurnContext, state: TState, data: TData) => Promise + ): Application { + this._app.addRoute( + async (context) => { + return ( + context?.activity?.type === ActivityTypes.Invoke && + context?.activity?.name === MessageInvokeNames.FETCH_INVOKE_NAME + ); + }, + async (context, state) => { + if (context?.activity?.channelId === Channels.Msteams) { + const result = await handler(context, state, context.activity.value?.data ?? {}); + + if (!context.turnState.get(INVOKE_RESPONSE_KEY)) { + // Format invoke response + let response: TaskModuleResponse; + if (typeof result == 'string') { + // Return message + response = { + task: { + type: 'message', + value: result + } + }; + } else { + // Return card + response = { + task: { + type: 'continue', + value: result + } + }; + } + + // Queue up invoke response + await context.sendActivity({ + value: { body: response, status: 200 } as InvokeResponse, + type: ActivityTypes.InvokeResponse + }); + } + } + }, + true + ); + + return this._app; + } +} From 1a376e6f5a8bb05d78dd3562a2ccc984e85cf6ac Mon Sep 17 00:00:00 2001 From: Alex Acebo Date: Wed, 13 Nov 2024 10:52:57 -0800 Subject: [PATCH 4/6] Update ClientCitation.ts --- js/packages/teams-ai/src/types/ClientCitation.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/js/packages/teams-ai/src/types/ClientCitation.ts b/js/packages/teams-ai/src/types/ClientCitation.ts index 1c7202c39..cc42f1079 100644 --- a/js/packages/teams-ai/src/types/ClientCitation.ts +++ b/js/packages/teams-ai/src/types/ClientCitation.ts @@ -68,13 +68,12 @@ export interface ClientCitation { url?: string; /** - * Content of the citation. (max length 160) - * Must be clipped if longer than 480 characters. + * Extract of the referenced content. (max length 160) */ abstract: string; /** - * Used for icon; for now it is ignored. + * Encoding format of the ` citation.appearance.text` field. */ encodingFormat?: 'application/vnd.microsoft.card.adaptive'; From 896f31ea525148e0e23c5c1a66dc65e6896899e4 Mon Sep 17 00:00:00 2001 From: Alex Acebo Date: Mon, 25 Nov 2024 11:51:44 -0800 Subject: [PATCH 5/6] add way to specify feedback loop type --- js/packages/teams-ai/src/AI.ts | 17 ++++++++++++++++- js/packages/teams-ai/src/actions/SayCommand.ts | 13 +++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/js/packages/teams-ai/src/AI.ts b/js/packages/teams-ai/src/AI.ts index ab7114f42..a8244e4ce 100644 --- a/js/packages/teams-ai/src/AI.ts +++ b/js/packages/teams-ai/src/AI.ts @@ -68,6 +68,12 @@ export interface AIOptions { * https://github.com/microsoft/teams-ai/blob/main/getting-started/CONCEPTS/POWERED-BY-AI.md */ enable_feedback_loop?: boolean; + + /** + * Optional. Only used when `enable_feedback_loop` == `true`. When set to `custom` the user will be presented with a text input + * to provide feedback. + */ + feedback_loop_type?: 'default' | 'custom'; } /** @@ -104,6 +110,12 @@ export interface ConfiguredAIOptions { * If true, the AI system will enable the feedback loop in Teams that allows a user to give thumbs up or down to a response. */ enable_feedback_loop: boolean; + + /** + * Optional. Only used when `enable_feedback_loop` == `true`. When set to `custom` the user will be presented with a text input + * to provide feedback. + */ + feedback_loop_type?: 'default' | 'custom'; } /** @@ -225,8 +237,11 @@ export class AI { this.defaultAction(AI.HttpErrorActionName, actions.httpError()); this.defaultAction(AI.PlanReadyActionName, actions.planReady()); this.defaultAction(AI.DoCommandActionName, actions.doCommand()); - this.defaultAction(AI.SayCommandActionName, actions.sayCommand(this._options.enable_feedback_loop)); this.defaultAction(AI.TooManyStepsActionName, actions.tooManySteps()); + this.defaultAction(AI.SayCommandActionName, actions.sayCommand( + this._options.enable_feedback_loop, + this._options.feedback_loop_type || 'default' + )); } /** diff --git a/js/packages/teams-ai/src/actions/SayCommand.ts b/js/packages/teams-ai/src/actions/SayCommand.ts index 78f3dcd74..48220bb40 100644 --- a/js/packages/teams-ai/src/actions/SayCommand.ts +++ b/js/packages/teams-ai/src/actions/SayCommand.ts @@ -17,7 +17,10 @@ import { AIEntity, ClientCitation } from '../types'; * @param {boolean} feedbackLoopEnabled - If true, the feedback loop UI for Teams will be enabled. * @returns {''} - An empty string. */ -export function sayCommand(feedbackLoopEnabled: boolean = false) { +export function sayCommand( + feedbackLoopEnabled: boolean = false, + feedbackLoopType: 'default' | 'custom' = 'default', +) { return async (context: TurnContext, _state: TState, data: PredictedSayCommand) => { if (!data.response?.content) { return ''; @@ -54,6 +57,11 @@ export function sayCommand(feedbackLoopEna // If there are citations, filter out the citations unused in content. const referencedCitations = citations ? Utilities.getUsedCitations(contentText, citations) : undefined; + const channelData = feedbackLoopEnabled && feedbackLoopType ? { + feedbackLoop: { + type: feedbackLoopType + } + } : { feedbackLoopEnabled }; const entities: AIEntity[] = [ { @@ -65,10 +73,11 @@ export function sayCommand(feedbackLoopEna ...(referencedCitations ? { citation: referencedCitations } : {}) } ]; + const activity: Partial = { type: ActivityTypes.Message, text: contentText, - ...(isTeamsChannel ? { channelData: { feedbackLoopEnabled } } : {}), + ...(isTeamsChannel ? channelData : {}), entities: entities }; From 62fc27a7a67121794f68fdfef422767ca4c543d4 Mon Sep 17 00:00:00 2001 From: Alex Acebo Date: Tue, 26 Nov 2024 11:14:43 -0800 Subject: [PATCH 6/6] fix channel data --- js/packages/teams-ai/src/actions/SayCommand.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/packages/teams-ai/src/actions/SayCommand.ts b/js/packages/teams-ai/src/actions/SayCommand.ts index 48220bb40..8af08452a 100644 --- a/js/packages/teams-ai/src/actions/SayCommand.ts +++ b/js/packages/teams-ai/src/actions/SayCommand.ts @@ -15,6 +15,7 @@ import { AIEntity, ClientCitation } from '../types'; /** * @private * @param {boolean} feedbackLoopEnabled - If true, the feedback loop UI for Teams will be enabled. + * @param {'default' | 'custom'} feedbackLoopType - the type of UI to use for feedback loop * @returns {''} - An empty string. */ export function sayCommand( @@ -77,7 +78,7 @@ export function sayCommand( const activity: Partial = { type: ActivityTypes.Message, text: contentText, - ...(isTeamsChannel ? channelData : {}), + ...(isTeamsChannel ? { channelData } : {}), entities: entities };