Skip to content

Commit f6e28bf

Browse files
committed
Latest bug fixes.
1 parent d6760c7 commit f6e28bf

File tree

8 files changed

+111
-86
lines changed

8 files changed

+111
-86
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ It leverages Azure Open AI Service and Azure AI Search, to identify relevant doc
2121

2222
This example focuses on a generic use case - chat with your own data, generate a document template using your own data, and exporting the document in a docx format.
2323

24-
The sample data is sourced from 1) generic AI-generated promissory notes and 2) a select set of research and grants published on PubMed and NIH.
24+
The sample data is sourced from generic AI-generated promissory notes.
2525
The documents are intended for use as sample data only.
2626

2727
<br/>

docs/TRANSPARENCY_FAQ.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This solution accelerator is an open-source GitHub Repository to help create AI assistants using Azure Open AI Service and Azure AI Search. This can be used by anyone looking for reusable architecture and code snippets to build AI assistants with their own enterprise data. The repository showcases a generic scenario of a user who wants to generate a document template based on a sample set of data.
44

55
- ### What can Build your own copilot - Generic Solution Accelerator do?
6-
The sample solution included focuses on a generic use case - chat with your own data, generate a document template using your own data, and exporting the document in a docx format. The sample data is sourced from 1) generic AI-generated promissory notes and 2) a select set of research and grants published on PubMed and NIH. The documents are intended for use as sample data only. The sample solution takes user input in text format and returns LLM responses in text format up to 800 tokens. It uses prompt flow to search data from AI search vector store, summarize the retrieved documents with Azure Open AI.
6+
The sample solution included focuses on a generic use case - chat with your own data, generate a document template using your own data, and exporting the document in a docx format. The sample data is sourced from generic AI-generated promissory notes. The documents are intended for use as sample data only. The sample solution takes user input in text format and returns LLM responses in text format up to 800 tokens. It uses prompt flow to search data from AI search vector store, summarize the retrieved documents with Azure Open AI.
77

88
- ### What is/are Build your own copilot - Generic Solution Accelerator’s intended use(s)?
99
This repository is to be used only as a solution accelerator following the open-source license terms listed in the GitHub repository. The example scenario’s intended purpose is to help users generate a document template to perform their work more efficiently.

frontend/src/components/DraftCards/SectionCard.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useContext, useState } from 'react'
1+
import React, { useContext, useEffect, useState } from 'react'
22
import { Stack } from '@fluentui/react'
33
import { AppStateContext } from '../../state/AppProvider'
44
import { sectionGenerate, SectionGenerateRequest } from '../../api'
@@ -8,6 +8,7 @@ import GenerateIcon from '../../assets/Generate.svg'
88
import type { PopoverProps } from '@fluentui/react-components'
99
import { Dismiss16Regular } from '@fluentui/react-icons'
1010
import { Textarea, makeStyles, Text, Popover, PopoverSurface, PopoverTrigger, Button } from '@fluentui/react-components'
11+
import { useLocation } from 'react-router-dom'
1112

1213
interface SectionCardProps {
1314
sectionIdx: number
@@ -56,7 +57,7 @@ const useStyles = makeStyles({
5657
margin: '0.5rem 0',
5758
padding: '0.5rem',
5859
'&::selection': {
59-
backgroundColor: '#FFD6A5',
60+
backgroundColor: '#FFD6A5'
6061
}
6162
},
6263

@@ -111,12 +112,13 @@ const useStyles = makeStyles({
111112
})
112113

113114
const SectionCard = ({ sectionIdx }: SectionCardProps) => {
115+
const location = useLocation()
114116
const classes = useStyles()
115117
const [isLoading, setIsLoading] = useState(false)
116118
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
117119
const appStateContext = useContext(AppStateContext)
118120
const [charCount, setCharCount] = useState(0)
119-
const [wasInitialized, setWasInitialized] = useState(false)
121+
const [isManuallyCleared, setIsManuallyCleared] = useState(false)
120122

121123
if (!appStateContext) {
122124
throw new Error('useAppState must be used within a AppStateProvider')
@@ -132,6 +134,10 @@ const SectionCard = ({ sectionIdx }: SectionCardProps) => {
132134
const sectionContent = section.content
133135
const sectionCharacterLimit = 2000
134136

137+
useEffect(() => {
138+
setCharCount(sectionContent.length)
139+
}, [location])
140+
135141
const handleOpenChange: PopoverProps['onOpenChange'] = (e, data) => setIsPopoverOpen(data.open || false)
136142

137143
async function fetchSectionContent(sectionTitle: string, sectionDescription: string) {
@@ -148,7 +154,7 @@ const SectionCard = ({ sectionIdx }: SectionCardProps) => {
148154
}
149155
appStateContext?.dispatch({ type: 'UPDATE_SECTION', payload: { sectionIdx: sectionIdx, section: updatedSection } })
150156
let content = updatedSection.content || ''
151-
157+
152158
// limit the character count to 2000
153159
if (content.length > sectionCharacterLimit) {
154160
content = content.slice(0, sectionCharacterLimit)
@@ -158,10 +164,11 @@ const SectionCard = ({ sectionIdx }: SectionCardProps) => {
158164
setIsLoading(false)
159165
}
160166

161-
if (sectionContent === '' && !isLoading && !wasInitialized) {
162-
fetchSectionContent(sectionTitle, sectionDescription)
163-
setWasInitialized(true)
164-
}
167+
useEffect(() => {
168+
if (sectionContent === '' && !isLoading && !isManuallyCleared) {
169+
fetchSectionContent(sectionTitle, sectionDescription)
170+
}
171+
}, [sectionContent, isLoading, isManuallyCleared])
165172

166173
return (
167174
<Stack className={classes.sectionCard}>
@@ -191,7 +198,7 @@ const SectionCard = ({ sectionIdx }: SectionCardProps) => {
191198
appearance="outline"
192199
size="large"
193200
defaultValue={sectionDescription}
194-
className={ classes.popoverTextarea }
201+
className={classes.popoverTextarea}
195202
textarea={{ className: classes.popoverTextarea }}
196203
/>
197204

@@ -229,6 +236,11 @@ const SectionCard = ({ sectionIdx }: SectionCardProps) => {
229236
maxLength={sectionCharacterLimit}
230237
onChange={(e, data) => {
231238
const content = data.value || ''
239+
if (content === '') {
240+
setIsManuallyCleared(true)
241+
} else {
242+
setIsManuallyCleared(false)
243+
}
232244
setCharCount(content.length)
233245
const updatedSection: Section = {
234246
title: sectionTitle,

frontend/src/components/DraftCards/TitleCard.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { Stack, TextField } from '@fluentui/react'
22
import { makeStyles, Text } from '@fluentui/react-components'
3-
import React from 'react'
3+
import React, { useContext } from 'react'
4+
import { AppStateContext } from '../../state/AppProvider'
45

5-
interface TitleCardProps {
6-
onTitleChange: (value: string) => void
7-
}
6+
interface TitleCardProps {}
87

98
const useStyles = makeStyles({
109
sectionTitle: {
@@ -15,12 +14,19 @@ const useStyles = makeStyles({
1514
}
1615
})
1716

18-
const TitleCard: React.FC<TitleCardProps> = ({ onTitleChange }) => {
17+
const TitleCard: React.FC<TitleCardProps> = () => {
18+
const appStateContext = useContext(AppStateContext)
1919
const classes = useStyles()
2020
const handleChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>) => {
21-
onTitleChange(event.currentTarget.value)
21+
appStateContext?.dispatch({ type: 'UPDATE_DRAFTED_DOCUMENT_TITLE', payload: event.currentTarget.value })
22+
}
23+
24+
if (!appStateContext) {
25+
throw new Error('useAppState must be used within a AppStateProvider')
2226
}
2327

28+
const title = appStateContext.state.draftedDocumentTitle === null ? '' : appStateContext.state.draftedDocumentTitle
29+
2430
return (
2531
<Stack style={{ marginBottom: '1rem' }}>
2632
<Text className={classes.sectionTitle}>Draft Document</Text>
@@ -29,6 +35,7 @@ const TitleCard: React.FC<TitleCardProps> = ({ onTitleChange }) => {
2935
onChange={handleChange}
3036
placeholder="Enter title here"
3137
styles={{ root: { width: '100%' } }} // Adjust styles as needed
38+
value={title}
3239
/>
3340
</Stack>
3441
)

frontend/src/pages/chat/Chat.tsx

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const enum contentTemplateSections {
6363
NewLine = '\n\n',
6464
Intro = 'The proposal will include the following sections:',
6565
Closing = 'Does this look good? If so, you can **generate the document** now. You can also ask me to **add an item** or **change the order of the sections**.',
66-
JSONParseError = 'Could not create a template, please try again and ask for a document type.',
66+
JSONParseError = 'I was unable to find content related to your query and could not generate a template. Please try again.',
6767
JSONStructureError = 'Unable to render the sections within the template. Please try again.'
6868
}
6969

@@ -80,8 +80,7 @@ const modalStyles: IModalStyles = {
8080
borderRadius: '8px'
8181
},
8282
root: undefined,
83-
scrollableContent:
84-
{
83+
scrollableContent: {
8584
minWidth: '800px'
8685
},
8786
layer: undefined,
@@ -931,7 +930,7 @@ const Chat = ({ type = ChatType.Browse }: Props) => {
931930
<Answer
932931
answer={{
933932
answer:
934-
type === ChatType.Browse ? answer.content : generateTemplateSections(jsonDraftDocument),
933+
type === ChatType.Browse ? answer.content : generateTemplateSections(answer.content),
935934
citations: parseCitationFromMessage(messages[index - 1]),
936935
message_id: answer.id,
937936
feedback: answer.feedback
@@ -984,31 +983,32 @@ const Chat = ({ type = ChatType.Browse }: Props) => {
984983
</Stack>
985984
)}
986985
<Stack>
987-
{appStateContext?.state.isCosmosDBAvailable?.status !== CosmosDBStatus.NotConfigured && type === ChatType.Template && (
988-
<CommandBarButton
989-
role="button"
990-
styles={{
991-
icon: {
992-
color: '#FFFFFF'
993-
},
994-
iconDisabled: {
995-
color: '#BDBDBD !important'
996-
},
997-
root: {
998-
color: '#FFFFFF',
999-
background: '#0F6CBD'
1000-
},
1001-
rootDisabled: {
1002-
background: '#F0F0F0'
1003-
}
1004-
}}
1005-
className={styles.newChatIcon}
1006-
iconProps={{ iconName: 'Add' }}
1007-
onClick={newChat}
1008-
disabled={disabledButton()}
1009-
aria-label="start a new chat button"
1010-
/>
1011-
)}
986+
{appStateContext?.state.isCosmosDBAvailable?.status !== CosmosDBStatus.NotConfigured &&
987+
type === ChatType.Template && (
988+
<CommandBarButton
989+
role="button"
990+
styles={{
991+
icon: {
992+
color: '#FFFFFF'
993+
},
994+
iconDisabled: {
995+
color: '#BDBDBD !important'
996+
},
997+
root: {
998+
color: '#FFFFFF',
999+
background: '#0F6CBD'
1000+
},
1001+
rootDisabled: {
1002+
background: '#F0F0F0'
1003+
}
1004+
}}
1005+
className={styles.newChatIcon}
1006+
iconProps={{ iconName: 'Add' }}
1007+
onClick={newChat}
1008+
disabled={disabledButton()}
1009+
aria-label="start a new chat button"
1010+
/>
1011+
)}
10121012
<CommandBarButton
10131013
role="button"
10141014
styles={{
@@ -1026,7 +1026,7 @@ const Chat = ({ type = ChatType.Browse }: Props) => {
10261026
background: '#F0F0F0'
10271027
}
10281028
}}
1029-
className={ styles.clearChatBroom }
1029+
className={styles.clearChatBroom}
10301030
iconProps={{ iconName: 'Broom' }}
10311031
onClick={
10321032
appStateContext?.state.isCosmosDBAvailable?.status !== CosmosDBStatus.NotConfigured &&
@@ -1079,7 +1079,7 @@ const Chat = ({ type = ChatType.Browse }: Props) => {
10791079
onClick={generateDocument} //Update for Document Generation
10801080
disabled={draftDocument === undefined || disabledButton()}
10811081
aria-label="generate draft"
1082-
title='Generate Draft'
1082+
title="Generate Draft"
10831083
/>
10841084
)}
10851085
</Stack>

frontend/src/pages/draft/Draft.tsx

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useContext } from 'react'
1+
import { useContext } from 'react'
22
import styles from './Draft.module.css'
33
import { useLocation, useNavigate } from 'react-router-dom'
44
import TitleCard from '../../components/DraftCards/TitleCard'
@@ -11,7 +11,6 @@ import { CommandBarButton, Stack } from '@fluentui/react'
1111
const Draft = (): JSX.Element => {
1212
const appStateContext = useContext(AppStateContext)
1313
const location = useLocation()
14-
const [title, setTitle] = useState('')
1514
const navigate = useNavigate()
1615

1716
// get draftedDocument from context
@@ -33,7 +32,7 @@ const Draft = (): JSX.Element => {
3332
new Paragraph({
3433
children: [
3534
new TextRun({
36-
text: title,
35+
text: getTitle(),
3736
bold: true,
3837
size: 24
3938
}),
@@ -64,16 +63,19 @@ const Draft = (): JSX.Element => {
6463
text: '',
6564
break: 1 // Add a new line after the section title
6665
}),
67-
...section.content.split('\n').map((line, lineIndex) => [
68-
new TextRun({
69-
text: line,
70-
size: 16
71-
}),
72-
new TextRun({
73-
text: '',
74-
break: 1 // Add a new line after each line of content
75-
})
76-
]).flat()
66+
...section.content
67+
.split('\n')
68+
.map((line, lineIndex) => [
69+
new TextRun({
70+
text: line,
71+
size: 16
72+
}),
73+
new TextRun({
74+
text: '',
75+
break: 1 // Add a new line after each line of content
76+
})
77+
])
78+
.flat()
7779
]
7880
})
7981
)
@@ -83,21 +85,21 @@ const Draft = (): JSX.Element => {
8385
})
8486

8587
Packer.toBlob(doc).then(blob => {
86-
saveAs(blob, `DraftTemplate-${sanitizeTitle(title)}.docx`)
88+
saveAs(blob, `DraftTemplate-${sanitizeTitle(getTitle())}.docx`)
8789
})
8890
}
8991

90-
const handleTitleChange = (newTitle: string) => {
91-
setTitle(newTitle)
92+
function getTitle() {
93+
if (appStateContext === undefined) return ''
94+
return appStateContext.state.draftedDocumentTitle === null ? '' : appStateContext.state.draftedDocumentTitle
9295
}
93-
9496
function sanitizeTitle(title: string): string {
9597
return title.replace(/[^a-zA-Z0-9]/g, '')
9698
}
9799

98100
return (
99101
<Stack className={styles.container}>
100-
<TitleCard onTitleChange={handleTitleChange} />
102+
<TitleCard />
101103
{(sections ?? []).map((_, index) => (
102104
<SectionCard key={index} sectionIdx={index} />
103105
))}

frontend/src/state/AppProvider.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import React, { createContext, ReactNode, useEffect,
2-
useReducer } from 'react'
1+
import React, { createContext, ReactNode, useEffect, useReducer } from 'react'
32

43
import {
54
ChatHistoryLoadingState,
@@ -29,6 +28,7 @@ export interface AppState {
2928
frontendSettings: FrontendSettings | null
3029
feedbackState: { [answerId: string]: Feedback.Neutral | Feedback.Positive | Feedback.Negative }
3130
draftedDocument: DraftedDocument | null
31+
draftedDocumentTitle: string
3232
}
3333

3434
export type Action =
@@ -53,6 +53,7 @@ export type Action =
5353
| { type: 'UPDATE_DRAFTED_DOCUMENT'; payload: DraftedDocument }
5454
| { type: 'UPDATE_BROWSE_CHAT'; payload: Conversation | null }
5555
| { type: 'UPDATE_GENERATE_CHAT'; payload: Conversation | null }
56+
| { type: 'UPDATE_DRAFTED_DOCUMENT_TITLE'; payload: string }
5657

5758
const initialState: AppState = {
5859
isChatHistoryOpen: false,
@@ -68,7 +69,8 @@ const initialState: AppState = {
6869
},
6970
frontendSettings: null,
7071
feedbackState: {},
71-
draftedDocument: null
72+
draftedDocument: null,
73+
draftedDocumentTitle: ''
7274
}
7375

7476
export const AppStateContext = createContext<

0 commit comments

Comments
 (0)