File size: 9,278 Bytes
85cec0e
 
954241d
21ffffb
5058660
 
 
 
 
 
21ffffb
 
 
 
 
 
 
85cec0e
954241d
85cec0e
21ffffb
 
 
 
954241d
21ffffb
 
 
 
 
954241d
21ffffb
 
 
 
 
 
 
 
 
 
 
 
954241d
21ffffb
 
 
954241d
21ffffb
 
 
 
 
 
 
 
 
 
 
 
954241d
21ffffb
 
 
 
 
 
 
 
 
5058660
 
 
 
 
 
 
 
 
 
 
 
 
 
21ffffb
5058660
85cec0e
21ffffb
 
 
 
 
 
5058660
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85cec0e
21ffffb
 
 
 
 
85cec0e
21ffffb
 
 
 
 
 
 
 
 
 
85cec0e
21ffffb
 
954241d
21ffffb
85cec0e
 
21ffffb
 
 
85cec0e
 
 
 
 
 
5058660
 
 
85cec0e
21ffffb
954241d
21ffffb
 
 
85cec0e
 
 
5058660
 
21ffffb
 
954241d
 
 
21ffffb
 
954241d
21ffffb
954241d
21ffffb
 
954241d
 
 
21ffffb
 
954241d
 
5058660
 
21ffffb
5058660
 
21ffffb
 
5058660
 
 
21ffffb
 
 
 
 
5058660
 
21ffffb
5058660
 
 
21ffffb
 
 
 
 
5058660
 
21ffffb
 
 
5058660
21ffffb
5058660
85cec0e
 
 
 
 
 
 
21ffffb
954241d
 
21ffffb
954241d
 
 
 
21ffffb
954241d
 
 
 
21ffffb
954241d
 
 
21ffffb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
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%;
}
"""


# defautl query: how to make slingshot?
# who created light (e.g., electricity) Tesla or Edison in quick short?
with gr.Blocks(css=css, fill_height=True) as demo:
    # 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()