mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-04-09 04:18:32 +02:00
Replace 'respond_sender_only' with 'respond_tag_only' + prettify UI
This commit is contained in:
parent
59bac1ca8f
commit
29a0a45518
@ -55,7 +55,7 @@ def handle_message(
|
||||
|
||||
# List of user id to send message to, if None, send to everyone in channel
|
||||
send_to: list[str] | None = None
|
||||
respond_sender_only = False
|
||||
respond_tag_only = False
|
||||
respond_team_member_list = None
|
||||
if slack_bot_config and slack_bot_config.channel_config:
|
||||
channel_conf = slack_bot_config.channel_config
|
||||
@ -77,14 +77,20 @@ def handle_message(
|
||||
f"validity checks enabled: {channel_conf['answer_filters']}"
|
||||
)
|
||||
|
||||
respond_sender_only = channel_conf.get("respond_sender_only") or False
|
||||
respond_tag_only = channel_conf.get("respond_tag_only") or False
|
||||
respond_team_member_list = (
|
||||
channel_conf.get("respond_team_member_list") or None
|
||||
)
|
||||
|
||||
if sender_id and (respond_sender_only or is_bot_msg):
|
||||
send_to = [sender_id]
|
||||
elif respond_team_member_list:
|
||||
# `skip_filters=True` -> this is a tag, so we *should* respond
|
||||
if respond_tag_only and not skip_filters:
|
||||
logger.info(
|
||||
"Skipping message since the channel is configured such that "
|
||||
"DanswerBot only responds to tags"
|
||||
)
|
||||
return
|
||||
|
||||
if respond_team_member_list:
|
||||
send_to = fetch_userids_from_emails(respond_team_member_list, client)
|
||||
|
||||
# If configured to respond to team members only, then cannot be used with a /danswerbot command
|
||||
@ -208,14 +214,7 @@ def handle_message(
|
||||
|
||||
# For DM (ephemeral message), we need to create a thread via a normal message so the user can see
|
||||
# the ephemeral message. This also will give the user a notification which ephemeral message does not.
|
||||
if respond_sender_only:
|
||||
respond_in_thread(
|
||||
client=client,
|
||||
channel=channel,
|
||||
text="We've just DM-ed you the answer, hope you find it useful! 💃",
|
||||
thread_ts=message_ts_to_respond_to,
|
||||
)
|
||||
elif respond_team_member_list:
|
||||
if respond_team_member_list:
|
||||
respond_in_thread(
|
||||
client=client,
|
||||
channel=channel,
|
||||
|
@ -2,6 +2,7 @@ import datetime
|
||||
from enum import Enum as PyEnum
|
||||
from typing import Any
|
||||
from typing import List
|
||||
from typing import Literal
|
||||
from typing import NotRequired
|
||||
from typing import TypedDict
|
||||
from uuid import UUID
|
||||
@ -477,14 +478,19 @@ class ChatMessage(Base):
|
||||
persona: Mapped[Persona | None] = relationship("Persona")
|
||||
|
||||
|
||||
AllowedAnswerFilters = (
|
||||
Literal["well_answered_postfilter"] | Literal["questionmark_prefilter"]
|
||||
)
|
||||
|
||||
|
||||
class ChannelConfig(TypedDict):
|
||||
"""NOTE: is a `TypedDict` so it can be used a type hint for a JSONB column
|
||||
in Postgres"""
|
||||
|
||||
channel_names: list[str]
|
||||
respond_sender_only: NotRequired[bool] # defaults to False
|
||||
respond_tag_only: NotRequired[bool] # defaults to False
|
||||
respond_team_member_list: NotRequired[list[str]]
|
||||
answer_filters: NotRequired[list[str]]
|
||||
answer_filters: NotRequired[list[AllowedAnswerFilters]]
|
||||
|
||||
|
||||
class SlackBotConfig(Base):
|
||||
|
@ -19,6 +19,7 @@ from danswer.configs.constants import QAFeedbackType
|
||||
from danswer.configs.constants import SearchFeedbackType
|
||||
from danswer.connectors.models import InputType
|
||||
from danswer.datastores.interfaces import IndexFilter
|
||||
from danswer.db.models import AllowedAnswerFilters
|
||||
from danswer.db.models import ChannelConfig
|
||||
from danswer.db.models import Connector
|
||||
from danswer.db.models import Credential
|
||||
@ -436,10 +437,10 @@ class SlackBotConfigCreationRequest(BaseModel):
|
||||
# for now for simplicity / speed of development
|
||||
document_sets: list[int]
|
||||
channel_names: list[str]
|
||||
# If not responder_sender_only and no team members, assume respond in the channel to everyone
|
||||
respond_sender_only: bool = False
|
||||
respond_tag_only: bool = False
|
||||
# If no team members, assume respond in the channel to everyone
|
||||
respond_team_member_list: list[str] = []
|
||||
answer_filters: list[str] = []
|
||||
answer_filters: list[AllowedAnswerFilters] = []
|
||||
|
||||
@validator("answer_filters", pre=True)
|
||||
def validate_filters(cls, value: list[str]) -> list[str]:
|
||||
|
@ -29,7 +29,7 @@ def _form_channel_config(
|
||||
db_session: Session,
|
||||
) -> ChannelConfig:
|
||||
raw_channel_names = slack_bot_config_creation_request.channel_names
|
||||
respond_sender_only = slack_bot_config_creation_request.respond_sender_only
|
||||
respond_tag_only = slack_bot_config_creation_request.respond_tag_only
|
||||
respond_team_member_list = (
|
||||
slack_bot_config_creation_request.respond_team_member_list
|
||||
)
|
||||
@ -53,17 +53,17 @@ def _form_channel_config(
|
||||
detail=str(e),
|
||||
)
|
||||
|
||||
if respond_sender_only and respond_team_member_list:
|
||||
if respond_tag_only and respond_team_member_list:
|
||||
raise ValueError(
|
||||
"Cannot set DanswerBot to only respond to sender and "
|
||||
"also respond to a predetermined set of users. This is not logically possible..."
|
||||
"Cannot set DanswerBot to only respond to tags only and "
|
||||
"also respond to a predetermined set of users."
|
||||
)
|
||||
|
||||
channel_config: ChannelConfig = {
|
||||
"channel_names": cleaned_channel_names,
|
||||
}
|
||||
if respond_sender_only is not None:
|
||||
channel_config["respond_sender_only"] = respond_sender_only
|
||||
if respond_tag_only is not None:
|
||||
channel_config["respond_tag_only"] = respond_tag_only
|
||||
if respond_team_member_list:
|
||||
channel_config["respond_team_member_list"] = respond_team_member_list
|
||||
if answer_filters:
|
||||
|
@ -26,11 +26,11 @@ export const SlackBotCreationForm = ({
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
|
||||
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-10 overflow-y-auto overscroll-contain"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="bg-gray-800 p-6 rounded border border-gray-700 shadow-lg relative w-1/2 text-sm"
|
||||
className="bg-gray-800 rounded-lg border border-gray-700 shadow-lg relative w-1/2 text-sm"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<Formik
|
||||
@ -44,8 +44,8 @@ export const SlackBotCreationForm = ({
|
||||
questionmark_prefilter_enabled: (
|
||||
existingSlackBotConfig?.channel_config?.answer_filters || []
|
||||
).includes("questionmark_prefilter"),
|
||||
respond_sender_only:
|
||||
existingSlackBotConfig?.channel_config?.respond_sender_only ||
|
||||
respond_tag_only:
|
||||
existingSlackBotConfig?.channel_config?.respond_tag_only ||
|
||||
false,
|
||||
respond_team_member_list:
|
||||
existingSlackBotConfig?.channel_config
|
||||
@ -60,7 +60,7 @@ export const SlackBotCreationForm = ({
|
||||
channel_names: Yup.array().of(Yup.string()),
|
||||
answer_validity_check_enabled: Yup.boolean().required(),
|
||||
questionmark_prefilter_enabled: Yup.boolean().required(),
|
||||
respond_sender_only: Yup.boolean().required(),
|
||||
respond_tag_only: Yup.boolean().required(),
|
||||
respond_team_member_list: Yup.array().of(Yup.string()).required(),
|
||||
document_sets: Yup.array().of(Yup.number()),
|
||||
})}
|
||||
@ -98,7 +98,8 @@ export const SlackBotCreationForm = ({
|
||||
});
|
||||
onClose();
|
||||
} else {
|
||||
const errorMsg = (await response.json()).detail;
|
||||
const responseJson = await response.json();
|
||||
const errorMsg = responseJson.detail || responseJson.message;
|
||||
setPopup({
|
||||
message: isUpdate
|
||||
? `Error updating DanswerBot config - ${errorMsg}`
|
||||
@ -110,83 +111,83 @@ export const SlackBotCreationForm = ({
|
||||
>
|
||||
{({ isSubmitting, values }) => (
|
||||
<Form>
|
||||
<h2 className="text-lg font-bold mb-3">
|
||||
<h2 className="text-xl font-bold mb-3 border-b border-gray-600 pt-4 pb-3 bg-gray-700 px-6">
|
||||
{isUpdate
|
||||
? "Update a DanswerBot Config"
|
||||
: "Create a new DanswerBot Config"}
|
||||
</h2>
|
||||
<TextArrayField
|
||||
name="channel_names"
|
||||
label="Channel Names:"
|
||||
values={values}
|
||||
subtext={
|
||||
<div>
|
||||
The names of the Slack channels you want this
|
||||
configuration to apply to. For example,
|
||||
'#ask-danswer'.
|
||||
<br />
|
||||
<br />
|
||||
<i>NOTE</i>: you still need to add DanswerBot to the
|
||||
channel(s) in Slack itself. Setting this config will not
|
||||
auto-add the bot to the channel.
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<div className="border-t border-gray-700 py-2" />
|
||||
<BooleanFormField
|
||||
name="answer_validity_check_enabled"
|
||||
label="Hide Non-Answers"
|
||||
subtext="If set, will only answer questions that the model determines it can answer"
|
||||
/>
|
||||
<div className="border-t border-gray-700 py-2" />
|
||||
<BooleanFormField
|
||||
name="questionmark_prefilter_enabled"
|
||||
label="Only respond to questions"
|
||||
subtext="If set, will only respond to messages that contain a question mark"
|
||||
/>
|
||||
<div className="border-t border-gray-700 py-2" />
|
||||
<BooleanFormField
|
||||
name="respond_sender_only"
|
||||
label="Respond to Sender Only"
|
||||
subtext="If set, will respond with a message that is only visible to the sender"
|
||||
/>
|
||||
<div className="border-t border-gray-700 py-2" />
|
||||
<TextArrayField
|
||||
name="respond_team_member_list"
|
||||
label="Team Members Emails:"
|
||||
subtext={`If specified, DanswerBot responses will only be
|
||||
<div className="px-6 pb-6">
|
||||
<TextArrayField
|
||||
name="channel_names"
|
||||
label="Channel Names:"
|
||||
values={values}
|
||||
subtext={
|
||||
<div>
|
||||
The names of the Slack channels you want this
|
||||
configuration to apply to. For example,
|
||||
'#ask-danswer'.
|
||||
<br />
|
||||
<br />
|
||||
<i>NOTE</i>: you still need to add DanswerBot to the
|
||||
channel(s) in Slack itself. Setting this config will not
|
||||
auto-add the bot to the channel.
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<div className="border-t border-gray-600 py-2" />
|
||||
<BooleanFormField
|
||||
name="answer_validity_check_enabled"
|
||||
label="Hide Non-Answers"
|
||||
subtext="If set, will only answer questions that the model determines it can answer"
|
||||
/>
|
||||
<div className="border-t border-gray-600 py-2" />
|
||||
<BooleanFormField
|
||||
name="questionmark_prefilter_enabled"
|
||||
label="Only respond to questions"
|
||||
subtext="If set, will only respond to messages that contain a question mark"
|
||||
/>
|
||||
<div className="border-t border-gray-600 py-2" />
|
||||
<BooleanFormField
|
||||
name="respond_tag_only"
|
||||
label="Respond to @DanswerBot Only"
|
||||
subtext="If set, DanswerBot will only respond when directly tagged"
|
||||
/>
|
||||
<div className="border-t border-gray-600 py-2" />
|
||||
<TextArrayField
|
||||
name="respond_team_member_list"
|
||||
label="Team Members Emails:"
|
||||
subtext={`If specified, DanswerBot responses will only be
|
||||
visible to members in this list. This is
|
||||
useful if you want DanswerBot to operate in an
|
||||
"assistant" mode, where it helps the team members find
|
||||
answers, but let's them build on top of DanswerBot's response / throw
|
||||
out the occasional incorrect answer.`}
|
||||
values={values}
|
||||
/>
|
||||
<div className="border-t border-gray-700 py-2" />
|
||||
<FieldArray
|
||||
name="document_sets"
|
||||
render={(arrayHelpers: ArrayHelpers) => (
|
||||
<div>
|
||||
values={values}
|
||||
/>
|
||||
<div className="border-t border-gray-600 py-2" />
|
||||
<FieldArray
|
||||
name="document_sets"
|
||||
render={(arrayHelpers: ArrayHelpers) => (
|
||||
<div>
|
||||
Document Sets:
|
||||
<br />
|
||||
<div className="text-xs">
|
||||
The document sets that DanswerBot should search
|
||||
through. If left blank, DanswerBot will search through
|
||||
all documents.
|
||||
<div>
|
||||
<p className="font-medium">Document Sets:</p>
|
||||
<div className="text-xs">
|
||||
The document sets that DanswerBot should search
|
||||
through. If left blank, DanswerBot will search
|
||||
through all documents.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3 mt-2 flex gap-2 flex-wrap">
|
||||
{documentSets.map((documentSet) => {
|
||||
const ind = values.document_sets.indexOf(
|
||||
documentSet.id
|
||||
);
|
||||
let isSelected = ind !== -1;
|
||||
return (
|
||||
<div
|
||||
key={documentSet.id}
|
||||
className={
|
||||
`
|
||||
<div className="mb-3 mt-2 flex gap-2 flex-wrap">
|
||||
{documentSets.map((documentSet) => {
|
||||
const ind = values.document_sets.indexOf(
|
||||
documentSet.id
|
||||
);
|
||||
let isSelected = ind !== -1;
|
||||
return (
|
||||
<div
|
||||
key={documentSet.id}
|
||||
className={
|
||||
`
|
||||
px-3
|
||||
py-1
|
||||
rounded-lg
|
||||
@ -195,38 +196,42 @@ export const SlackBotCreationForm = ({
|
||||
w-fit
|
||||
flex
|
||||
cursor-pointer ` +
|
||||
(isSelected
|
||||
? " bg-gray-600"
|
||||
: " bg-gray-900 hover:bg-gray-700")
|
||||
}
|
||||
onClick={() => {
|
||||
if (isSelected) {
|
||||
arrayHelpers.remove(ind);
|
||||
} else {
|
||||
arrayHelpers.push(documentSet.id);
|
||||
(isSelected
|
||||
? " bg-gray-600"
|
||||
: " bg-gray-900 hover:bg-gray-700")
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="my-auto">{documentSet.name}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
onClick={() => {
|
||||
if (isSelected) {
|
||||
arrayHelpers.remove(ind);
|
||||
} else {
|
||||
arrayHelpers.push(documentSet.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="my-auto">
|
||||
{documentSet.name}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<div className="flex">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className={
|
||||
"bg-slate-500 hover:bg-slate-700 text-white " +
|
||||
"font-bold py-2 px-4 rounded focus:outline-none " +
|
||||
"focus:shadow-outline w-full max-w-sm mx-auto"
|
||||
}
|
||||
>
|
||||
{isUpdate ? "Update!" : "Create!"}
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
<div className="border-t border-gray-600 py-2" />
|
||||
<div className="flex">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className={
|
||||
"bg-slate-500 hover:bg-slate-700 text-white " +
|
||||
"font-bold py-2 px-4 rounded focus:outline-none " +
|
||||
"focus:shadow-outline w-full max-w-sm mx-auto"
|
||||
}
|
||||
>
|
||||
{isUpdate ? "Update!" : "Create!"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
|
@ -5,7 +5,7 @@ interface SlackBotConfigCreationRequest {
|
||||
channel_names: string[];
|
||||
answer_validity_check_enabled: boolean;
|
||||
questionmark_prefilter_enabled: boolean;
|
||||
respond_sender_only: boolean;
|
||||
respond_tag_only: boolean;
|
||||
respond_team_member_list: string[];
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ const buildRequestBodyFromCreationRequest = (
|
||||
) => {
|
||||
return JSON.stringify({
|
||||
channel_names: creationRequest.channel_names,
|
||||
respond_sender_only: creationRequest.respond_sender_only,
|
||||
respond_tag_only: creationRequest.respond_tag_only,
|
||||
respond_team_member_list: creationRequest.respond_team_member_list,
|
||||
document_sets: creationRequest.document_sets,
|
||||
answer_filters: buildFiltersFromCreationRequest(creationRequest),
|
||||
|
@ -101,6 +101,10 @@ const SlackBotConfigsTable = ({
|
||||
header: "Questions Only",
|
||||
key: "question_mark_only",
|
||||
},
|
||||
{
|
||||
header: "Tags Only",
|
||||
key: "respond_tag_only",
|
||||
},
|
||||
{
|
||||
header: "Delete",
|
||||
key: "delete",
|
||||
@ -154,6 +158,12 @@ const SlackBotConfigsTable = ({
|
||||
) : (
|
||||
<div className="text-gray-300">No</div>
|
||||
),
|
||||
respond_tag_only:
|
||||
slackBotConfig.channel_config.respond_tag_only || false ? (
|
||||
<div className="text-gray-300">Yes</div>
|
||||
) : (
|
||||
<div className="text-gray-300">No</div>
|
||||
),
|
||||
delete: (
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
|
@ -33,7 +33,7 @@ export const TextFormField = ({
|
||||
}: TextFormFieldProps) => {
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<label htmlFor={name} className="block">
|
||||
<label htmlFor={name} className="block font-medium">
|
||||
{label}
|
||||
</label>
|
||||
{subtext && <p className="text-xs mb-1">{subtext}</p>}
|
||||
@ -82,7 +82,7 @@ export const BooleanFormField = ({
|
||||
<label className="flex text-sm">
|
||||
<Field name={name} type="checkbox" className="mx-3 px-5" />
|
||||
<div>
|
||||
{label}
|
||||
<p className="font-medium">{label}</p>
|
||||
{subtext && <p className="text-xs">{subtext}</p>}
|
||||
</div>
|
||||
</label>
|
||||
@ -113,7 +113,7 @@ export function TextArrayField<T extends Yup.AnyObject>({
|
||||
}: TextArrayFieldProps<T>) {
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<label htmlFor={name} className="block">
|
||||
<label htmlFor={name} className="block font-medium">
|
||||
{label}
|
||||
</label>
|
||||
{subtext && <p className="text-xs">{subtext}</p>}
|
||||
|
@ -7,7 +7,7 @@ export interface PopupSpec {
|
||||
|
||||
export const Popup: React.FC<PopupSpec> = ({ message, type }) => (
|
||||
<div
|
||||
className={`fixed bottom-4 left-4 p-4 rounded-md shadow-lg text-white ${
|
||||
className={`fixed bottom-4 left-4 p-4 rounded-md shadow-lg text-white z-30 ${
|
||||
type === "success" ? "bg-green-500" : "bg-red-500"
|
||||
}`}
|
||||
>
|
||||
|
@ -245,7 +245,7 @@ export type AnswerFilterOption =
|
||||
|
||||
export interface ChannelConfig {
|
||||
channel_names: string[];
|
||||
respond_sender_only?: boolean;
|
||||
respond_tag_only?: boolean;
|
||||
respond_team_member_list?: string[];
|
||||
answer_filters?: AnswerFilterOption[];
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user