Skip to content

Commit

Permalink
fully functional
Browse files Browse the repository at this point in the history
  • Loading branch information
pablonyx committed Feb 4, 2025
1 parent 33ba2ee commit 64540d0
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 66 deletions.
13 changes: 13 additions & 0 deletions backend/onyx/db/persona.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
55 changes: 34 additions & 21 deletions backend/onyx/onyxbot/slack/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 []
Expand Down Expand Up @@ -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] = []

Expand Down Expand Up @@ -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.
Expand All @@ -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"):
Expand Down
50 changes: 28 additions & 22 deletions backend/onyx/onyxbot/slack/handlers/handle_regular_answer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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(
Expand All @@ -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
):
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const SlackChannelConfigCreationForm = ({
knowledge_source: existingSlackBotUsesPersona
? existingPersonaHasSearchTool
? "assistant"
: "nonsearch_assistant"
: "non_search_assistant"
: existingSlackChannelConfig?.persona
? "document_sets"
: "all_public",
Expand Down Expand Up @@ -167,7 +167,7 @@ export const SlackChannelConfigCreationForm = ({
"all_public",
"document_sets",
"assistant",
"nonsearch_assistant",
"non_search_assistant",
])
.required(),
})}
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
} from "formik";
import { CCPairDescriptor, DocumentSet } from "@/lib/types";
import {
BooleanFormField,
Label,
SelectorFormField,
SubLabel,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -254,8 +255,8 @@ export function SlackChannelConfigFormFields({
sublabel="Control both the documents and the prompt to use for answering questions"
/>
<RadioGroupItemField
value="nonsearch_assistant"
id="nonsearch_assistant"
value="non_search_assistant"
id="non_search_assistant"
label="Non-Search Assistant"
sublabel="Chat with an assistant that does not use documents"
/>
Expand Down Expand Up @@ -422,7 +423,7 @@ export function SlackChannelConfigFormFields({
)}
</div>
)}
{values.knowledge_source === "nonsearch_assistant" && (
{values.knowledge_source === "non_search_assistant" && (
<div className="mt-4">
<SubLabel>
<>
Expand Down Expand Up @@ -468,7 +469,7 @@ export function SlackChannelConfigFormFields({
</div>
<Separator className="my-4" />
<Accordion type="multiple" className=" gap-y-2 w-full">
{values.knowledge_source !== "nonsearch_assistant" && (
{values.knowledge_source !== "non_search_assistant" && (
<AccordionItem value="search-options">
<AccordionTrigger className="text-text">
Search Configuration
Expand All @@ -486,15 +487,14 @@ export function SlackChannelConfigFormFields({
]}
/>
</div>
<BooleanFormField
<CheckFormField
name="enable_auto_filters"
removeIndent
label="Enable LLM Autofiltering"
tooltip="If set, the LLM will generate source and time filters based on the user's query"
/>
<BooleanFormField

<CheckFormField
name="answer_validity_check_enabled"
removeIndent
label="Only respond if citations found"
tooltip="If set, will only answer questions where the model successfully produces citations"
/>
Expand All @@ -507,16 +507,14 @@ export function SlackChannelConfigFormFields({
<AccordionTrigger>General Configuration</AccordionTrigger>
<AccordionContent>
<div className="space-y-4">
<BooleanFormField
<CheckFormField
name="show_continue_in_web_ui"
removeIndent
label="Show Continue in Web UI button"
tooltip="If set, will show a button at the bottom of the response that allows the user to continue the conversation in the Onyx Web UI"
/>

<BooleanFormField
<CheckFormField
name="still_need_help_enabled"
removeIndent
onChange={(checked: boolean) => {
setFieldValue("still_need_help_enabled", checked);
if (!checked) {
Expand Down Expand Up @@ -546,21 +544,18 @@ export function SlackChannelConfigFormFields({
</CollapsibleSection>
)}

<BooleanFormField
<CheckFormField
name="questionmark_prefilter_enabled"
removeIndent
label="Only respond to questions"
tooltip="If set, OnyxBot will only respond to messages that contain a question mark"
/>
<BooleanFormField
<CheckFormField
name="respond_tag_only"
removeIndent
label="Respond to @OnyxBot Only"
tooltip="If set, OnyxBot will only respond when directly tagged"
/>
<BooleanFormField
<CheckFormField
name="respond_to_bots"
removeIndent
label="Respond to Bot messages"
tooltip="If not set, OnyxBot will always ignore messages from Bots"
/>
Expand Down
Loading

0 comments on commit 64540d0

Please sign in to comment.