Add predefined feedback option

This commit is contained in:
Weves 2024-05-09 18:51:51 -07:00 committed by Chris Weaver
parent 957d3625c2
commit 7f1ffa3921
13 changed files with 122 additions and 37 deletions

View File

@ -0,0 +1,25 @@
"""Add pre-defined feedback
Revision ID: f1c6478c3fd8
Revises: 643a84a42a33
Create Date: 2024-05-09 18:11:49.210667
"""
from alembic import op
import sqlalchemy as sa
revision = "f1c6478c3fd8"
down_revision = "643a84a42a33"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"chat_feedback",
sa.Column("predefined_feedback", sa.String(), nullable=True),
)
def downgrade() -> None:
op.drop_column("chat_feedback", "predefined_feedback")

View File

@ -148,8 +148,14 @@ def create_chat_message_feedback(
db_session: Session,
# Slack user requested help from human
required_followup: bool | None = None,
predefined_feedback: str | None = None, # Added predefined_feedback parameter
) -> None:
if is_positive is None and feedback_text is None and required_followup is None:
if (
is_positive is None
and feedback_text is None
and required_followup is None
and predefined_feedback is None
):
raise ValueError("No feedback provided")
chat_message = get_chat_message(
@ -164,6 +170,7 @@ def create_chat_message_feedback(
is_positive=is_positive,
feedback_text=feedback_text,
required_followup=required_followup,
predefined_feedback=predefined_feedback,
)
db_session.add(message_feedback)

View File

@ -763,6 +763,7 @@ class ChatMessageFeedback(Base):
is_positive: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
required_followup: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
feedback_text: Mapped[str | None] = mapped_column(Text, nullable=True)
predefined_feedback: Mapped[str | None] = mapped_column(String, nullable=True)
chat_message: Mapped[ChatMessage] = relationship(
"ChatMessage", back_populates="chat_message_feedbacks"

View File

@ -288,6 +288,7 @@ def create_chat_feedback(
create_chat_message_feedback(
is_positive=feedback.is_positive,
feedback_text=feedback.feedback_text,
predefined_feedback=feedback.predefined_feedback,
chat_message_id=feedback.chat_message_id,
user_id=user_id,
db_session=db_session,

View File

@ -51,6 +51,7 @@ class ChatFeedbackRequest(BaseModel):
chat_message_id: int
is_positive: bool | None = None
feedback_text: str | None = None
predefined_feedback: str | None = None
@root_validator
def check_is_positive_or_feedback_text(cls: BaseModel, values: dict) -> dict:

View File

@ -201,6 +201,8 @@ services:
args:
- NEXT_PUBLIC_DISABLE_STREAMING=${NEXT_PUBLIC_DISABLE_STREAMING:-false}
- NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA=${NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA:-false}
- NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
- NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
depends_on:
- api_server
restart: always

View File

@ -197,6 +197,8 @@ services:
args:
- NEXT_PUBLIC_DISABLE_STREAMING=${NEXT_PUBLIC_DISABLE_STREAMING:-false}
- NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA=${NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA:-false}
- NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
- NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
depends_on:
- api_server
restart: always

View File

@ -73,6 +73,8 @@ services:
args:
- NEXT_PUBLIC_DISABLE_STREAMING=${NEXT_PUBLIC_DISABLE_STREAMING:-false}
- NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA=${NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA:-false}
- NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
- NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
depends_on:
- api_server
restart: always

View File

@ -73,6 +73,8 @@ services:
args:
- NEXT_PUBLIC_DISABLE_STREAMING=${NEXT_PUBLIC_DISABLE_STREAMING:-false}
- NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA=${NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA:-false}
- NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
- NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
depends_on:
- api_server
restart: always

View File

@ -44,6 +44,13 @@ ENV NEXT_PUBLIC_DISABLE_STREAMING=${NEXT_PUBLIC_DISABLE_STREAMING}
ARG NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA
ENV NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA=${NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA}
# allow user to specify custom feedback options
ARG NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS
ENV NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS}
ARG NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS
ENV NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS}
RUN npm run build
# Step 3. Production image, copy all the files and run next
@ -82,6 +89,13 @@ ENV NEXT_PUBLIC_DISABLE_STREAMING=${NEXT_PUBLIC_DISABLE_STREAMING}
ARG NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA
ENV NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA=${NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA}
# allow user to specify custom feedback options
ARG NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS
ENV NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS}
ARG NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS
ENV NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS}
# Note: Don't expose ports here, Compose will handle that for us if necessary.
# If you want to run this without compose, specify the ports to
# expose via cli

View File

@ -576,7 +576,8 @@ export function ChatPage({
const onFeedback = async (
messageId: number,
feedbackType: FeedbackType,
feedbackDetails: string
feedbackDetails: string,
predefinedFeedback: string | undefined
) => {
if (chatSessionId === null) {
return;
@ -585,7 +586,8 @@ export function ChatPage({
const response = await handleChatFeedback(
messageId,
feedbackType,
feedbackDetails
feedbackDetails,
predefinedFeedback
);
if (response.ok) {
@ -648,11 +650,12 @@ export function ChatPage({
<FeedbackModal
feedbackType={currentFeedback[0]}
onClose={() => setCurrentFeedback(null)}
onSubmit={(feedbackDetails) => {
onSubmit={({ message, predefinedFeedback }) => {
onFeedback(
currentFeedback[1],
currentFeedback[0],
feedbackDetails
message,
predefinedFeedback
);
setCurrentFeedback(null);
}}

View File

@ -150,7 +150,8 @@ export async function nameChatSession(chatSessionId: number, message: string) {
export async function handleChatFeedback(
messageId: number,
feedback: FeedbackType,
feedbackDetails: string
feedbackDetails: string,
predefinedFeedback: string | undefined
) {
const response = await fetch("/api/chat/create-chat-message-feedback", {
method: "POST",
@ -161,11 +162,11 @@ export async function handleChatFeedback(
chat_message_id: messageId,
is_positive: feedback === "like",
feedback_text: feedbackDetails,
predefined_feedback: predefinedFeedback,
}),
});
return response;
}
export async function renameChatSession(
chatSessionId: number,
newName: string

View File

@ -5,10 +5,23 @@ import { FeedbackType } from "../types";
import { FiThumbsDown, FiThumbsUp } from "react-icons/fi";
import { ModalWrapper } from "./ModalWrapper";
const predefinedPositiveFeedbackOptions =
process.env.NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS?.split(",") ||
[];
const predefinedNegativeFeedbackOptions =
process.env.NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS?.split(",") || [
"Retrieved documents were not relevant",
"AI misread the documents",
"Cited source had incorrect information",
];
interface FeedbackModalProps {
feedbackType: FeedbackType;
onClose: () => void;
onSubmit: (feedbackDetails: string) => void;
onSubmit: (feedbackDetails: {
message: string;
predefinedFeedback?: string;
}) => void;
}
export const FeedbackModal = ({
@ -17,6 +30,23 @@ export const FeedbackModal = ({
onSubmit,
}: FeedbackModalProps) => {
const [message, setMessage] = useState("");
const [predefinedFeedback, setPredefinedFeedback] = useState<
string | undefined
>();
const handlePredefinedFeedback = (feedback: string) => {
setPredefinedFeedback(feedback);
};
const handleSubmit = () => {
onSubmit({ message, predefinedFeedback });
onClose();
};
const predefinedFeedbackOptions =
feedbackType === "like"
? predefinedPositiveFeedbackOptions
: predefinedNegativeFeedbackOptions;
return (
<ModalWrapper onClose={onClose} modalClassName="max-w-5xl">
@ -31,49 +61,43 @@ export const FeedbackModal = ({
</div>
Provide additional feedback
</h2>
<div className="mb-4 flex flex-wrap justify-start">
{predefinedFeedbackOptions.map((feedback, index) => (
<button
key={index}
className={`bg-border hover:bg-hover text-default py-2 px-4 rounded m-1
${predefinedFeedback === feedback && "ring-2 ring-accent"}`}
onClick={() => handlePredefinedFeedback(feedback)}
>
{feedback}
</button>
))}
</div>
<textarea
autoFocus
className={`
w-full
flex-grow
ml-2
border
border-border-strong
rounded
outline-none
placeholder-subtle
pl-4
pr-14
py-4
bg-background
overflow-hidden
h-28
whitespace-normal
resize-none
break-all
overscroll-contain`}
w-full flex-grow
border border-border-strong rounded
outline-none placeholder-subtle
pl-4 pr-4 py-4 bg-background
overflow-hidden h-28
whitespace-normal resize-none
break-all overscroll-contain
`}
role="textarea"
aria-multiline
placeholder={
feedbackType === "like"
? "What did you like about this response?"
: "What was the issue with the response? How could it be improved?"
? "(Optional) What did you like about this response?"
: "(Optional) What was the issue with the response? How could it be improved?"
}
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyDown={(event) => {
if (event.key === "Enter" && !event.shiftKey) {
onSubmit(message);
event.preventDefault();
}
}}
suppressContentEditableWarning={true}
/>
<div className="flex mt-2">
<button
className="bg-accent text-white py-2 px-4 rounded hover:bg-blue-600 focus:outline-none mx-auto"
onClick={() => onSubmit(message)}
onClick={handleSubmit}
>
Submit feedback
</button>