diff --git a/app/backend/app.py b/app/backend/app.py index 0cef5b013..b987478a1 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -10,9 +10,10 @@ from fastapi import FastAPI, HTTPException, Request from fastapi.responses import RedirectResponse import openai -from approaches.chatrrrbingcompare import ChatReadRetrieveReadBingCompare -from approaches.chatbingsearchcompare import ChatBingSearchCompare +from approaches.comparewebwithwork import CompareWebWithWork +from approaches.compareworkwithweb import CompareWorkWithWeb from approaches.chatreadretrieveread import ChatReadRetrieveReadApproach +from approaches.chatwebretrieveread import ChatWebRetrieveRead from approaches.gpt_direct_approach import GPTDirectApproach from approaches.approach import Approaches from azure.core.credentials import AzureKeyCredential @@ -26,7 +27,6 @@ generate_account_sas, ) from shared_code.status_log import State, StatusClassification, StatusLog, StatusQueryLevel -from approaches.chatbingsearch import ChatBingSearch from azure.cosmos import CosmosClient @@ -192,7 +192,7 @@ ENV["AZURE_AI_TRANSLATION_DOMAIN"], str_to_bool.get(ENV["USE_SEMANTIC_RERANKER"]) ), - Approaches.ChatBingSearch: ChatBingSearch( + Approaches.ChatWebRetrieveRead: ChatWebRetrieveRead( model_name, ENV["AZURE_OPENAI_CHATGPT_DEPLOYMENT"], ENV["TARGET_TRANSLATION_LANGUAGE"], @@ -200,7 +200,7 @@ ENV["BING_SEARCH_KEY"], str_to_bool.get(ENV["ENABLE_BING_SAFE_SEARCH"]) ), - Approaches.ChatBingSearchCompare: ChatBingSearchCompare( + Approaches.CompareWorkWithWeb: CompareWorkWithWeb( model_name, ENV["AZURE_OPENAI_CHATGPT_DEPLOYMENT"], ENV["TARGET_TRANSLATION_LANGUAGE"], @@ -208,9 +208,9 @@ ENV["BING_SEARCH_KEY"], str_to_bool.get(ENV["ENABLE_BING_SAFE_SEARCH"]) ), - Approaches.BingRRRCompare: ChatReadRetrieveReadBingCompare( + Approaches.CompareWebWithWork: CompareWebWithWork( search_client, - ENV["AZURE_OPENAI_SERVICE"], + ENV["AZURE_OPENAI_ENDPOINT"], ENV["AZURE_OPENAI_SERVICE_KEY"], ENV["AZURE_OPENAI_CHATGPT_DEPLOYMENT"], ENV["KB_FIELDS_SOURCEFILE"], diff --git a/app/backend/approaches/approach.py b/app/backend/approaches/approach.py index c49ca1c87..6cc8741f9 100644 --- a/app/backend/approaches/approach.py +++ b/app/backend/approaches/approach.py @@ -11,9 +11,9 @@ class Approaches(Enum): ReadRetrieveRead = 1 ReadDecomposeAsk = 2 GPTDirect = 3 - ChatBingSearch = 4 - ChatBingSearchCompare = 5 - BingRRRCompare = 6 + ChatWebRetrieveRead = 4 + CompareWorkWithWeb = 5 + CompareWebWithWork = 6 class Approach: """ diff --git a/app/backend/approaches/chatbingsearch.py b/app/backend/approaches/chatwebretrieveread.py similarity index 99% rename from app/backend/approaches/chatbingsearch.py rename to app/backend/approaches/chatwebretrieveread.py index 540ebf765..13da7f1e9 100644 --- a/app/backend/approaches/chatbingsearch.py +++ b/app/backend/approaches/chatwebretrieveread.py @@ -13,7 +13,7 @@ from core.messagebuilder import MessageBuilder from core.modelhelper import get_token_limit -class ChatBingSearch(Approach): +class ChatWebRetrieveRead(Approach): """Class to help perform RAG based on Bing Search and ChatGPT.""" SYSTEM_MESSAGE_CHAT_CONVERSATION = """You are an Azure OpenAI Completion system. Your persona is {systemPersona} who helps answer questions. {response_length_prompt} diff --git a/app/backend/approaches/chatrrrbingcompare.py b/app/backend/approaches/comparewebwithwork.py similarity index 90% rename from app/backend/approaches/chatrrrbingcompare.py rename to app/backend/approaches/comparewebwithwork.py index 7aa4c1543..e045cae5e 100644 --- a/app/backend/approaches/chatrrrbingcompare.py +++ b/app/backend/approaches/comparewebwithwork.py @@ -14,13 +14,13 @@ ) from core.modelhelper import get_token_limit -class ChatReadRetrieveReadBingCompare(Approach): +class CompareWebWithWork(Approach): """ - Approach for comparing and contrasting answers from internal data and Bing Chat. + Approach for comparing and contrasting generative response answers based on web search results vs. based on work search results. """ COMPARATIVE_SYSTEM_MESSAGE_CHAT_CONVERSATION = """You are an Azure OpenAI Completion system. Your persona is {systemPersona}. User persona is {userPersona}. - Compare and contrast the answers provided below from two sources of data. The first source is internal data indexed using a RAG pattern while the second source is from Bing Chat. + Compare and contrast the answers provided below from two sources of data. The first source is Web where data is retrieved from an internet search while the second source is Work where internal data indexed using a RAG pattern. Only explain the differences between the two sources and nothing else. Do not provide personal opinions or assumptions. Only answer in the language {query_term_language}. If you cannot find answer in below sources, respond with I am not sure. Do not provide personal opinions or assumptions. @@ -29,8 +29,8 @@ class ChatReadRetrieveReadBingCompare(Approach): """ COMPARATIVE_RESPONSE_PROMPT_FEW_SHOTS = [ - {"role": Approach.USER ,'content': 'I am looking for comparative information in the Bing Search Response and want to compare against the Internal Documents'}, - {'role': Approach.ASSISTANT, 'content': 'user is looking to compare information in Bing Search Response against Internal Documents.'} + {"role": Approach.USER ,'content': 'I am looking for comparative information on an answer based on Web search results and want to compare against an answer based on Work internal documents'}, + {'role': Approach.ASSISTANT, 'content': 'User is looking to compare an answer based on Web search results against an answer based on Work internal documents.'} ] citations = {} @@ -82,7 +82,7 @@ def __init__( async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any]) -> Any: """ - Runs the approach to compare and contrast answers from internal data and Bing Chat. + Runs the approach to compare and contrast answers from internal data and Web Search results. Args: history (Sequence[dict[str, str]]): The conversation history. @@ -118,13 +118,13 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] self.citations = rrr_response.get("citation_lookup") user_query = history[-1].get("user") - bing_answer = history[0].get("bot") + web_answer = history[0].get("bot") user_persona = overrides.get("user_persona", "") system_persona = overrides.get("system_persona", "") response_length = int(overrides.get("response_length") or 1024) # Step 2: Contruct the comparative system message with passed Rag response and Bing Search Response from above approach - bing_compare_query = user_query + "Internal Documents:\n" + rrr_response.get("answer") + "\n\n" + " Bing Search Response:\n" + bing_answer + "\n\n" + bing_compare_query = user_query + " Web search resutls:\n" + web_answer + "\n\n" + "Work internal Documents:\n" + rrr_response.get("answer") + "\n\n" messages = self.get_messages_builder( self.COMPARATIVE_SYSTEM_MESSAGE_CHAT_CONVERSATION.format( diff --git a/app/backend/approaches/chatbingsearchcompare.py b/app/backend/approaches/compareworkwithweb.py similarity index 78% rename from app/backend/approaches/chatbingsearchcompare.py rename to app/backend/approaches/compareworkwithweb.py index c92d4cf74..4c0269727 100644 --- a/app/backend/approaches/chatbingsearchcompare.py +++ b/app/backend/approaches/compareworkwithweb.py @@ -5,19 +5,19 @@ from typing import Any, Sequence import urllib.parse import openai -from approaches.chatbingsearch import ChatBingSearch +from approaches.chatwebretrieveread import ChatWebRetrieveRead from approaches.approach import Approach from core.messagebuilder import MessageBuilder from core.modelhelper import get_token_limit -class ChatBingSearchCompare(Approach): +class CompareWorkWithWeb(Approach): """ - Approach class for performing comparative analysis between Bing Search Response and Internal Documents. + Approach class for performing comparative analysis between Generative answer responses based on Bing search results vs. work internal document search results. """ COMPARATIVE_SYSTEM_MESSAGE_CHAT_CONVERSATION = """You are an Azure OpenAI Completion system. Your persona is {systemPersona}. User persona is {userPersona}. - Compare and contrast the answers provided below from two sources of data. The first source is internal data indexed using a RAG pattern while the second source is from Bing Chat. + Compare and contrast the answers provided below from two sources of data. The first source is Work where internal data is indexed using a RAG pattern while the second source Web where results are from an internet search. Only explain the differences between the two sources and nothing else. Do not provide personal opinions or assumptions. Only answer in the language {query_term_language}. If you cannot find answer in below sources, respond with I am not sure. Do not provide personal opinions or assumptions. @@ -26,22 +26,25 @@ class ChatBingSearchCompare(Approach): """ COMPARATIVE_RESPONSE_PROMPT_FEW_SHOTS = [ - {"role": Approach.USER ,'content': 'I am looking for comparative information in the Bing Search Response and want to compare against the Internal Documents'}, - {'role': Approach.ASSISTANT, 'content': 'user is looking to compare information in Bing Search Response against Internal Documents.'} + {"role": Approach.USER ,'content': 'I am looking for comparative information on an answer based on Work internal documents and want to compare against an answer based on Web search results'}, + {'role': Approach.ASSISTANT, 'content': 'User is looking to compare an answer based on Work internal documents against an answer based on Web search results.'} ] citations = {} def __init__(self, model_name: str, chatgpt_deployment: str, query_term_language: str, bing_search_endpoint: str, bing_search_key: str, bing_safe_search: bool): """ - Initializes the ChatBingSearchCompare approach. + Initializes the CompareWorkWithWeb approach. Args: model_name (str): The name of the model to be used for chat-based language model. chatgpt_deployment (str): The deployment ID of the chat-based language model. query_term_language (str): The language to be used for querying the data. + bing_search_endpoint (str): The endpoint for the Bing Search API. + bing_search_key (str): The API key for the Bing Search API. + bing_safe_search (bool): The flag to enable or disable safe search for the Bing Search API. """ - self.name = "ChatBingSearchCompare" + self.name = "CompareWorkWithWeb" self.model_name = model_name self.chatgpt_deployment = chatgpt_deployment self.query_term_language = query_term_language @@ -62,7 +65,7 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] Any: The result of the comparative analysis. """ # Step 1: Call bing Search Approach for a Bing LLM Response and Citations - chat_bing_search = ChatBingSearch(self.model_name, self.chatgpt_deployment, self.query_term_language, self.bing_search_endpoint, self.bing_search_key, self.bing_safe_search) + chat_bing_search = ChatWebRetrieveRead(self.model_name, self.chatgpt_deployment, self.query_term_language, self.bing_search_endpoint, self.bing_search_key, self.bing_safe_search) bing_search_response = await chat_bing_search.run(history, overrides) self.citations = bing_search_response.get("citation_lookup") @@ -73,7 +76,7 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] response_length = int(overrides.get("response_length") or 1024) # Step 2: Contruct the comparative system message with passed Rag response and Bing Search Response from above approach - bing_compare_query = user_query + "Internal Documents:\n" + rag_answer + "\n\n" + " Bing Search Response:\n" + bing_search_response.get("answer") + "\n\n" + bing_compare_query = user_query + "Work internal documents:\n" + rag_answer + "\n\n" + " Web search results:\n" + bing_search_response.get("answer") + "\n\n" messages = self.get_messages_builder( self.COMPARATIVE_SYSTEM_MESSAGE_CHAT_CONVERSATION.format( @@ -93,9 +96,9 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] msg_to_display = '\n\n'.join([str(message) for message in messages]) # Step 3: Final comparative analysis using OpenAI Chat Completion - bing_compare_resp = await self.make_chat_completion(messages) + compare_resp = await self.make_chat_completion(messages) - final_response = f"{urllib.parse.unquote(bing_compare_resp)}" + final_response = f"{urllib.parse.unquote(compare_resp)}" # Step 4: Append web citations from the Bing Search approach for idx, url in enumerate(self.citations.keys(), start=1): diff --git a/app/backend/approaches/gpt_direct_approach.py b/app/backend/approaches/gpt_direct_approach.py index 9fe730363..efba6c35d 100644 --- a/app/backend/approaches/gpt_direct_approach.py +++ b/app/backend/approaches/gpt_direct_approach.py @@ -34,21 +34,15 @@ class GPTDirectApproach(Approach): USER = "user" ASSISTANT = "assistant" - system_message_chat_conversation = """You are an Azure OpenAI Completion system. Your persona is {systemPersona} who helps answer questions about an agency's data. {response_length_prompt} - User persona is {userPersona} - Your goal is to provide accurate and relevant answers based on the facts. Make sure to avoid making assumptions or adding personal opinions. - - Emphasize the use of facts. - - Here is how you should answer every question: - -Please respond with relevant information from the data in the response. + system_message_chat_conversation = """You are an Azure OpenAI Completion system. Your persona is {systemPersona} who helps users interact with a Large Language Model. {response_length_prompt} + User persona is {userPersona}. You are having a conversation with a user and you need to provide a response. {follow_up_questions_prompt} {injected_prompt} """ follow_up_questions_prompt_content = """ - Generate three very brief follow-up questions that the user would likely ask next about their agencies data. Use triple angle brackets to reference the questions, e.g. <<>>. Try not to repeat questions that have already been asked. + Generate three very brief follow-up questions that the user would likely ask next about their previous chat context. Use triple angle brackets to reference the questions, e.g. <<>>. Try not to repeat questions that have already been asked. Only generate questions and do not generate any text before or after the questions, such as 'Next Questions' """ @@ -105,33 +99,7 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] system_persona = overrides.get("system_persona", "") response_length = int(overrides.get("response_length") or 1024) - user_q = 'Generate search query for: ' + history[-1]["user"] - - query_prompt=self.query_prompt_template.format(query_term_language=self.query_term_language) - - # STEP 1: Generate an optimized keyword search query based on the chat history and the last question - messages = self.get_messages_from_history( - query_prompt, - self.model_name, - history, - user_q, - self.query_prompt_few_shots, - self.chatgpt_token_limit - len(user_q) - ) - - chat_completion = openai.ChatCompletion.create( - deployment_id=self.chatgpt_deployment, - model=self.model_name, - messages=messages, - temperature=0.0, - max_tokens=32, - n=1) - - generated_query = chat_completion.choices[0].message.content - #if we fail to generate a query, return the last user question - if generated_query.strip() == "0": - generated_query = history[-1]["user"] - + user_q = 'Generate response for: ' + history[-1]["user"] #Generate the follow up prompt to be sent to the GPT model follow_up_questions_prompt = ( @@ -140,39 +108,16 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] else "" ) - # Allow client to replace the entire prompt, or to inject into the existing prompt using >>> - prompt_override = overrides.get("prompt_template") - - if prompt_override is None: - system_message = self.system_message_chat_conversation.format( - injected_prompt="", - follow_up_questions_prompt=follow_up_questions_prompt, - response_length_prompt=self.get_response_length_prompt_text( - response_length - ), - userPersona=user_persona, - systemPersona=system_persona, - ) - elif prompt_override.startswith(">>>"): - system_message = self.system_message_chat_conversation.format( - injected_prompt=prompt_override[3:] + "\n ", - follow_up_questions_prompt=follow_up_questions_prompt, - response_length_prompt=self.get_response_length_prompt_text( - response_length - ), - userPersona=user_persona, - systemPersona=system_persona, - ) - else: - system_message = self.system_message_chat_conversation.format( - follow_up_questions_prompt=follow_up_questions_prompt, - response_length_prompt=self.get_response_length_prompt_text( - response_length - ), - userPersona=user_persona, - systemPersona=system_persona, - ) - + system_message = self.system_message_chat_conversation.format( + injected_prompt="", + follow_up_questions_prompt=follow_up_questions_prompt, + response_length_prompt=self.get_response_length_prompt_text( + response_length + ), + userPersona=user_persona, + systemPersona=system_persona, + ) + #Generate a contextual and content-specific answer using the search results and chat history. #Added conditional block to use different system messages for different models. messages = self.get_messages_from_history( @@ -198,7 +143,7 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] return { "data_points": [], "answer": f"{urllib.parse.unquote(chat_completion.choices[0].message.content)}", - "thoughts": f"Searched for:
{generated_query}

Conversations:
" + msg_to_display.replace('\n', '
'), + "thoughts": f"Searched for:
{user_q}

Conversations:
" + msg_to_display.replace('\n', '
'), "citation_lookup": {} } diff --git a/app/frontend/src/api/models.ts b/app/frontend/src/api/models.ts index 3f2299570..2e3b6850f 100644 --- a/app/frontend/src/api/models.ts +++ b/app/frontend/src/api/models.ts @@ -1,16 +1,20 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { string } from "prop-types"; +export const enum ChatMode { + WorkOnly = 0, + WorkPlusWeb = 1, + Ungrounded = 2 +} export const enum Approaches { RetrieveThenRead = 0, ReadRetrieveRead = 1, ReadDecomposeAsk = 2, GPTDirect = 3, - BingSearch = 4, - BingSearchCompare = 5, - BingRRRCompare = 6 + ChatWebRetrieveRead = 4, + CompareWorkWithWeb = 5, + CompareWebWithWork = 6 } export type AskRequestOverrides = { @@ -43,8 +47,7 @@ export type AskResponse = { answer: string; thoughts: string | null; data_points: string[]; - source: string; - comparative: boolean; + approach: Approaches; // citation_lookup: {} // added this for citation bug. aparmar. citation_lookup: { [key: string]: { citation: string; source_path: string; page_number: string } }; diff --git a/app/frontend/src/components/Answer/Answer.module.css b/app/frontend/src/components/Answer/Answer.module.css index 907f6a111..68ffbd14d 100644 --- a/app/frontend/src/components/Answer/Answer.module.css +++ b/app/frontend/src/components/Answer/Answer.module.css @@ -9,20 +9,34 @@ outline: transparent solid 1px; } -.bingAnswerContainer { +.answerContainerWork { padding: 20px; - background: rgb(180, 199, 221); + background: rgb(249, 249, 249); border-radius: 8px; box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.14), 0px 0px 2px rgba(0, 0, 0, 0.12); - outline: transparent solid 1px; + outline: #1B4AEF solid 1px; } -.comparativeAnswerContainer { +.answerContainerWeb { padding: 20px; - background: rgb(183, 199, 188); + background: rgb(249, 249, 249); border-radius: 8px; box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.14), 0px 0px 2px rgba(0, 0, 0, 0.12); - outline: transparent solid 1px; + outline: #188d45 solid 1px; +} + +.answerContainerCompare { + padding: 20px; + background: rgb(249, 249, 249); + border-radius: 8px; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.14), 0px 0px 2px rgba(0, 0, 0, 0.12); + outline: #ce7b2e solid 1px; +} + +.answerContainerUngrounded { + padding: 20px; + background: transparent; + outline: transparent; } .warningText { @@ -32,8 +46,48 @@ text-align: center; } -.answerLogo { - font-size: 28px; +.answerLogoWeb { + font-size: 22px; + color: #188d45; + white-space: break-spaces; + word-break: break-word; + display: flex; + align-items: center; +} + +.answerLogoWork { + font-size: 22px; + color: #1B4AEF; + white-space: break-spaces; + word-break: break-word; + display: flex; + align-items: center; +} + +.answerLogoCompare { + font-size: 22px; + color: #ce7b2e; + white-space: break-spaces; + word-break: break-word; + display: flex; + align-items: center; +} + +.answerLogoUngrounded { + font-size: 22px; + color: rgba(0, 0, 0, 1); + white-space: break-spaces; + word-break: break-word; + display: flex; + align-items: center; +} + +.answerLogoUngroundedText { + padding-left: 10px; + font-size: 16px; + line-height: 22px; + font-weight: 700; + font-variation-settings: unset; } .answerText { @@ -45,6 +99,16 @@ white-space: pre-line; } +.answerTextUngrounded { + font-size: 16px; + font-weight: 400; + line-height: 22px; + padding-top: 16px; + padding-bottom: 16px; + white-space: pre-line; + padding-left: 36px; +} + .selected { outline: 2px solid rgba(115, 118, 225, 1); } @@ -55,7 +119,7 @@ line-height: 24px; } -.citation { +.citationWork { font-weight: 500; line-height: 24px; text-align: center; @@ -67,7 +131,39 @@ cursor: pointer; } -.citation:hover { +.citationWork:hover { + text-decoration: underline; +} + +.citationWeb { + font-weight: 500; + line-height: 24px; + text-align: center; + border-radius: 4px; + padding: 0px 8px; + background: #d1fad8; + color: #123bb6; + text-decoration: none; + cursor: pointer; +} + +.citationWeb:hover { + text-decoration: underline; +} + +.citationCompare { + font-weight: 500; + line-height: 24px; + text-align: center; + border-radius: 4px; + padding: 0px 8px; + background: #fae3d1; + color: #123bb6; + text-decoration: none; + cursor: pointer; +} + +.citationCompare:hover { text-decoration: underline; } @@ -149,3 +245,16 @@ sup { content: ""; animation: loading 1s infinite; } + +.raiwarning { + font-size: 12px; + font-weight: 400; + color: rgb(133, 133, 133); + line-height: 22px; + padding-top: 6px; + padding-bottom: 6px; + white-space: pre-line; + display: flex; + align-items: center; + flex-direction: column; +} \ No newline at end of file diff --git a/app/frontend/src/components/Answer/Answer.tsx b/app/frontend/src/components/Answer/Answer.tsx index c93beef94..847af4d14 100644 --- a/app/frontend/src/components/Answer/Answer.tsx +++ b/app/frontend/src/components/Answer/Answer.tsx @@ -2,12 +2,13 @@ // Licensed under the MIT license. import { useMemo } from "react"; -import { Stack, IconButton, Icon } from "@fluentui/react"; +import { Stack, IconButton } from "@fluentui/react"; +import { ShieldCheckmark20Regular } from '@fluentui/react-icons'; import DOMPurify from "dompurify"; import styles from "./Answer.module.css"; -import { AskResponse, getCitationFilePath } from "../../api"; +import { Approaches, AskResponse, getCitationFilePath, ChatMode } from "../../api"; import { parseAnswerToHtml } from "./AnswerParser"; import { AnswerIcon } from "./AnswerIcon"; import { RAIPanel } from "../RAIPanel"; @@ -17,15 +18,16 @@ interface Props { isSelected?: boolean; onCitationClicked: (filePath: string, sourcePath: string, pageNumber: string) => void; onThoughtProcessClicked: () => void; - onBingSearchClicked: () => void; + onWebSearchClicked: () => void; onRagSearchClicked: () => void; - onBingCompareClicked: () => void; + onWebCompareClicked: () => void; onRagCompareClicked: () => void; onSupportingContentClicked: () => void; onFollowupQuestionClicked?: (question: string) => void; showFollowupQuestions?: boolean; onAdjustClick?: () => void; onRegenerateClick?: () => void; + chatMode: ChatMode; } export const Answer = ({ @@ -33,35 +35,42 @@ export const Answer = ({ isSelected, onCitationClicked, onThoughtProcessClicked, - onBingSearchClicked, + onWebSearchClicked, onRagSearchClicked, - onBingCompareClicked, + onWebCompareClicked, onRagCompareClicked, onSupportingContentClicked, onFollowupQuestionClicked, showFollowupQuestions, onAdjustClick, - onRegenerateClick + onRegenerateClick, + chatMode }: Props) => { - const parsedAnswer = useMemo(() => parseAnswerToHtml(answer.answer, answer.source, answer.citation_lookup, onCitationClicked), [answer]); + const parsedAnswer = useMemo(() => parseAnswerToHtml(answer.answer, answer.approach, answer.citation_lookup, onCitationClicked), [answer]); const sanitizedAnswerHtml = DOMPurify.sanitize(parsedAnswer.answerHtml); return ( - + - +
- onThoughtProcessClicked()} - disabled={!answer.thoughts} - /> - {answer.source !== 'bing' && !answer.comparative && + {answer.approach != Approaches.GPTDirect && + onThoughtProcessClicked()} + disabled={!answer.thoughts} + /> + } + {answer.approach == Approaches.ReadRetrieveRead && - {answer.source === 'bing' && -
- ****Warning: This response is from the internet using Bing**** + {(answer.approach == Approaches.ChatWebRetrieveRead || answer.approach == Approaches.CompareWorkWithWeb) && +
+ Your personal and company data are protected
} -
+
{!!parsedAnswer.citations.length && ( @@ -91,15 +100,23 @@ export const Answer = ({ {parsedAnswer.citations.map((x, i) => { const path = getCitationFilePath(x); return ( - onCitationClicked(path, (parsedAnswer.sourceFiles as any)[x], (parsedAnswer.pageNumbers as any)[x])}> + (parsedAnswer.approach == Approaches.ChatWebRetrieveRead || parsedAnswer.approach == Approaches.CompareWorkWithWeb) ? + {`${++i}. ${x}`} + + : + onCitationClicked(path, (parsedAnswer.sourceFiles as any)[x], (parsedAnswer.pageNumbers as any)[x])}> + {`${++i}. ${x}`} ); })} + )} - + {!!parsedAnswer.followupQuestions.length && showFollowupQuestions && onFollowupQuestionClicked && ( @@ -114,8 +131,11 @@ export const Answer = ({ )} + +
AI-generated content may be incorrect
+
- + ); diff --git a/app/frontend/src/components/Answer/AnswerIcon.tsx b/app/frontend/src/components/Answer/AnswerIcon.tsx index 39262a83a..7aa7b7ff4 100644 --- a/app/frontend/src/components/Answer/AnswerIcon.tsx +++ b/app/frontend/src/components/Answer/AnswerIcon.tsx @@ -1,16 +1,29 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { Sparkle28Filled } from "@fluentui/react-icons"; -import { Icon } from "@fluentui/react"; +import { BuildingMultiple24Filled, Globe24Filled, Link20Filled, Sparkle24Filled } from "@fluentui/react-icons"; +import { Approaches } from "../../api"; + +import styles from "./Answer.module.css"; interface AnswerIconProps { - source?: string; + approach: Approaches; } -export const AnswerIcon: React.FC = ({ source }) => { - if (source === 'bing') { - return