From 0f4d43da10475ce35695600b0d45767b142f218a Mon Sep 17 00:00:00 2001 From: dayland Date: Mon, 18 Mar 2024 23:36:06 +0000 Subject: [PATCH 1/3] Remove references to Bing and update ux design for work, web, and compare --- app/backend/app.py | 14 +-- app/backend/approaches/approach.py | 6 +- ...atbingsearch.py => chatwebretrieveread.py} | 2 +- ...rrbingcompare.py => comparewebwithwork.py} | 16 +-- ...searchcompare.py => compareworkwithweb.py} | 27 +++-- app/frontend/src/api/models.ts | 9 +- .../src/components/Answer/Answer.module.css | 96 ++++++++++++++-- app/frontend/src/components/Answer/Answer.tsx | 47 +++++--- .../src/components/Answer/AnswerIcon.tsx | 27 +++-- .../src/components/Answer/AnswerLoading.tsx | 3 +- .../src/components/Answer/AnswerParser.tsx | 13 ++- .../src/components/RAIPanel/RAIPanel.tsx | 41 ++++--- .../UserChatMessage.module.css | 24 +++- .../UserChatMessage/UserChatMessage.tsx | 23 +++- app/frontend/src/pages/chat/Chat.tsx | 107 ++++-------------- .../src/pages/layout/Layout.module.css | 23 ---- app/frontend/src/pages/layout/Layout.tsx | 29 ----- infra/main.tf | 2 +- 18 files changed, 269 insertions(+), 240 deletions(-) rename app/backend/approaches/{chatbingsearch.py => chatwebretrieveread.py} (99%) rename app/backend/approaches/{chatrrrbingcompare.py => comparewebwithwork.py} (90%) rename app/backend/approaches/{chatbingsearchcompare.py => compareworkwithweb.py} (78%) 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/frontend/src/api/models.ts b/app/frontend/src/api/models.ts index 3f2299570..446a40e91 100644 --- a/app/frontend/src/api/models.ts +++ b/app/frontend/src/api/models.ts @@ -8,9 +8,9 @@ export const enum Approaches { ReadRetrieveRead = 1, ReadDecomposeAsk = 2, GPTDirect = 3, - BingSearch = 4, - BingSearchCompare = 5, - BingRRRCompare = 6 + ChatWebRetrieveRead = 4, + CompareWorkWithWeb = 5, + CompareWebWithWork = 6 } export type AskRequestOverrides = { @@ -43,8 +43,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..ef5095340 100644 --- a/app/frontend/src/components/Answer/Answer.module.css +++ b/app/frontend/src/components/Answer/Answer.module.css @@ -9,20 +9,28 @@ 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; } .warningText { @@ -32,8 +40,31 @@ 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; } .answerText { @@ -55,7 +86,7 @@ line-height: 24px; } -.citation { +.citationWork { font-weight: 500; line-height: 24px; text-align: center; @@ -67,7 +98,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 +212,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..95f13f1ce 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 } from "../../api"; import { parseAnswerToHtml } from "./AnswerParser"; import { AnswerIcon } from "./AnswerIcon"; import { RAIPanel } from "../RAIPanel"; @@ -17,9 +18,9 @@ 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; @@ -33,9 +34,9 @@ export const Answer = ({ isSelected, onCitationClicked, onThoughtProcessClicked, - onBingSearchClicked, + onWebSearchClicked, onRagSearchClicked, - onBingCompareClicked, + onWebCompareClicked, onRagCompareClicked, onSupportingContentClicked, onFollowupQuestionClicked, @@ -43,15 +44,18 @@ export const Answer = ({ onAdjustClick, onRegenerateClick }: 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.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
}
@@ -91,15 +95,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 +126,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..50089ef28 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 { DocumentLock24Filled, Globe24Filled, Link20Filled, Sparkle20Filled } 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