diff --git a/backend/onyx/db/persona.py b/backend/onyx/db/persona.py
index 94a60e7b5cd..638ee74a6ef 100644
--- a/backend/onyx/db/persona.py
+++ b/backend/onyx/db/persona.py
@@ -11,6 +11,7 @@
from sqlalchemy import select
from sqlalchemy import update
from sqlalchemy.orm import aliased
+from sqlalchemy.orm import joinedload
from sqlalchemy.orm import selectinload
from sqlalchemy.orm import Session
@@ -708,3 +709,15 @@ def update_persona_label(
def delete_persona_label(label_id: int, db_session: Session) -> None:
db_session.query(PersonaLabel).filter(PersonaLabel.id == label_id).delete()
db_session.commit()
+
+
+def persona_has_search_tool(persona_id: int, db_session: Session) -> bool:
+ persona = (
+ db_session.query(Persona)
+ .options(joinedload(Persona.tools))
+ .filter(Persona.id == persona_id)
+ .one_or_none()
+ )
+ if persona is None:
+ raise ValueError(f"Persona with ID {persona_id} does not exist")
+ return any(tool.in_code_tool_id == "run_search" for tool in persona.tools)
diff --git a/backend/onyx/onyxbot/slack/blocks.py b/backend/onyx/onyxbot/slack/blocks.py
index 73b8a098d6f..96c2aa1a440 100644
--- a/backend/onyx/onyxbot/slack/blocks.py
+++ b/backend/onyx/onyxbot/slack/blocks.py
@@ -338,13 +338,30 @@ def _build_citations_blocks(
return citations_block
+def _build_answer_blocks(
+ answer: ChatOnyxBotResponse, fallback_answer: str
+) -> list[Block]:
+ if not answer.answer:
+ answer_blocks = [SectionBlock(text=fallback_answer)]
+ else:
+ # replaces markdown links with slack format links
+ formatted_answer = format_slack_message(answer.answer)
+ answer_processed = decode_escapes(
+ remove_slack_text_interactions(formatted_answer)
+ )
+ answer_blocks = [
+ SectionBlock(text=text) for text in _split_text(answer_processed)
+ ]
+ return answer_blocks
+
+
def _build_qa_response_blocks(
answer: ChatOnyxBotResponse,
) -> list[Block]:
retrieval_info = answer.docs
- # if not retrieval_info:
- # This should not happen, even with no docs retrieved, there is still info returned
- # raise RuntimeError("Failed to retrieve docs, cannot answer question.")
+ if not retrieval_info:
+ # This should not happen, even with no docs retrieved, there is still info returned
+ raise RuntimeError("Failed to retrieve docs, cannot answer question.")
if DISABLE_GENERATIVE_AI:
return []
@@ -376,21 +393,10 @@ def _build_qa_response_blocks(
filter_block = SectionBlock(text=f"_{filter_text}_")
- if not answer.answer:
- answer_blocks = [
- SectionBlock(
- text="Sorry, I was unable to find an answer, but I did find some potentially relevant docs 🤓"
- )
- ]
- else:
- # replaces markdown links with slack format links
- formatted_answer = format_slack_message(answer.answer)
- answer_processed = decode_escapes(
- remove_slack_text_interactions(formatted_answer)
- )
- answer_blocks = [
- SectionBlock(text=text) for text in _split_text(answer_processed)
- ]
+ answer_blocks = _build_answer_blocks(
+ answer=answer,
+ fallback_answer="Sorry, I was unable to find an answer, but I did find some potentially relevant docs 🤓",
+ )
response_blocks: list[Block] = []
@@ -481,6 +487,7 @@ def build_slack_response_blocks(
use_citations: bool,
feedback_reminder_id: str | None,
skip_ai_feedback: bool = False,
+ expecting_search_result: bool = False,
) -> list[Block]:
"""
This function is a top level function that builds all the blocks for the Slack response.
@@ -491,9 +498,15 @@ def build_slack_response_blocks(
message_info.thread_messages[-1].message, message_info.is_bot_msg
)
- answer_blocks = _build_qa_response_blocks(
- answer=answer,
- )
+ if expecting_search_result:
+ answer_blocks = _build_qa_response_blocks(
+ answer=answer,
+ )
+
+ else:
+ answer_blocks = _build_answer_blocks(
+ answer=answer, fallback_answer="Sorry, I was unable to generate an answer."
+ )
web_follow_up_block = []
if channel_conf and channel_conf.get("show_continue_in_web_ui"):
diff --git a/backend/onyx/onyxbot/slack/handlers/handle_regular_answer.py b/backend/onyx/onyxbot/slack/handlers/handle_regular_answer.py
index c359b3fe0e9..4479bbcd536 100644
--- a/backend/onyx/onyxbot/slack/handlers/handle_regular_answer.py
+++ b/backend/onyx/onyxbot/slack/handlers/handle_regular_answer.py
@@ -27,6 +27,7 @@
from onyx.db.models import SlackChannelConfig
from onyx.db.models import User
from onyx.db.persona import get_persona_by_id
+from onyx.db.persona import persona_has_search_tool
from onyx.db.users import get_user_by_email
from onyx.onyxbot.slack.blocks import build_slack_response_blocks
from onyx.onyxbot.slack.handlers.utils import send_team_member_message
@@ -106,6 +107,9 @@ def handle_regular_answer(
]
prompt = persona.prompts[0] if persona.prompts else None
+ with get_session_with_tenant(tenant_id) as db_session:
+ expecting_search_result = persona_has_search_tool(persona.id, db_session)
+
# TODO: Add in support for Slack to truncate messages based on max LLM context
# llm, _ = get_llms_for_persona(persona)
@@ -300,27 +304,27 @@ def _get_slack_answer(
logger.debug(answer.answer)
return True
- answer.docs
- # if not retrieval_info:
- # This should not happen, even with no docs retrieved, there is still info returned
- # raise RuntimeError("Failed to retrieve docs, cannot answer question.")
-
- # top_docs = retrieval_info.top_documents
- # if not top_docs and not should_respond_even_with_no_docs:
- # logger.error(
- # f"Unable to answer question: '{user_message}' - no documents found"
- # )
- # # Optionally, respond in thread with the error message
- # # Used primarily for debugging purposes
- # if should_respond_with_error_msgs:
- # respond_in_thread(
- # client=client,
- # channel=channel,
- # receiver_ids=None,
- # text="Found no documents when trying to answer. Did you index any documents?",
- # thread_ts=message_ts_to_respond_to,
- # )
- # return True
+ retrieval_info = answer.docs
+ if not retrieval_info and expecting_search_result:
+ # This should not happen, even with no docs retrieved, there is still info returned
+ raise RuntimeError("Failed to retrieve docs, cannot answer question.")
+
+ top_docs = retrieval_info.top_documents if retrieval_info else []
+ if not top_docs and expecting_search_result:
+ logger.error(
+ f"Unable to answer question: '{user_message}' - no documents found"
+ )
+ # Optionally, respond in thread with the error message
+ # Used primarily for debugging purposes
+ if should_respond_with_error_msgs:
+ respond_in_thread(
+ client=client,
+ channel=channel,
+ receiver_ids=None,
+ text="Found no documents when trying to answer. Did you index any documents?",
+ thread_ts=message_ts_to_respond_to,
+ )
+ return True
if not answer.answer and disable_docs_only_answer:
logger.notice(
@@ -335,7 +339,8 @@ def _get_slack_answer(
)
if (
- only_respond_if_citations
+ expecting_search_result
+ and only_respond_if_citations
and not answer.citations
and not message_info.bypass_filters
):
@@ -361,6 +366,7 @@ def _get_slack_answer(
channel_conf=channel_conf,
use_citations=True, # No longer supporting quotes
feedback_reminder_id=feedback_reminder_id,
+ expecting_search_result=expecting_search_result,
)
try:
diff --git a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx
index e01d13e3fb4..4415d9e4cea 100644
--- a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx
+++ b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx
@@ -120,7 +120,7 @@ export const SlackChannelConfigCreationForm = ({
knowledge_source: existingSlackBotUsesPersona
? existingPersonaHasSearchTool
? "assistant"
- : "nonsearch_assistant"
+ : "non_search_assistant"
: existingSlackChannelConfig?.persona
? "document_sets"
: "all_public",
@@ -167,7 +167,7 @@ export const SlackChannelConfigCreationForm = ({
"all_public",
"document_sets",
"assistant",
- "nonsearch_assistant",
+ "non_search_assistant",
])
.required(),
})}
@@ -181,14 +181,14 @@ export const SlackChannelConfigCreationForm = ({
respond_member_group_list: values.respond_member_group_list,
usePersona:
values.knowledge_source === "assistant" ||
- values.knowledge_source === "nonsearch_assistant",
+ values.knowledge_source === "non_search_assistant",
document_sets:
values.knowledge_source === "document_sets"
? values.document_sets
: [],
persona_id:
values.knowledge_source === "assistant" ||
- values.knowledge_source === "nonsearch_assistant"
+ values.knowledge_source === "non_search_assistant"
? values.persona_id
: null,
standard_answer_categories: values.standard_answer_categories.map(
diff --git a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx
index a883ee022e5..aa79e73d6fe 100644
--- a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx
+++ b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx
@@ -10,7 +10,6 @@ import {
} from "formik";
import { CCPairDescriptor, DocumentSet } from "@/lib/types";
import {
- BooleanFormField,
Label,
SelectorFormField,
SubLabel,
@@ -50,6 +49,8 @@ import {
} from "@/components/ui/accordion";
import { Separator } from "@/components/ui/separator";
+import { CheckFormField } from "@/components/ui/CheckField";
+
export interface SlackChannelConfigFormFieldsProps {
isUpdate: boolean;
isDefault: boolean;
@@ -254,8 +255,8 @@ export function SlackChannelConfigFormFields({
sublabel="Control both the documents and the prompt to use for answering questions"
/>
{tooltip}
+