mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-27 20:38:32 +02:00
Slack Followup Option (#948)
This commit is contained in:
26
backend/alembic/versions/7f726bad5367_slack_followup.py
Normal file
26
backend/alembic/versions/7f726bad5367_slack_followup.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""Slack Followup
|
||||
|
||||
Revision ID: 7f726bad5367
|
||||
Revises: 79acd316403a
|
||||
Create Date: 2024-01-15 00:19:55.991224
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "7f726bad5367"
|
||||
down_revision = "79acd316403a"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"chat_feedback",
|
||||
sa.Column("required_followup", sa.Boolean(), nullable=True),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("chat_feedback", "required_followup")
|
@@ -16,7 +16,6 @@ from danswer.background.indexing.run_indexing import run_indexing_entrypoint
|
||||
from danswer.configs.app_configs import CLEANUP_INDEXING_JOBS_TIMEOUT
|
||||
from danswer.configs.app_configs import DASK_JOB_CLIENT_ENABLED
|
||||
from danswer.configs.app_configs import LOG_LEVEL
|
||||
from danswer.configs.app_configs import MODEL_SERVER_HOST
|
||||
from danswer.configs.app_configs import NUM_INDEXING_WORKERS
|
||||
from danswer.configs.model_configs import MIN_THREADS_ML_MODELS
|
||||
from danswer.db.connector import fetch_connectors
|
||||
@@ -33,7 +32,6 @@ from danswer.db.index_attempt import mark_attempt_failed
|
||||
from danswer.db.models import Connector
|
||||
from danswer.db.models import IndexAttempt
|
||||
from danswer.db.models import IndexingStatus
|
||||
from danswer.search.search_nlp_models import warm_up_models
|
||||
from danswer.utils.logger import setup_logger
|
||||
|
||||
logger = setup_logger()
|
||||
@@ -343,9 +341,6 @@ def update__main() -> None:
|
||||
if not DASK_JOB_CLIENT_ENABLED:
|
||||
torch.multiprocessing.set_start_method("spawn")
|
||||
|
||||
if not MODEL_SERVER_HOST:
|
||||
logger.info("Warming up Embedding Model(s)")
|
||||
warm_up_models(indexer_only=True, skip_cross_encoders=True)
|
||||
logger.info("Starting Indexing Loop")
|
||||
update_loop()
|
||||
|
||||
|
@@ -17,6 +17,8 @@ DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER = os.environ.get(
|
||||
).lower() not in ["false", ""]
|
||||
# When Danswer is considering a message, what emoji does it react with
|
||||
DANSWER_REACT_EMOJI = os.environ.get("DANSWER_REACT_EMOJI") or "eyes"
|
||||
# When User needs more help, what should the emoji be
|
||||
DANSWER_FOLLOWUP_EMOJI = os.environ.get("DANSWER_FOLLOWUP_EMOJI") or "sos"
|
||||
# Should DanswerBot send an apology message if it's not able to find an answer
|
||||
# That way the user isn't confused as to why DanswerBot reacted but then said nothing
|
||||
# Off by default to be less intrusive (don't want to give a notif that just says we couldnt help)
|
||||
|
@@ -91,6 +91,9 @@ def make_slack_api_rate_limited(
|
||||
f"Slack call rate limited, retrying after {retry_after} seconds. Exception: {e}"
|
||||
)
|
||||
time.sleep(retry_after)
|
||||
if e.response["error"] in ["already_reacted", "no_reaction"]:
|
||||
# The response isn't used for reactions, this is basically just a pass
|
||||
return e.response
|
||||
else:
|
||||
# Raise the error for non-transient errors
|
||||
raise
|
||||
|
@@ -18,6 +18,8 @@ from danswer.configs.constants import SearchFeedbackType
|
||||
from danswer.configs.danswerbot_configs import DANSWER_BOT_NUM_DOCS_TO_DISPLAY
|
||||
from danswer.danswerbot.slack.constants import DISLIKE_BLOCK_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import FEEDBACK_DOC_BUTTON_BLOCK_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import FOLLOWUP_BUTTON_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import FOLLOWUP_BUTTON_RESOLVED_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import LIKE_BLOCK_ACTION_ID
|
||||
from danswer.danswerbot.slack.utils import build_feedback_id
|
||||
from danswer.danswerbot.slack.utils import remove_slack_text_interactions
|
||||
@@ -211,6 +213,7 @@ def build_qa_response_blocks(
|
||||
time_cutoff: datetime | None,
|
||||
favor_recent: bool,
|
||||
skip_quotes: bool = False,
|
||||
skip_ai_feedback: bool = False,
|
||||
) -> list[Block]:
|
||||
if DISABLE_GENERATIVE_AI:
|
||||
return []
|
||||
@@ -255,10 +258,6 @@ def build_qa_response_blocks(
|
||||
)
|
||||
]
|
||||
|
||||
feedback_block = None
|
||||
if message_id is not None:
|
||||
feedback_block = build_qa_feedback_block(message_id=message_id)
|
||||
|
||||
response_blocks: list[Block] = [ai_answer_header]
|
||||
|
||||
if filter_block is not None:
|
||||
@@ -266,11 +265,45 @@ def build_qa_response_blocks(
|
||||
|
||||
response_blocks.append(answer_block)
|
||||
|
||||
if feedback_block is not None:
|
||||
response_blocks.append(feedback_block)
|
||||
if message_id is not None and not skip_ai_feedback:
|
||||
response_blocks.append(build_qa_feedback_block(message_id=message_id))
|
||||
|
||||
if not skip_quotes:
|
||||
response_blocks.extend(quotes_blocks)
|
||||
response_blocks.append(DividerBlock())
|
||||
|
||||
return response_blocks
|
||||
|
||||
|
||||
def build_follow_up_block(message_id: int | None) -> ActionsBlock:
|
||||
return ActionsBlock(
|
||||
block_id=build_feedback_id(message_id) if message_id is not None else None,
|
||||
elements=[
|
||||
ButtonElement(
|
||||
action_id=FOLLOWUP_BUTTON_ACTION_ID,
|
||||
style="danger",
|
||||
text="I need more help from a human!",
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def build_follow_up_resolved_blocks(tag_ids: list[str]) -> list[Block]:
|
||||
tag_str = " ".join([f"<@{tag}>" for tag in tag_ids])
|
||||
if tag_str:
|
||||
tag_str += " "
|
||||
text = (
|
||||
tag_str
|
||||
+ "Someone has requested more help.\n:point_down:Please mark this resolved after answering!"
|
||||
)
|
||||
text_block = SectionBlock(text=text)
|
||||
button_block = ActionsBlock(
|
||||
elements=[
|
||||
ButtonElement(
|
||||
action_id=FOLLOWUP_BUTTON_RESOLVED_ACTION_ID,
|
||||
style="primary",
|
||||
text="Mark Resolved",
|
||||
)
|
||||
]
|
||||
)
|
||||
return [text_block, button_block]
|
||||
|
@@ -1,5 +1,7 @@
|
||||
LIKE_BLOCK_ACTION_ID = "feedback-like"
|
||||
DISLIKE_BLOCK_ACTION_ID = "feedback-dislike"
|
||||
FEEDBACK_DOC_BUTTON_BLOCK_ACTION_ID = "feedback-doc-button"
|
||||
FOLLOWUP_BUTTON_ACTION_ID = "followup-button"
|
||||
FOLLOWUP_BUTTON_RESOLVED_ACTION_ID = "followup-resolved-button"
|
||||
SLACK_CHANNEL_ID = "channel_id"
|
||||
VIEW_DOC_FEEDBACK_ID = "view-doc-feedback"
|
||||
|
@@ -1,3 +1,6 @@
|
||||
from typing import Any
|
||||
from typing import cast
|
||||
|
||||
from slack_sdk import WebClient
|
||||
from slack_sdk.models.views import View
|
||||
from slack_sdk.socket_mode import SocketModeClient
|
||||
@@ -5,12 +8,19 @@ from slack_sdk.socket_mode.request import SocketModeRequest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from danswer.configs.constants import SearchFeedbackType
|
||||
from danswer.configs.danswerbot_configs import DANSWER_FOLLOWUP_EMOJI
|
||||
from danswer.danswerbot.slack.blocks import build_follow_up_resolved_blocks
|
||||
from danswer.danswerbot.slack.blocks import get_document_feedback_blocks
|
||||
from danswer.danswerbot.slack.config import get_slack_bot_config_for_channel
|
||||
from danswer.danswerbot.slack.constants import DISLIKE_BLOCK_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import LIKE_BLOCK_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import VIEW_DOC_FEEDBACK_ID
|
||||
from danswer.danswerbot.slack.utils import build_feedback_id
|
||||
from danswer.danswerbot.slack.utils import decompose_feedback_id
|
||||
from danswer.danswerbot.slack.utils import decompose_action_id
|
||||
from danswer.danswerbot.slack.utils import fetch_userids_from_emails
|
||||
from danswer.danswerbot.slack.utils import get_channel_name_from_id
|
||||
from danswer.danswerbot.slack.utils import respond_in_thread
|
||||
from danswer.danswerbot.slack.utils import update_emote_react
|
||||
from danswer.db.engine import get_sqlalchemy_engine
|
||||
from danswer.db.feedback import create_chat_message_feedback
|
||||
from danswer.db.feedback import create_doc_retrieval_feedback
|
||||
@@ -30,7 +40,7 @@ def handle_doc_feedback_button(
|
||||
|
||||
# Extracts the feedback_id coming from the 'source feedback' button
|
||||
# and generates a new one for the View, to keep track of the doc info
|
||||
query_event_id, doc_id, doc_rank = decompose_feedback_id(actions[0].get("value"))
|
||||
query_event_id, doc_id, doc_rank = decompose_action_id(actions[0].get("value"))
|
||||
external_id = build_feedback_id(query_event_id, doc_id, doc_rank)
|
||||
|
||||
channel_id = req.payload["container"]["channel_id"]
|
||||
@@ -63,7 +73,7 @@ def handle_slack_feedback(
|
||||
) -> None:
|
||||
engine = get_sqlalchemy_engine()
|
||||
|
||||
message_id, doc_id, doc_rank = decompose_feedback_id(feedback_id)
|
||||
message_id, doc_id, doc_rank = decompose_action_id(feedback_id)
|
||||
|
||||
with Session(engine) as db_session:
|
||||
if feedback_type in [LIKE_BLOCK_ACTION_ID, DISLIKE_BLOCK_ACTION_ID]:
|
||||
@@ -108,3 +118,76 @@ def handle_slack_feedback(
|
||||
thread_ts=thread_ts_to_post_confirmation,
|
||||
text="Thanks for your feedback!",
|
||||
)
|
||||
|
||||
|
||||
def handle_followup_button(
|
||||
req: SocketModeRequest,
|
||||
client: SocketModeClient,
|
||||
) -> None:
|
||||
action_id = None
|
||||
if actions := req.payload.get("actions"):
|
||||
action = cast(dict[str, Any], actions[0])
|
||||
action_id = cast(str, action.get("block_id"))
|
||||
|
||||
channel_id = req.payload["container"]["channel_id"]
|
||||
thread_ts = req.payload["container"]["thread_ts"]
|
||||
|
||||
update_emote_react(
|
||||
emoji=DANSWER_FOLLOWUP_EMOJI,
|
||||
channel=channel_id,
|
||||
message_ts=thread_ts,
|
||||
remove=False,
|
||||
client=client.web_client,
|
||||
)
|
||||
|
||||
tag_ids = []
|
||||
with Session(get_sqlalchemy_engine()) as db_session:
|
||||
channel_name, is_dm = get_channel_name_from_id(
|
||||
client=client.web_client, channel_id=channel_id
|
||||
)
|
||||
slack_bot_config = get_slack_bot_config_for_channel(
|
||||
channel_name=channel_name, db_session=db_session
|
||||
)
|
||||
if slack_bot_config:
|
||||
emails = slack_bot_config.channel_config.get("follow_up_tags")
|
||||
if emails:
|
||||
tag_ids = fetch_userids_from_emails(emails, client.web_client)
|
||||
|
||||
blocks = build_follow_up_resolved_blocks(tag_ids=tag_ids)
|
||||
|
||||
respond_in_thread(
|
||||
client=client.web_client,
|
||||
channel=channel_id,
|
||||
text="Received your request for more help",
|
||||
blocks=blocks,
|
||||
thread_ts=thread_ts,
|
||||
unfurl=False,
|
||||
)
|
||||
|
||||
if action_id is not None:
|
||||
message_id, _, _ = decompose_action_id(action_id)
|
||||
|
||||
create_chat_message_feedback(
|
||||
is_positive=None,
|
||||
feedback_text="",
|
||||
chat_message_id=message_id,
|
||||
user_id=None, # no "user" for Slack bot for now
|
||||
db_session=db_session,
|
||||
required_followup=True,
|
||||
)
|
||||
|
||||
|
||||
def handle_followup_resolved_button(
|
||||
req: SocketModeRequest,
|
||||
client: SocketModeClient,
|
||||
) -> None:
|
||||
channel_id = req.payload["container"]["channel_id"]
|
||||
thread_ts = req.payload["container"]["thread_ts"]
|
||||
|
||||
update_emote_react(
|
||||
emoji=DANSWER_FOLLOWUP_EMOJI,
|
||||
channel=channel_id,
|
||||
message_ts=thread_ts,
|
||||
remove=True,
|
||||
client=client.web_client,
|
||||
)
|
@@ -14,8 +14,8 @@ from danswer.configs.danswerbot_configs import DANSWER_BOT_NUM_RETRIES
|
||||
from danswer.configs.danswerbot_configs import DANSWER_REACT_EMOJI
|
||||
from danswer.configs.danswerbot_configs import DISABLE_DANSWER_BOT_FILTER_DETECT
|
||||
from danswer.configs.danswerbot_configs import ENABLE_DANSWERBOT_REFLEXION
|
||||
from danswer.connectors.slack.utils import make_slack_api_rate_limited
|
||||
from danswer.danswerbot.slack.blocks import build_documents_blocks
|
||||
from danswer.danswerbot.slack.blocks import build_follow_up_block
|
||||
from danswer.danswerbot.slack.blocks import build_qa_response_blocks
|
||||
from danswer.danswerbot.slack.blocks import get_restate_blocks
|
||||
from danswer.danswerbot.slack.constants import SLACK_CHANNEL_ID
|
||||
@@ -23,6 +23,7 @@ from danswer.danswerbot.slack.models import SlackMessageInfo
|
||||
from danswer.danswerbot.slack.utils import ChannelIdAdapter
|
||||
from danswer.danswerbot.slack.utils import fetch_userids_from_emails
|
||||
from danswer.danswerbot.slack.utils import respond_in_thread
|
||||
from danswer.danswerbot.slack.utils import update_emote_react
|
||||
from danswer.db.engine import get_sqlalchemy_engine
|
||||
from danswer.db.models import SlackBotConfig
|
||||
from danswer.one_shot_answer.answer_question import get_search_answer
|
||||
@@ -49,23 +50,12 @@ def send_msg_ack_to_user(details: SlackMessageInfo, client: WebClient) -> None:
|
||||
)
|
||||
return
|
||||
|
||||
slack_call = make_slack_api_rate_limited(client.reactions_add)
|
||||
slack_call(
|
||||
name=DANSWER_REACT_EMOJI,
|
||||
update_emote_react(
|
||||
emoji=DANSWER_REACT_EMOJI,
|
||||
channel=details.channel_to_respond,
|
||||
timestamp=details.msg_to_respond,
|
||||
)
|
||||
|
||||
|
||||
def remove_react(details: SlackMessageInfo, client: WebClient) -> None:
|
||||
if details.is_bot_msg:
|
||||
return
|
||||
|
||||
slack_call = make_slack_api_rate_limited(client.reactions_remove)
|
||||
slack_call(
|
||||
name=DANSWER_REACT_EMOJI,
|
||||
channel=details.channel_to_respond,
|
||||
timestamp=details.msg_to_respond,
|
||||
message_ts=details.msg_to_respond,
|
||||
remove=False,
|
||||
client=client,
|
||||
)
|
||||
|
||||
|
||||
@@ -130,6 +120,7 @@ def handle_message(
|
||||
# with non-public document sets
|
||||
bypass_acl = True
|
||||
|
||||
channel_conf = None
|
||||
if channel_config and channel_config.channel_config:
|
||||
channel_conf = channel_config.channel_config
|
||||
if not bypass_filters and "answer_filters" in channel_conf:
|
||||
@@ -265,7 +256,13 @@ def handle_message(
|
||||
|
||||
# In case of failures, don't keep the reaction there permanently
|
||||
try:
|
||||
remove_react(message_info, client)
|
||||
update_emote_react(
|
||||
emoji=DANSWER_REACT_EMOJI,
|
||||
channel=message_info.channel_to_respond,
|
||||
message_ts=message_info.msg_to_respond,
|
||||
remove=True,
|
||||
client=client,
|
||||
)
|
||||
except SlackApiError as e:
|
||||
logger.error(f"Failed to remove Reaction due to: {e}")
|
||||
|
||||
@@ -273,7 +270,13 @@ def handle_message(
|
||||
|
||||
# Got an answer at this point, can remove reaction and give results
|
||||
try:
|
||||
remove_react(message_info, client)
|
||||
update_emote_react(
|
||||
emoji=DANSWER_REACT_EMOJI,
|
||||
channel=message_info.channel_to_respond,
|
||||
message_ts=message_info.msg_to_respond,
|
||||
remove=True,
|
||||
client=client,
|
||||
)
|
||||
except SlackApiError as e:
|
||||
logger.error(f"Failed to remove Reaction due to: {e}")
|
||||
|
||||
@@ -343,13 +346,18 @@ def handle_message(
|
||||
else []
|
||||
)
|
||||
|
||||
all_blocks = restate_question_block + answer_blocks + document_blocks
|
||||
|
||||
if channel_conf and channel_conf.get("follow_up_tags") is not None:
|
||||
all_blocks.append(build_follow_up_block(message_id=answer.chat_message_id))
|
||||
|
||||
try:
|
||||
respond_in_thread(
|
||||
client=client,
|
||||
channel=channel,
|
||||
receiver_ids=send_to,
|
||||
text="Hello! Danswer has some results for you!",
|
||||
blocks=restate_question_block + answer_blocks + document_blocks,
|
||||
blocks=all_blocks,
|
||||
thread_ts=message_ts_to_respond_to,
|
||||
# don't unfurl, since otherwise we will have 5+ previews which makes the message very long
|
||||
unfurl=False,
|
||||
|
@@ -16,19 +16,24 @@ from danswer.configs.model_configs import ENABLE_RERANKING_ASYNC_FLOW
|
||||
from danswer.danswerbot.slack.config import get_slack_bot_config_for_channel
|
||||
from danswer.danswerbot.slack.constants import DISLIKE_BLOCK_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import FEEDBACK_DOC_BUTTON_BLOCK_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import FOLLOWUP_BUTTON_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import FOLLOWUP_BUTTON_RESOLVED_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import LIKE_BLOCK_ACTION_ID
|
||||
from danswer.danswerbot.slack.constants import SLACK_CHANNEL_ID
|
||||
from danswer.danswerbot.slack.constants import VIEW_DOC_FEEDBACK_ID
|
||||
from danswer.danswerbot.slack.handlers.handle_feedback import handle_doc_feedback_button
|
||||
from danswer.danswerbot.slack.handlers.handle_feedback import handle_slack_feedback
|
||||
from danswer.danswerbot.slack.handlers.handle_buttons import handle_doc_feedback_button
|
||||
from danswer.danswerbot.slack.handlers.handle_buttons import handle_followup_button
|
||||
from danswer.danswerbot.slack.handlers.handle_buttons import (
|
||||
handle_followup_resolved_button,
|
||||
)
|
||||
from danswer.danswerbot.slack.handlers.handle_buttons import handle_slack_feedback
|
||||
from danswer.danswerbot.slack.handlers.handle_message import handle_message
|
||||
from danswer.danswerbot.slack.models import SlackMessageInfo
|
||||
from danswer.danswerbot.slack.tokens import fetch_tokens
|
||||
from danswer.danswerbot.slack.utils import ChannelIdAdapter
|
||||
from danswer.danswerbot.slack.utils import decompose_feedback_id
|
||||
from danswer.danswerbot.slack.utils import decompose_action_id
|
||||
from danswer.danswerbot.slack.utils import get_channel_name_from_id
|
||||
from danswer.danswerbot.slack.utils import get_danswer_bot_app_id
|
||||
from danswer.danswerbot.slack.utils import get_view_values
|
||||
from danswer.danswerbot.slack.utils import read_slack_thread
|
||||
from danswer.danswerbot.slack.utils import remove_danswer_bot_tag
|
||||
from danswer.danswerbot.slack.utils import respond_in_thread
|
||||
@@ -136,27 +141,14 @@ def prefilter_requests(req: SocketModeRequest, client: SocketModeClient) -> bool
|
||||
|
||||
|
||||
def process_feedback(req: SocketModeRequest, client: SocketModeClient) -> None:
|
||||
# Answer feedback
|
||||
if actions := req.payload.get("actions"):
|
||||
action = cast(dict[str, Any], actions[0])
|
||||
feedback_type = cast(str, action.get("action_id"))
|
||||
feedback_id = cast(str, action.get("block_id"))
|
||||
channel_id = cast(str, req.payload["container"]["channel_id"])
|
||||
thread_ts = cast(str, req.payload["container"]["thread_ts"])
|
||||
# Doc feedback
|
||||
elif view := req.payload.get("view"):
|
||||
view_values = get_view_values(view["state"]["values"])
|
||||
private_metadata = view.get("private_metadata").split("_")
|
||||
if not view_values:
|
||||
logger.error("Unable to process feedback. Missing view values")
|
||||
return
|
||||
|
||||
feedback_type = [x for x in view_values.values()][0]
|
||||
feedback_id = cast(str, view.get("external_id"))
|
||||
channel_id = private_metadata[0]
|
||||
thread_ts = private_metadata[1]
|
||||
else:
|
||||
logger.error("Unable to process feedback. Actions or View not found")
|
||||
logger.error("Unable to process feedback. Action not found")
|
||||
return
|
||||
|
||||
user_id = cast(str, req.payload["user"]["id"])
|
||||
@@ -170,7 +162,7 @@ def process_feedback(req: SocketModeRequest, client: SocketModeClient) -> None:
|
||||
thread_ts_to_post_confirmation=thread_ts,
|
||||
)
|
||||
|
||||
query_event_id, _, _ = decompose_feedback_id(feedback_id)
|
||||
query_event_id, _, _ = decompose_action_id(feedback_id)
|
||||
logger.info(f"Successfully handled QA feedback for event: {query_event_id}")
|
||||
|
||||
|
||||
@@ -304,6 +296,10 @@ def action_routing(req: SocketModeRequest, client: SocketModeClient) -> None:
|
||||
elif action["action_id"] == FEEDBACK_DOC_BUTTON_BLOCK_ACTION_ID:
|
||||
# Activation of the "source feedback" button
|
||||
return handle_doc_feedback_button(req, client)
|
||||
elif action["action_id"] == FOLLOWUP_BUTTON_ACTION_ID:
|
||||
return handle_followup_button(req, client)
|
||||
elif action["action_id"] == FOLLOWUP_BUTTON_RESOLVED_ACTION_ID:
|
||||
return handle_followup_resolved_button(req, client)
|
||||
|
||||
|
||||
def view_routing(req: SocketModeRequest, client: SocketModeClient) -> None:
|
||||
|
@@ -29,6 +29,26 @@ logger = setup_logger()
|
||||
DANSWER_BOT_APP_ID: str | None = None
|
||||
|
||||
|
||||
def update_emote_react(
|
||||
emoji: str,
|
||||
channel: str,
|
||||
message_ts: str | None,
|
||||
remove: bool,
|
||||
client: WebClient,
|
||||
) -> None:
|
||||
if not message_ts:
|
||||
logger.error(f"Tried to remove a react in {channel} but no message specified")
|
||||
return
|
||||
|
||||
func = client.reactions_remove if remove else client.reactions_add
|
||||
slack_call = make_slack_api_rate_limited(func) # type: ignore
|
||||
slack_call(
|
||||
name=emoji,
|
||||
channel=channel,
|
||||
timestamp=message_ts,
|
||||
)
|
||||
|
||||
|
||||
def get_danswer_bot_app_id(web_client: WebClient) -> Any:
|
||||
global DANSWER_BOT_APP_ID
|
||||
if DANSWER_BOT_APP_ID is None:
|
||||
@@ -134,7 +154,7 @@ def build_feedback_id(
|
||||
return unique_prefix + ID_SEPARATOR + feedback_id
|
||||
|
||||
|
||||
def decompose_feedback_id(feedback_id: str) -> tuple[int, str | None, int | None]:
|
||||
def decompose_action_id(feedback_id: str) -> tuple[int, str | None, int | None]:
|
||||
"""Decompose into query_id, document_id, document_rank, see above function"""
|
||||
try:
|
||||
components = feedback_id.split(ID_SEPARATOR)
|
||||
|
@@ -146,8 +146,10 @@ def create_chat_message_feedback(
|
||||
chat_message_id: int,
|
||||
user_id: UUID | None,
|
||||
db_session: Session,
|
||||
# Slack user requested help from human
|
||||
required_followup: bool | None = None,
|
||||
) -> None:
|
||||
if is_positive is None and feedback_text is None:
|
||||
if is_positive is None and feedback_text is None and required_followup is None:
|
||||
raise ValueError("No feedback provided")
|
||||
|
||||
chat_message = get_chat_message(
|
||||
@@ -161,6 +163,7 @@ def create_chat_message_feedback(
|
||||
chat_message_id=chat_message_id,
|
||||
is_positive=is_positive,
|
||||
feedback_text=feedback_text,
|
||||
required_followup=required_followup,
|
||||
)
|
||||
|
||||
db_session.add(message_feedback)
|
||||
|
@@ -602,6 +602,7 @@ class ChatMessageFeedback(Base):
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
chat_message_id: Mapped[int] = mapped_column(ForeignKey("chat_message.id"))
|
||||
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)
|
||||
|
||||
chat_message: Mapped[ChatMessage] = relationship(
|
||||
@@ -740,6 +741,9 @@ class ChannelConfig(TypedDict):
|
||||
respond_tag_only: NotRequired[bool] # defaults to False
|
||||
respond_team_member_list: NotRequired[list[str]]
|
||||
answer_filters: NotRequired[list[AllowedAnswerFilters]]
|
||||
# If None then no follow up
|
||||
# If empty list, follow up with no tags
|
||||
follow_up_tags: NotRequired[list[str]]
|
||||
|
||||
|
||||
class SlackBotConfig(Base):
|
||||
|
@@ -78,6 +78,8 @@ class SlackBotConfigCreationRequest(BaseModel):
|
||||
# If no team members, assume respond in the channel to everyone
|
||||
respond_team_member_list: list[str] = []
|
||||
answer_filters: list[AllowedAnswerFilters] = []
|
||||
# list of user emails
|
||||
follow_up_tags: list[str] | None = None
|
||||
|
||||
@validator("answer_filters", pre=True)
|
||||
def validate_filters(cls, value: list[str]) -> list[str]:
|
||||
|
@@ -39,6 +39,7 @@ def _form_channel_config(
|
||||
slack_bot_config_creation_request.respond_team_member_list
|
||||
)
|
||||
answer_filters = slack_bot_config_creation_request.answer_filters
|
||||
follow_up_tags = slack_bot_config_creation_request.follow_up_tags
|
||||
|
||||
if not raw_channel_names:
|
||||
raise HTTPException(
|
||||
@@ -73,6 +74,8 @@ def _form_channel_config(
|
||||
channel_config["respond_team_member_list"] = respond_team_member_list
|
||||
if answer_filters:
|
||||
channel_config["answer_filters"] = answer_filters
|
||||
if follow_up_tags is not None:
|
||||
channel_config["follow_up_tags"] = follow_up_tags
|
||||
|
||||
return channel_config
|
||||
|
||||
|
Reference in New Issue
Block a user