From dfcc527938fa70e4405b0da953adb7993ec206b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cluismanez=E2=80=9D?= <“luis.manez@outlook.com”> Date: Fri, 6 Oct 2023 11:42:00 +0200 Subject: [PATCH] Minor UX improvements + Link to community call recording --- .../react-azure-openai-connector/README.md | 2 + .../ourHotelsFinder/OurHotelsFinderWebPart.ts | 2 - .../components/AssistantResponse.tsx | 2 +- .../components/IOurHotelsFinderState.ts | 1 + .../components/MessagesList.tsx | 14 ++++- .../components/OurHotelsFinder.tsx | 24 +++++-- .../components/UserMessage.tsx | 63 ++++++++++++++----- .../models/CompletionsRequestBuilder.ts | 10 +-- .../models/ICompletionsRequest.ts | 4 +- 9 files changed, 90 insertions(+), 32 deletions(-) diff --git a/samples/react-azure-openai-connector/README.md b/samples/react-azure-openai-connector/README.md index 923ba89f1..7f6537be7 100644 --- a/samples/react-azure-openai-connector/README.md +++ b/samples/react-azure-openai-connector/README.md @@ -2,6 +2,8 @@ ## Summary +__UPDATE__: I demo-ed this sample in the Community call. Recording available here: [https://t.co/Ew4XSpU6Yh](https://t.co/Ew4XSpU6Yh) + This (__experimental__) webpart, shows how to use the new feature (_in preview at the moment of building this sample_) Azure OpenAI Data Connectors. ![./assets/react-azure-openai-connector.gif](./assets/react-azure-openai-connector.gif) diff --git a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/OurHotelsFinderWebPart.ts b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/OurHotelsFinderWebPart.ts index acf589447..66c786571 100644 --- a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/OurHotelsFinderWebPart.ts +++ b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/OurHotelsFinderWebPart.ts @@ -49,8 +49,6 @@ export default class OurHotelsFinderWebPart extends BaseClientSideWebPart { if (!!this.context.sdks.microsoftTeams) { // running in Teams, office.com or Outlook return this.context.sdks.microsoftTeams.teamsJs.app.getContext() diff --git a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/AssistantResponse.tsx b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/AssistantResponse.tsx index 10e0bcbf3..19a74d74d 100644 --- a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/AssistantResponse.tsx +++ b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/AssistantResponse.tsx @@ -6,7 +6,7 @@ export interface IAssistantResponseProps { message: string; } -export default class AssitantResponse extends React.Component< +export default class AssistantResponse extends React.Component< IAssistantResponseProps, {} > { diff --git a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/IOurHotelsFinderState.ts b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/IOurHotelsFinderState.ts index cdb663679..40fcd37ed 100644 --- a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/IOurHotelsFinderState.ts +++ b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/IOurHotelsFinderState.ts @@ -3,4 +3,5 @@ import { IChatMessage } from "../models/IChatMessage"; export interface IOurHotelsFinderState { userQuery: string; sessionMessages: IChatMessage[]; + findingHotels: boolean; } diff --git a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/MessagesList.tsx b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/MessagesList.tsx index 07658b036..791f1c4f1 100644 --- a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/MessagesList.tsx +++ b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/MessagesList.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import UserQuestion from "./UserQuestion"; -import AssitantResponse from "./AssistantResponse"; +import AssistantResponse from "./AssistantResponse"; import { IChatMessage } from "../models/IChatMessage"; import { ScrollablePane, ScrollbarVisibility } from '@fluentui/react'; @@ -11,12 +11,22 @@ export interface IMessagesListProps { export default class MessagesList extends React.Component { + public componentDidUpdate(): void { + const scrollContainers = document.querySelectorAll(".ms-ScrollablePane--contentContainer"); + const lastScrollContainer = scrollContainers[scrollContainers.length - 1] as HTMLElement; + + if (lastScrollContainer) { + lastScrollContainer.scrollTop = lastScrollContainer.scrollHeight; + } + } + + public render(): React.ReactElement { const output = this.props.messages.map((m, i) => { if (m.role === 'user') { return } - return + return }); return ( diff --git a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/OurHotelsFinder.tsx b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/OurHotelsFinder.tsx index 28cdfbad9..b2e9adb55 100644 --- a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/OurHotelsFinder.tsx +++ b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/OurHotelsFinder.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import { IOurHotelsFinderProps } from "./IOurHotelsFinderProps"; import MessagesList from "./MessagesList"; -import { Stack } from "@fluentui/react"; +import { Spinner, SpinnerSize, Stack } from "@fluentui/react"; import UserMessage from "./UserMessage"; import { IOurHotelsFinderState } from "./IOurHotelsFinderState"; import CompletionsService from "../services/CompletionsService"; @@ -14,7 +14,8 @@ export default class OurHotelsFinder extends React.Component + {this.state.findingHotels && ( + + + + )} - + ); diff --git a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/UserMessage.tsx b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/UserMessage.tsx index c4782123d..504139e94 100644 --- a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/UserMessage.tsx +++ b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/components/UserMessage.tsx @@ -4,28 +4,57 @@ import * as React from 'react'; export interface IUserMessageProps { onMessageChange: (query: string) => void; sendQuery: () => Promise; + textFieldValue: string; } export default class UserMessage extends React.Component { + private _onChange = ( + ev: React.FormEvent, + newText: string + ): void => { + this.props.onMessageChange(newText); + }; - private _onChange = (ev: React.FormEvent, newText: string): void => { - this.props.onMessageChange(newText); - } + private _handleClick = async (): Promise => { + await this.props.sendQuery(); + }; - private _handleClick = async (): Promise => { - await this.props.sendQuery(); + private _keyDownHandler = async (e: KeyboardEvent): Promise => { + if (e.ctrlKey && e.code === "Enter") { + await this._handleClick(); } + }; - public render(): React.ReactElement { - return ( - - - - - - - - - ); - } + public componentDidMount(): void { + window.addEventListener("keydown", this._keyDownHandler); + } + + public componentWillUnmount(): void { + window.removeEventListener("keydown", this._keyDownHandler); + } + + public render(): React.ReactElement { + return ( + + + + + + + + + ); + } } \ No newline at end of file diff --git a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/models/CompletionsRequestBuilder.ts b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/models/CompletionsRequestBuilder.ts index 4b8f39679..d552a08b0 100644 --- a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/models/CompletionsRequestBuilder.ts +++ b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/models/CompletionsRequestBuilder.ts @@ -13,17 +13,19 @@ export default class CompletionsRequestBuilder { endpoint: Constants.AzureSearchEndpoint, key: Constants.AzureSearchKey, indexName: Constants.AzureSearchIndexName, - semanticConfiguration: "", + //semanticConfiguration: "hotels-index-semantic-config", + //queryType: "semantic", queryType: "simple", fieldsMapping: { contentFieldsSeparator: "\n", - contentFields: ["Description", "HotelName", "Category"], - filepathField: "HotelName", + contentFields: ["Description", "HotelName", "Description_fr", "Category"], + filepathField: "HotelId", titleField: "HotelName", - urlField: "HotelName" + urlField: undefined }, inScope: true, roleInformation: "You are an AI assistant that helps users of a travel agency to find Hotels in our internal company database for our customers." + // "filter": "Tags/any(g:search.in(g, 'pool, view'))", } }], messages: [{ diff --git a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/models/ICompletionsRequest.ts b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/models/ICompletionsRequest.ts index 3a5d52502..580b2d345 100644 --- a/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/models/ICompletionsRequest.ts +++ b/samples/react-azure-openai-connector/src/webparts/ourHotelsFinder/models/ICompletionsRequest.ts @@ -16,7 +16,7 @@ export interface ICompletionsDataSourceParameters { endpoint: string; key: string; indexName: string; - semanticConfiguration: string; + semanticConfiguration?: string; queryType: string; fieldsMapping: ICompletionsDataSourceFieldsMapping; inScope: boolean; @@ -28,7 +28,7 @@ export interface ICompletionsDataSourceFieldsMapping { contentFields: string[]; filepathField: string; titleField: string; - urlField: string; + urlField: string | undefined; } export interface ICompletionsMessage {