Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
slreznit authored Sep 12, 2024
2 parents 7d2c46c + d2ff7ad commit a7172bf
Show file tree
Hide file tree
Showing 17 changed files with 228 additions and 102 deletions.
1 change: 1 addition & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ async def assets(path):
"show_chat_history_button": app_settings.ui.show_chat_history_button,
},
"sanitize_answer": app_settings.base_settings.sanitize_answer,
"oyd_enabled": app_settings.base_settings.datasource_type,
}


Expand Down
4 changes: 3 additions & 1 deletion backend/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,12 @@ def format_pf_non_streaming_response(
"content": chatCompletion[response_field_name]
})
if citations_field_name in chatCompletion:
citation_content= {"citations": chatCompletion[citations_field_name]}
messages.append({
"role": "tool",
"content": chatCompletion[citations_field_name]
"content": json.dumps(citation_content)
})

response_obj = {
"id": chatCompletion["id"],
"model": "",
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/api/models.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export type AskResponse = {
answer: string
answer: string | []
citations: Citation[]
generated_chart: string | null
error?: string
Expand Down Expand Up @@ -40,7 +40,7 @@ export type AzureSqlServerExecResults = {
export type ChatMessage = {
id: string
role: string
content: string
content: string | [{ type: string; text: string }, { type: string; image_url: { url: string } }]
end_turn?: boolean
date: string
feedback?: Feedback
Expand Down Expand Up @@ -138,6 +138,7 @@ export type FrontendSettings = {
feedback_enabled?: string | null
ui?: UI
sanitize_answer?: boolean
oyd_enabled?: boolean
}

export enum Feedback {
Expand Down
16 changes: 8 additions & 8 deletions frontend/src/components/Answer/Answer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,17 +247,17 @@ export const Answer = ({ answer, onCitationClicked, onExectResultClicked }: Prop
<Stack.Item>
<Stack horizontal grow>
<Stack.Item grow>
<ReactMarkdown
{parsedAnswer && <ReactMarkdown
linkTarget="_blank"
remarkPlugins={[remarkGfm, supersub]}
children={
SANITIZE_ANSWER
? DOMPurify.sanitize(parsedAnswer.markdownFormatText, { ALLOWED_TAGS: XSSAllowTags, ALLOWED_ATTR: XSSAllowAttributes })
: parsedAnswer.markdownFormatText
? DOMPurify.sanitize(parsedAnswer?.markdownFormatText, { ALLOWED_TAGS: XSSAllowTags, ALLOWED_ATTR: XSSAllowAttributes })
: parsedAnswer?.markdownFormatText
}
className={styles.answerText}
components={components}
/>
/>}
</Stack.Item>
<Stack.Item className={styles.answerHeader}>
{FEEDBACK_ENABLED && answer.message_id !== undefined && (
Expand Down Expand Up @@ -290,15 +290,15 @@ export const Answer = ({ answer, onCitationClicked, onExectResultClicked }: Prop
</Stack.Item>
</Stack>
</Stack.Item>
{parsedAnswer.generated_chart !== null && (
{parsedAnswer?.generated_chart !== null && (
<Stack className={styles.answerContainer}>
<Stack.Item grow>
<img src={`data:image/png;base64, ${parsedAnswer.generated_chart}`} />
<img src={`data:image/png;base64, ${parsedAnswer?.generated_chart}`} />
</Stack.Item>
</Stack>
)}
<Stack horizontal className={styles.answerFooter}>
{!!parsedAnswer.citations.length && (
{!!parsedAnswer?.citations.length && (
<Stack.Item onKeyDown={e => (e.key === 'Enter' || e.key === ' ' ? toggleIsRefAccordionOpen() : null)}>
<Stack style={{ width: '100%' }}>
<Stack horizontal horizontalAlign="start" verticalAlign="center">
Expand Down Expand Up @@ -352,7 +352,7 @@ export const Answer = ({ answer, onCitationClicked, onExectResultClicked }: Prop
</Stack>
{chevronIsExpanded && (
<div className={styles.citationWrapper}>
{parsedAnswer.citations.map((citation, idx) => {
{parsedAnswer?.citations.map((citation, idx) => {
return (
<span
title={createCitationFilepath(citation, ++idx)}
Expand Down
14 changes: 0 additions & 14 deletions frontend/src/components/Answer/AnswerParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,3 @@ describe('enumerateCitations', () => {
expect(results[2].part_index).toEqual(1)
})
})

describe('parseAnswer', () => {
it('reformats the answer text and reindexes citations', () => {
const parsed: ParsedAnswer = parseAnswer(sampleAnswer)
expect(parsed.markdownFormatText).toBe('This is an example answer with citations ^1^ and ^2^ .')
expect(parsed.citations.length).toBe(2)
expect(parsed.citations[0].id).toBe('1')
expect(parsed.citations[0].reindex_id).toBe('1')
expect(parsed.citations[1].id).toBe('2')
expect(parsed.citations[1].reindex_id).toBe('2')
expect(parsed.citations[0].part_index).toBe(1)
expect(parsed.citations[1].part_index).toBe(2)
})
})
3 changes: 2 additions & 1 deletion frontend/src/components/Answer/AnswerParser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type ParsedAnswer = {
citations: Citation[]
markdownFormatText: string,
generated_chart: string | null
}
} | null

export const enumerateCitations = (citations: Citation[]) => {
const filepathMap = new Map()
Expand All @@ -23,6 +23,7 @@ export const enumerateCitations = (citations: Citation[]) => {
}

export function parseAnswer(answer: AskResponse): ParsedAnswer {
if (typeof answer.answer !== "string") return null
let answerText = answer.answer
const citationLinks = answerText.match(/\[(doc\d\d?\d?)]/g)

Expand Down
32 changes: 32 additions & 0 deletions frontend/src/components/QuestionInput/QuestionInput.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,35 @@
left: 16.5%;
}
}

.fileInputContainer {
position: absolute;
right: 24px;
top: 20px;
}

.fileInput {
width: 0;
height: 0;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}

.fileLabel {
display: inline-block;
border-radius: 5px;
cursor: pointer;
text-align: center;
font-size: 14px;
}

.fileIcon {
font-size: 20px;
color: #424242;
}

.uploadedImage {
margin-right: 70px;
}
62 changes: 56 additions & 6 deletions frontend/src/components/QuestionInput/QuestionInput.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { useState } from 'react'
import { Stack, TextField } from '@fluentui/react'
import { useContext, useState } from 'react'
import { FontIcon, Stack, TextField } from '@fluentui/react'
import { SendRegular } from '@fluentui/react-icons'

import Send from '../../assets/Send.svg'

import styles from './QuestionInput.module.css'
import { ChatMessage } from '../../api'
import { AppStateContext } from '../../state/AppProvider'

interface Props {
onSend: (question: string, id?: string) => void
onSend: (question: ChatMessage['content'], id?: string) => void
disabled: boolean
placeholder?: string
clearOnSend?: boolean
Expand All @@ -16,16 +18,46 @@ interface Props {

export const QuestionInput = ({ onSend, disabled, placeholder, clearOnSend, conversationId }: Props) => {
const [question, setQuestion] = useState<string>('')
const [base64Image, setBase64Image] = useState<string | null>(null);

const appStateContext = useContext(AppStateContext)
const OYD_ENABLED = appStateContext?.state.frontendSettings?.oyd_enabled || false;

const handleImageUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];

if (file) {
await convertToBase64(file);
}
};

const convertToBase64 = async (file: Blob) => {
const reader = new FileReader();

reader.readAsDataURL(file);

reader.onloadend = () => {
setBase64Image(reader.result as string);
};

reader.onerror = (error) => {
console.error('Error: ', error);
};
};

const sendQuestion = () => {
if (disabled || !question.trim()) {
return
}

if (conversationId) {
onSend(question, conversationId)
const questionTest: ChatMessage["content"] = base64Image ? [{ type: "text", text: question }, { type: "image_url", image_url: { url: base64Image } }] : question.toString();

if (conversationId && questionTest !== undefined) {
onSend(questionTest, conversationId)
setBase64Image(null)
} else {
onSend(question)
onSend(questionTest)
setBase64Image(null)
}

if (clearOnSend) {
Expand Down Expand Up @@ -58,6 +90,24 @@ export const QuestionInput = ({ onSend, disabled, placeholder, clearOnSend, conv
onChange={onQuestionChange}
onKeyDown={onEnterPress}
/>
{!OYD_ENABLED && (
<div className={styles.fileInputContainer}>
<input
type="file"
id="fileInput"
onChange={(event) => handleImageUpload(event)}
accept="image/*"
className={styles.fileInput}
/>
<label htmlFor="fileInput" className={styles.fileLabel} aria-label='Upload Image'>
<FontIcon
className={styles.fileIcon}
iconName={'PhotoCollection'}
aria-label='Upload Image'
/>
</label>
</div>)}
{base64Image && <img className={styles.uploadedImage} src={base64Image} alt="Uploaded Preview" />}
<div
className={styles.questionInputSendButtonContainer}
role="button"
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/pages/chat/Chat.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
}

.chatMessageUserMessage {
position: relative;
display: flex;
padding: 20px;
background: #edf5fd;
Expand Down Expand Up @@ -360,6 +361,15 @@ a {
cursor: pointer;
}

.uploadedImageChat {
position: absolute;
right: -23px;
bottom: -35px;
max-width: 70%;
max-height: 70%;
border-radius: 4px;
}

@media (max-width: 480px) {
.chatInput {
width: 90%;
Expand Down
Loading

0 comments on commit a7172bf

Please sign in to comment.