Skip to content

Commit

Permalink
move config variables to AIKeyConfigDialog.tsx
Browse files Browse the repository at this point in the history
optimize error handling
change SelectOption to Welcome
make the setting icon larger
make Dall-E account configurable
  • Loading branch information
xuruiyao-msft committed Dec 6, 2023
1 parent dbc08b5 commit 9384c1b
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 82 deletions.
2 changes: 1 addition & 1 deletion Word-Add-in-AI-Assistant/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This add-in demonstrates Word add-in capabilities to insert content generated by
- Generate picture. Select the 'Generate Picture' option and select the source words from document, submit, insert the picture into the cusor pisiton inside document.
- Switch to chat mode. You can send whatever text words to the chat box, insert the generated text into Document/Comment/Footnote/Header.
- You can configure the drop down list to whatever prompt prefix you like. Clone the project and open config.tsx. Change the AssistanceOption and GenerateOption following the format of the existing GenerateText and GeneratePicture.
- You need to input the Azure OpenAI account once you relaunch/reload the add-in. You can persist the account details: apiKey in config.tsx or just refill in it whenever the add in ask.
- You need to input the Azure OpenAI account once you relaunch/reload the add-in. You can persist the account details: apiKey endpoint deployment in AIKeyConfigDialog.tsx or just refill in it whenever the add in ask.

## Applies to
- Word on Windows, Mac, and in a browser.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from "react";
import TextArea from "antd/es/input/TextArea";
import { Page } from "./Home";
import { generate, GenerateOption, GenerateType } from "./utility/config";
import { _apiKey, _deployment, _endPoint } from "./AIKeyConfigDialog";

export interface AIGenerateState {
sourceWords: string;
Expand All @@ -12,6 +13,7 @@ export interface AIGenerateState {

export interface AIGenerateProps {
setCurrentPage: (page: Page, generatedContent: string) => void;
openConfigDialog: (isOpen: boolean) => void;
generateOption: GenerateOption;
}

Expand Down Expand Up @@ -51,6 +53,8 @@ export default class AIGenerateText extends React.Component<AIGenerateProps, AIG
generate = async () => {
if (this.state.sourceWords.length == 0) {
message.warning("please select source content.");
} else if (_apiKey === "" || _endPoint === "" || _deployment === "") {
this.props.openConfigDialog(true);
} else {
this.setState({ loading: true });
await generate(this.state.sourceWords, this.props.generateOption)
Expand All @@ -61,6 +65,9 @@ export default class AIGenerateText extends React.Component<AIGenerateProps, AIG
}
this.setState({ loading: false });
this.props.setCurrentPage(Page.GeneratedPage, genContent);
}).catch((err) => {
message.error(err.message);
this.setState({ loading: false });
})
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,100 @@
import { Input, Modal } from "antd";
import React from "react";

// global variable to store the api key/endpoint/deployment, configrued by developer
export let _apiKey = "";
export let _endPoint = "";
export let _deployment = "";

export interface ApiKeyConfigProps {
isOpen: boolean;
apiKey: string;
setKey: (key: string) => void;
setOpen: (isOpen: boolean) => void;
}

export interface ApiKeyConfigState {
inputKey: string;
endPoint: string;
deployment: string;
}

export default class AIKeyConfigDialog extends React.Component<ApiKeyConfigProps, ApiKeyConfigState> {
constructor(props, context) {
super(props, context);
this.state = {
inputKey: _apiKey,
endPoint: _endPoint,
deployment: _deployment,
};
}

componentDidMount(): void {
this.setState({
inputKey: _apiKey,
endPoint: _endPoint,
deployment: _deployment,
});
}

setKey = (key: string) => {
_apiKey = key;
}

setEndpoint = (endpoint: string) => {
_endPoint = endpoint;
}

setDeployment = (deployment: string) => {
_deployment = deployment;
}

handleOk = () => {
if (this.state != null && this.state.inputKey != null && this.state.inputKey.length > 0) {
this.props.setKey(this.state.inputKey);
} else {
this.props.setOpen(false);
if (this.state != null && this.state.inputKey != null) {
this.setKey(this.state.inputKey);
}
if (this.state != null && this.state.endPoint != null) {
this.setEndpoint(this.state.endPoint);
}
if (this.state != null && this.state.deployment != null) {
this.setDeployment(this.state.deployment);
}
this.props.setOpen(false);
};

handleCancel = () => {
//back to the global variable
this.setState({
inputKey: _apiKey,
endPoint: _endPoint,
deployment: _deployment,
})
this.props.setOpen(false);
};

inputChange = (e) => {
keyChange = (e) => {
this.setState({ inputKey: e.target.value });
}

ednpointChange = (e) => {
this.setState({ endPoint: e.target.value });
}

deploymentChange = (e) => {
this.setState({ deployment: e.target.value });
}

render() {
return <>
<Modal
title="Input the api key!"
title="Connect to Azure OpenAI service."
open={this.props.isOpen}
onOk={this.handleOk}
onCancel={this.handleCancel}>
<Input placeholder="input here" onChange={this.inputChange} />
<label>EndPoint:</label>
<Input placeholder="input endpoint here" onChange={this.ednpointChange} value={this.state.endPoint}/>
<label>Deployment:</label>
<Input placeholder="input deployment here" onChange={this.deploymentChange} value={this.state.deployment}/>
<label>API key:</label>
<Input placeholder="input api key here" onChange={this.keyChange} value={this.state.inputKey}/>
</Modal>
</>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default class AIWelcome extends React.Component {
<div className="bottom_item">
<RightOutlined className="item_icon" />
<div className="bottom_item_info">
<a href="https://github.com/OfficeDev/Word-Scenario-based-Add-in-Samples/tree/main/Word-Add-in-AIGC" target="_blank">Start your project from this sample</a>
<a href="https://github.com/OfficeDev/Word-Scenario-based-Add-in-Samples/tree/main/Word-Add-in-AI-Assistant" target="_blank">Start your project from this sample</a>
</div>
</div>
<div className="bottom_item">
Expand Down
45 changes: 17 additions & 28 deletions Word-Add-in-AI-Assistant/src/taskpane/components/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { LeftOutlined, SendOutlined } from "@ant-design/icons";
import { Input, Segmented } from "antd";
import React from "react";
import { Page, _apiKey } from "./Home";
import { apiKey, generateText } from "./utility/config";
import AIKeyConfigDialog from "./AIKeyConfigDialog";

// global variable to store the api key, configrued by developer
export let chatKey = "";
import { Input, Segmented, message } from "antd";
import React, { Children } from "react";
import { Page } from "./Home";
import { generateText } from "./utility/config";
import { _apiKey, _deployment, _endPoint } from "./AIKeyConfigDialog";

export interface IChatProps {
children?: React.ReactNode;
back: (page: Page, generatedContent: string) => void;
setOpen: (isOpen: boolean) => void;
}

export default class Chat extends React.Component<IChatProps> {
Expand All @@ -21,7 +20,6 @@ export default class Chat extends React.Component<IChatProps> {
greeting: "please input whatever you want to say to the AI.",
content: <></>,
input: "",
openKeyConfigDialog: false,
selectedMessage: "",
};

Expand Down Expand Up @@ -63,15 +61,6 @@ export default class Chat extends React.Component<IChatProps> {
}
};

setOpen = (open: boolean) => {
this.setState({ openKeyConfigDialog: open });
};

setKey = (key: string) => {
chatKey = key;
this.setState({ openKeyConfigDialog: false });
};

onChange = async (option) => {
await Word.run(async (context) => {
const selRange = context.document.getSelection();
Expand Down Expand Up @@ -122,15 +111,15 @@ export default class Chat extends React.Component<IChatProps> {
</div>
);

if (apiKey === "" && chatKey === "" && _apiKey === "") {
if (_apiKey === "" || _endPoint === "" || _deployment === "") {
const alertMessage = (
<>
<div className="message clear">
<div className="clear">
<span className="left">
Please config the{" "}
<span onClick={() => this.setOpen(true)} className="configKey">
Azure Open AI Key.{" "}
<span onClick={() => this.props.setOpen(true)} className="configKey">
Azure OpenAI Account.{" "}
</span>
</span>
</div>
Expand Down Expand Up @@ -161,6 +150,9 @@ export default class Chat extends React.Component<IChatProps> {

const ret = await generateText(input, 50).then((res) => {
return res.replace("\n\r\n", "").replace("\n", "").replace("\n", "");
}).catch((err) => {
message.error(err.message);
throw Error(err);
});
const responseMessage = (
<div className="message clear" onClick={this.getSelectedContent}>
Expand Down Expand Up @@ -199,7 +191,7 @@ export default class Chat extends React.Component<IChatProps> {

inputChange = (e) => {
this.setState({ input: e.target.value });
};
}

render() {
return (
Expand All @@ -213,12 +205,6 @@ export default class Chat extends React.Component<IChatProps> {
</div>
</div>
</div>
<AIKeyConfigDialog
isOpen={this.state.openKeyConfigDialog}
apiKey={chatKey}
setOpen={this.setOpen.bind(this)}
setKey={this.setKey.bind(this)}
/>
<div className="content">
<div id="chats">{this.state.content}</div>
<div id="bottom"></div>
Expand All @@ -233,6 +219,9 @@ export default class Chat extends React.Component<IChatProps> {
/>
<SendOutlined className="sendIcon" onClick={this.addChat}></SendOutlined>
</div>
<div>
{Children.only(this.props.children)}
</div>
</div>
</>
);
Expand Down
40 changes: 19 additions & 21 deletions Word-Add-in-AI-Assistant/src/taskpane/components/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@ import { DownOutlined, InteractionOutlined, RightOutlined, SettingOutlined } fro
import type { MenuProps } from 'antd';
import { Button, Dropdown, message, Space, Tooltip } from 'antd';
import AIWelcome from "./AIWelcome";
import { apiKey, AssistanceOption, GenerateOptionList } from "./utility/config";
import { AssistanceOption, GenerateOptionList } from "./utility/config";
import AIGenerate from "./AIGenerate";
import AIKeyConfigDialog from "./AIKeyConfigDialog";
import AITextDisplay from "./AITextDisplay";
import AIPictureDisplay from "./AIPictureDisplay";
import Chat, { chatKey } from "./Chat";

// global variable to store the api key, configrued by developer
export let _apiKey = "";
import Chat from "./Chat";

export enum Page {
Home = "Home",
Expand All @@ -25,7 +22,7 @@ export default class Home extends React.Component {
}

state = {
selectedOption: AssistanceOption.SelectAnOption,
selectedOption: AssistanceOption.Welcome,
content: <AIWelcome />,
curPage: Page.Home,
generatedContent: "",
Expand All @@ -44,20 +41,11 @@ export default class Home extends React.Component {
this.setState({ openKeyConfigDialog: isOpen });
};

setKey = (key: string) => {
_apiKey = key;
this.setState({ openKeyConfigDialog: false });
}

generateAssistanceContent: MenuProps['onClick'] = (e) => {
if (_apiKey === "" && apiKey === "" && chatKey === "") {
this.setState({ openKeyConfigDialog: true });
return;
}
if (e.key !== this.state.selectedOption) {
var content: ReactElement = <></>
switch (e.key) {
case AssistanceOption.SelectAnOption:
case AssistanceOption.Welcome:
content = <AIWelcome />
break;
default:
Expand All @@ -81,7 +69,7 @@ export default class Home extends React.Component {
var areaGenerated: ReactElement = <></>;
GenerateOptionList.forEach((item) => {
if (item.dropDownOption === key) {
areaGenerated = <AIGenerate setCurrentPage={this.setCurrentPage.bind(this)} generateOption={item} />
areaGenerated = <AIGenerate setCurrentPage={this.setCurrentPage.bind(this)} generateOption={item} openConfigDialog={this.open.bind(this)}/>
}
})
return areaGenerated;
Expand All @@ -101,15 +89,27 @@ export default class Home extends React.Component {
};

if(this.state.curPage === Page.Chat) {
return <Chat back= {this.setCurrentPage.bind(this)}/>
return (
<>
<Chat back={this.setCurrentPage.bind(this)} setOpen={this.open.bind(this)}>
<AIKeyConfigDialog
isOpen={this.state.openKeyConfigDialog}
setOpen={this.open.bind(this)}
/>
</Chat>
</>
);
}

return (
<>
<div className="wrapper">
<div className="survey">
<RightOutlined />
<a href="https://forms.office.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR8GFRbAYEV9Hmqgjcbr7lOdUNVAxQklNRkxCWEtMMFRFN0xXUFhYVlc5Ni4u" target="_blank">
<a
href="https://forms.office.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR8GFRbAYEV9Hmqgjcbr7lOdUNVAxQklNRkxCWEtMMFRFN0xXUFhYVlc5Ni4u"
target="_blank"
>
How do you like this sample? Tell us more!
</a>
</div>
Expand All @@ -122,9 +122,7 @@ export default class Home extends React.Component {
<span>Welcome:</span>
<AIKeyConfigDialog
isOpen={this.state.openKeyConfigDialog}
apiKey={_apiKey}
setOpen={this.open.bind(this)}
setKey={this.setKey.bind(this)}
/>
<div>
<Tooltip placement="topLeft" title="chat mode">
Expand Down
Loading

0 comments on commit 9384c1b

Please sign in to comment.