abdibrokhim's picture
updated
f19c6cd
import os
import gradio as gr
from bagoodex_client import BagoodexClient
from r_types import ChatMessage
from prompts import (
SYSTEM_PROMPT_FOLLOWUP,
SYSTEM_PROMPT_MAP,
SYSTEM_PROMPT_BASE,
SYSTEM_PROMPT_KNOWLEDGE_BASE
)
from helpers import (
embed_video,
format_links,
embed_google_map,
format_knowledge,
format_followup_questions
)
client = BagoodexClient()
# ----------------------------
# Chat & Follow-up Functions
# ----------------------------
def chat_function(message, history, followup_state, chat_history_state):
"""
Process a new user message.
Appends the message and response to the conversation,
and retrieves follow-up questions.
"""
# complete_chat returns a new followup id and answer
followup_id_new, answer = client.complete_chat(message)
# Update conversation history (if history is None, use an empty list)
if history is None:
history = []
updated_history = history + [ChatMessage({"role": "user", "content": message}),
ChatMessage({"role": "assistant", "content": answer})]
# Retrieve follow-up questions using the updated conversation
followup_questions_raw = client.base_qna(
messages=updated_history, system_prompt=SYSTEM_PROMPT_FOLLOWUP
)
# Format them using the helper
followup_md = format_followup_questions(followup_questions_raw)
return answer, followup_id_new, updated_history, followup_md
def handle_followup_click(question, followup_state, chat_history_state):
"""
When a follow-up question is clicked, send it as a new message.
"""
if not question:
return chat_history_state, followup_state, ""
# Process the follow-up question via complete_chat
followup_id_new, answer = client.complete_chat(question)
updated_history = chat_history_state + [ChatMessage({"role": "user", "content": question}),
ChatMessage({"role": "assistant", "content": answer})]
# Get new follow-up questions
followup_questions_raw = client.base_qna(
messages=updated_history, system_prompt=SYSTEM_PROMPT_FOLLOWUP
)
followup_md = format_followup_questions(followup_questions_raw)
return updated_history, followup_id_new, followup_md
def handle_local_map_click(followup_state, chat_history_state):
"""
On local map click, try to get a local map.
If issues occur, fall back to using the SYSTEM_PROMPT_MAP.
"""
if not followup_state:
return chat_history_state
try:
result = client.get_local_map(followup_state)
if result:
map_url = result.get('link', '')
# Use helper to produce an embedded map iframe
html = embed_google_map(map_url)
# Fall back: use the base_qna call with SYSTEM_PROMPT_MAP
result = client.base_qna(
messages=chat_history_state, system_prompt=SYSTEM_PROMPT_MAP
)
# Assume result contains a 'link' field
html = embed_google_map(result.get('link', ''))
new_message = ChatMessage({"role": "assistant", "content": html})
return chat_history_state + [new_message]
except Exception:
return chat_history_state
def handle_knowledge_click(followup_state, chat_history_state):
"""
On knowledge base click, fetch and format knowledge content.
"""
if not followup_state:
return chat_history_state
try:
print('trying to get knowledge')
result = client.get_knowledge(followup_state)
knowledge_md = format_knowledge(result)
if knowledge_md == 0000:
print('falling back to base_qna')
# Fall back: use the base_qna call with SYSTEM_PROMPT_KNOWLEDGE_BASE
result = client.base_qna(
messages=chat_history_state, system_prompt=SYSTEM_PROMPT_KNOWLEDGE_BASE
)
knowledge_md = format_knowledge(result)
new_message = ChatMessage({"role": "assistant", "content": knowledge_md})
return chat_history_state + [new_message]
except Exception:
return chat_history_state
# ----------------------------
# Advanced Search Functions
# ----------------------------
def perform_image_search(followup_state):
if not followup_state:
return []
result = client.get_images(followup_state)
# For images we simply return a list of original URLs
return [item.get("original", "") for item in result]
def perform_video_search(followup_state):
if not followup_state:
return "<p>No followup ID available.</p>"
result = client.get_videos(followup_state)
# Use the helper to produce the embed iframes (supports multiple videos)
return embed_video(result)
def perform_links_search(followup_state):
if not followup_state:
return gr.Markdown("No followup ID available.")
result = client.get_links(followup_state)
return format_links(result)
# ----------------------------
# UI Build
# ----------------------------
css = """
#chatbot {
height: 100%;
}
h1, h2, h3, h4, h5, h6 {
text-align: center;
display: block;
}
"""
# defautl query: how to make slingshot?
# who created light (e.g., electricity) Tesla or Edison in quick short?
with gr.Blocks() as demo:
gr.HTML("""
<div style="text-align: center; font-size: 32px; font-weight: bold; margin-bottom: 20px;">
Llike perplexity, but more precise.
</div>
<div style="text-align: center; display: flex; justify-content: center; align-items: center; margin-top: 1em; margin-bottom: .5em;">
<span>Built by</span>
<a href="https://yaps.gg" target="_blank" style="display: flex; align-items: center; margin-right: .75em;">
<img src="https://huggingface.co/spaces/AimlAPI/Bagoodex-Web-Search/resolve/main/assets/new-profile-photo%202.jpeg" width="40" style="margin-left: .75em; margin-right: .75em; background-color: white; border-radius: 10px; padding: 1px; margin-right: 1px;"/>
<span style="margin-left: .5em;">@abdibrokhim</span>
</a>
</div>
<div style="text-align: center;display: flex;justify-content: center;align-items: center;margin-top: 1em;margin-bottom: .5em;">
<a href="https://aimlapi.com/models/bagoodex-search-v1-api/?utm_source=bagoodex&utm_medium=huggingface&utm_campaign=tutorials">
<img src="https://huggingface.co/spaces/AimlAPI/Bagoodex-Web-Search/resolve/main/assets/powered%20by%20aimlapi.svg" width="160" style="margin-left: .75em; margin-right: .75em; background-color: white; border-radius: 10px; padding: 5px;"/>
</a>
</div>
""")
# State variables to hold followup ID and conversation history, plus follow-up questions text
followup_state = gr.State(None)
chat_history_state = gr.State([]) # holds conversation history as a list of messages
followup_md_state = gr.State("") # holds follow-up questions as Markdown text
with gr.Row():
with gr.Column(scale=3):
with gr.Row():
btn_local_map = gr.Button("Local Map Search (coming soon...)", variant="secondary", size="sm", interactive=False)
btn_knowledge = gr.Button("Knowledge Base (coming soon...)", variant="secondary", size="sm", interactive=False)
# The ChatInterface now uses additional outputs for both followup_state and conversation history,
# plus follow-up questions Markdown.
chat = gr.ChatInterface(
fn=chat_function,
type="messages",
additional_inputs=[followup_state, chat_history_state],
additional_outputs=[followup_state, chat_history_state, followup_md_state],
)
# Button callbacks to append local map and knowledge base results to chat
btn_local_map.click(
fn=handle_local_map_click,
inputs=[followup_state, chat_history_state],
outputs=chat.chatbot
)
btn_knowledge.click(
fn=handle_knowledge_click,
inputs=[followup_state, chat_history_state],
outputs=chat.chatbot
)
# Radio-based follow-up questions
followup_radio = gr.Radio(
choices=[],
label="Follow-up Questions (select one and click 'Send Follow-up')"
)
btn_send_followup = gr.Button("Send Follow-up")
# When the user clicks "Send Follow-up", the selected question is passed
# to handle_followup_click
btn_send_followup.click(
fn=handle_followup_click,
inputs=[followup_radio, followup_state, chat_history_state],
outputs=[chat.chatbot, followup_state, followup_md_state]
)
# Update the radio choices when followup_md_state changes
def update_followup_radio(md_text):
"""
Parse Markdown lines to extract questions starting with '- '.
"""
lines = md_text.splitlines()
questions = []
for line in lines:
if line.startswith("- "):
questions.append(line[2:])
return gr.update(choices=questions, value=None)
followup_md_state.change(
fn=update_followup_radio,
inputs=[followup_md_state],
outputs=[followup_radio]
)
with gr.Column(scale=1):
gr.Markdown("### Advanced Search Options")
with gr.Column(variant="panel"):
btn_images = gr.Button("Search Images")
btn_videos = gr.Button("Search Videos")
btn_links = gr.Button("Search Links")
gallery_output = gr.Gallery(label="Image Results", columns=2)
video_output = gr.HTML(label="Video Results") # HTML for embedded video iframes
links_output = gr.Markdown(label="Links Results")
btn_images.click(
fn=perform_image_search,
inputs=[followup_state],
outputs=[gallery_output]
)
btn_videos.click(
fn=perform_video_search,
inputs=[followup_state],
outputs=[video_output]
)
btn_links.click(
fn=perform_links_search,
inputs=[followup_state],
outputs=[links_output]
)
demo.launch()