webpart working
This commit is contained in:
parent
c5e92576da
commit
09c45abe08
|
@ -31,7 +31,8 @@ export default class OurHotelsFinderWebPart extends BaseClientSideWebPart<IOurHo
|
|||
isDarkTheme: this._isDarkTheme,
|
||||
environmentMessage: this._environmentMessage,
|
||||
hasTeamsContext: !!this.context.sdks.microsoftTeams,
|
||||
userDisplayName: this.context.pageContext.user.displayName
|
||||
userDisplayName: this.context.pageContext.user.displayName,
|
||||
httpClient: this.context.httpClient
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import {HttpClient} from '@microsoft/sp-http';
|
||||
|
||||
export interface IOurHotelsFinderProps {
|
||||
description: string;
|
||||
isDarkTheme: boolean;
|
||||
environmentMessage: string;
|
||||
hasTeamsContext: boolean;
|
||||
userDisplayName: string;
|
||||
httpClient: HttpClient;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import { IChatMessage } from "../models/IChatMessage";
|
||||
|
||||
export interface IOurHotelsFinderState {
|
||||
userQuery: string;
|
||||
sessionMessages: IChatMessage[];
|
||||
}
|
|
@ -2,39 +2,60 @@ import * as React from "react";
|
|||
//import styles from './OurHotelsFinder.module.scss';
|
||||
import { IOurHotelsFinderProps } from "./IOurHotelsFinderProps";
|
||||
import MessagesList from "./MessagesList";
|
||||
import { IChatMessage } from "../models/IChatMessage";
|
||||
import { DirectionalHint, Icon, Stack, Text, TooltipHost } from "@fluentui/react";
|
||||
import UserMessage from "./UserMessage";
|
||||
import { IOurHotelsFinderState } from "./IOurHotelsFinderState";
|
||||
import CompletionsService from "../services/CompletionsService";
|
||||
import { ICompletionsResponse } from "../models/ICompletionsResponse";
|
||||
|
||||
export default class OurHotelsFinder extends React.Component<IOurHotelsFinderProps, IOurHotelsFinderState> {
|
||||
|
||||
constructor(props: IOurHotelsFinderProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
userQuery: '',
|
||||
sessionMessages: []
|
||||
};
|
||||
}
|
||||
|
||||
private _onUserQueryChange = (newQuery: string): void => {
|
||||
this.setState({
|
||||
userQuery: newQuery
|
||||
});
|
||||
}
|
||||
|
||||
private _onQuerySent = async (): Promise<void> => {
|
||||
console.log(this.state.userQuery);
|
||||
console.log(this.state.sessionMessages);
|
||||
|
||||
const completionsService: CompletionsService = new CompletionsService(this.props.httpClient);
|
||||
|
||||
const response: ICompletionsResponse =
|
||||
await completionsService.getCompletions(this.state.sessionMessages, this.state.userQuery);
|
||||
|
||||
console.log(response);
|
||||
|
||||
const responseMessages = response.choices[0].messages.filter(m => {
|
||||
return m.role === 'assistant';
|
||||
});
|
||||
|
||||
const message = responseMessages[0];
|
||||
|
||||
const tempMessages = this.state.sessionMessages;
|
||||
tempMessages.push({
|
||||
role: 'user', text: this.state.userQuery
|
||||
});
|
||||
tempMessages.push({
|
||||
role: 'assistant', text: message.content
|
||||
});
|
||||
|
||||
this.setState({
|
||||
sessionMessages: tempMessages
|
||||
});
|
||||
}
|
||||
|
||||
export default class OurHotelsFinder extends React.Component<
|
||||
IOurHotelsFinderProps,
|
||||
{}
|
||||
> {
|
||||
public render(): React.ReactElement<IOurHotelsFinderProps> {
|
||||
// const {
|
||||
// hasTeamsContext,
|
||||
// } = this.props;
|
||||
|
||||
const fakeMessages: IChatMessage[] = [
|
||||
{
|
||||
role: "user",
|
||||
text: "Do you know if using Fluent UI components, you can implement like the usual Chat interface? like the one uses ChatGPT website, or WhatsApp?",
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
text: "Glad you asked. This is a common question that I have no idea how to do... bye!",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
text: "Icon looks a bit small, can I make it a bit bigger?",
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
text: "In this example, I have increased the size of the icon to 24 pixels. You can adjust the fontSize value to increase or decrease the size of the icon as needed.",
|
||||
},
|
||||
{ role: "user", text: "Another question" },
|
||||
{ role: "assistant", text: "In this example alksfh ks" },
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: 20 }} style={{ minHeight: "100%" }}>
|
||||
|
@ -69,10 +90,10 @@ export default class OurHotelsFinder extends React.Component<
|
|||
root: { minHeight: "200px", height: "100%", position: "relative" },
|
||||
}}
|
||||
>
|
||||
<MessagesList messages={fakeMessages} />
|
||||
<MessagesList messages={this.state.sessionMessages} />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<UserMessage />
|
||||
<UserMessage onMessageChange={this._onUserQueryChange} sendQuery={this._onQuerySent} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
@ -2,35 +2,28 @@ import { IconButton, Stack, TextField } from '@fluentui/react';
|
|||
import * as React from 'react';
|
||||
|
||||
export interface IUserMessageProps {
|
||||
onMessageChange: (query: string) => void;
|
||||
sendQuery: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface IUserMessageState {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export default class UserMessage extends React.Component<IUserMessageProps, IUserMessageState> {
|
||||
|
||||
constructor(props: IUserMessageProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
message: ''
|
||||
};
|
||||
}
|
||||
export default class UserMessage extends React.Component<IUserMessageProps, {}> {
|
||||
|
||||
private _onChange = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newText: string): void => {
|
||||
this.setState({
|
||||
message: newText
|
||||
});
|
||||
this.props.onMessageChange(newText);
|
||||
}
|
||||
|
||||
private _handleClick = async (): Promise<void> => {
|
||||
await this.props.sendQuery();
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IUserMessageProps> {
|
||||
return (
|
||||
<Stack horizontal tokens={{ childrenGap: 20 }}>
|
||||
<Stack horizontal tokens={{ childrenGap: 5 }}>
|
||||
<Stack.Item grow={1}>
|
||||
<TextField multiline autoAdjustHeight onChange={this._onChange} label="User message" placeholder="Type user query here." />
|
||||
</Stack.Item>
|
||||
<Stack.Item align="end">
|
||||
<IconButton iconProps={{ iconName: 'Send' }} title="Send" ariaLabel="Send" />
|
||||
<IconButton iconProps={{ iconName: 'Send' }} title="Send" ariaLabel="Send" onClick={this._handleClick} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import { ICompletionsRequest } from "./ICompletionsRequest";
|
||||
import Constants from '../Constants';
|
||||
|
||||
export default class CompletionsRequestBuilder {
|
||||
|
||||
private _completionsRequest: ICompletionsRequest
|
||||
|
||||
public constructor(deployment: "gpt-35-turbo-model-deployment" | "gpt-4") {
|
||||
this._completionsRequest = {
|
||||
dataSources: [{
|
||||
type: "AzureCognitiveSearch",
|
||||
parameters: {
|
||||
endpoint: "https://srch-atlas-cp-dev.search.windows.net",
|
||||
key: Constants.AzureSearchKey,
|
||||
indexName: "hotels-openai-test-index-lml",
|
||||
semanticConfiguration: "",
|
||||
queryType: "simple",
|
||||
fieldsMapping: {
|
||||
contentFieldsSeparator: "\n",
|
||||
contentFields: ["Description", "HotelName", "Category"],
|
||||
filepathField: "HotelName",
|
||||
titleField: "HotelName",
|
||||
urlField: "HotelName"
|
||||
},
|
||||
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."
|
||||
}
|
||||
}],
|
||||
messages: [{
|
||||
role: "system",
|
||||
content: "You are an AI assistant that helps users of a travel agency to find Hotels in our internal company database for our customers."}
|
||||
],
|
||||
deployment: deployment,
|
||||
temperature: 0,
|
||||
top_p: 1,
|
||||
max_tokens: 800
|
||||
}
|
||||
}
|
||||
|
||||
public addUserMessage(content: string): void {
|
||||
this._completionsRequest.messages.push({
|
||||
role: "user",
|
||||
content: content
|
||||
});
|
||||
}
|
||||
|
||||
public addAssistantMessage(content: string): void {
|
||||
this._completionsRequest.messages.push({
|
||||
role: "assistant",
|
||||
content: content
|
||||
});
|
||||
}
|
||||
|
||||
public build(): ICompletionsRequest {
|
||||
return this._completionsRequest;
|
||||
}
|
||||
|
||||
public buildAsJson(): string {
|
||||
return JSON.stringify(this._completionsRequest);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
export interface ICompletionsRequest {
|
||||
dataSources: ICompletionsDataSource[];
|
||||
messages: ICompletionsMessage[];
|
||||
deployment: "gpt-35-turbo-model-deployment" | "gpt-4";
|
||||
temperature: number;
|
||||
top_p: number;
|
||||
max_tokens: number;
|
||||
}
|
||||
|
||||
export interface ICompletionsDataSource {
|
||||
type: "AzureCognitiveSearch";
|
||||
parameters: ICompletionsDataSourceParameters;
|
||||
}
|
||||
|
||||
export interface ICompletionsDataSourceParameters {
|
||||
endpoint: string;
|
||||
key: string;
|
||||
indexName: string;
|
||||
semanticConfiguration: string;
|
||||
queryType: string;
|
||||
fieldsMapping: ICompletionsDataSourceFieldsMapping;
|
||||
inScope: boolean;
|
||||
roleInformation: string;
|
||||
}
|
||||
|
||||
export interface ICompletionsDataSourceFieldsMapping {
|
||||
contentFieldsSeparator: string;
|
||||
contentFields: string[];
|
||||
filepathField: string;
|
||||
titleField: string;
|
||||
urlField: string;
|
||||
}
|
||||
|
||||
export interface ICompletionsMessage {
|
||||
role: "user" | "assistant" | "system";
|
||||
content: string;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
export interface ICompletionsResponse {
|
||||
id: string
|
||||
model: string
|
||||
created: number
|
||||
object: string
|
||||
choices: Choice[]
|
||||
}
|
||||
|
||||
export interface Choice {
|
||||
index: number
|
||||
messages: Message[]
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
index: number
|
||||
role: "assistant" | "tool"
|
||||
content: string
|
||||
end_turn: boolean
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import { HttpClient, IHttpClientOptions, HttpClientResponse } from '@microsoft/sp-http';
|
||||
import { ICompletionsResponse } from '../models/ICompletionsResponse';
|
||||
import { IChatMessage } from '../models/IChatMessage';
|
||||
import CompletionsRequestBuilder from '../models/CompletionsRequestBuilder';
|
||||
import Constants from '../Constants';
|
||||
|
||||
export default class CompletionsService {
|
||||
private readonly _httpClient: HttpClient;
|
||||
|
||||
constructor(httpClient: HttpClient) {
|
||||
this._httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async getCompletions(
|
||||
sessionMessageHistory: IChatMessage[],
|
||||
query: string,
|
||||
deployment: "gpt-35-turbo-model-deployment" | "gpt-4" = "gpt-4") : Promise<ICompletionsResponse> {
|
||||
const requestBuilder: CompletionsRequestBuilder = new CompletionsRequestBuilder(deployment);
|
||||
|
||||
sessionMessageHistory.map(m => {
|
||||
if (m.role === 'assistant') {
|
||||
requestBuilder.addAssistantMessage(m.text);
|
||||
} else {
|
||||
requestBuilder.addUserMessage(m.text);
|
||||
}
|
||||
});
|
||||
requestBuilder.addUserMessage(query);
|
||||
|
||||
const requestHeaders: Headers = new Headers();
|
||||
requestHeaders.append('Content-type', 'application/json');
|
||||
requestHeaders.append('Api-Key', `${Constants.AzureOpenAiApiKey}`);
|
||||
|
||||
const httpClientOptions: IHttpClientOptions = {
|
||||
body: requestBuilder.buildAsJson(),
|
||||
headers: requestHeaders
|
||||
};
|
||||
|
||||
const response: HttpClientResponse =
|
||||
await this._httpClient.post(
|
||||
this._compose_AzureOpenAiApiUrl(deployment),
|
||||
HttpClient.configurations.v1,
|
||||
httpClientOptions);
|
||||
|
||||
const completionsResponse: ICompletionsResponse = await response.json();
|
||||
|
||||
return completionsResponse;
|
||||
}
|
||||
|
||||
private _compose_AzureOpenAiApiUrl(deployment: "gpt-35-turbo-model-deployment" | "gpt-4"): string {
|
||||
return `https://oai-atlas-dev-eus.openai.azure.com/openai/deployments/${deployment}/extensions/chat/completions?api-version=2023-06-01-preview`;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue