File size: 6,908 Bytes
374588f
0a372e8
593a090
 
 
 
268baab
0a372e8
 
268baab
0a372e8
 
95cdb75
268baab
593a090
268baab
593a090
0a372e8
95cdb75
593a090
 
0a372e8
95cdb75
 
 
 
 
 
 
593a090
268baab
 
 
 
 
cdf68de
593a090
95cdb75
 
 
 
 
 
 
268baab
 
593a090
95cdb75
 
593a090
 
95cdb75
 
 
593a090
95cdb75
 
 
 
 
 
 
 
 
 
 
593a090
95cdb75
593a090
95cdb75
 
268baab
cdf68de
95cdb75
 
 
 
 
268baab
95cdb75
 
 
 
 
268baab
cdf68de
95cdb75
 
 
 
 
268baab
cdf68de
95cdb75
593a090
95cdb75
 
593a090
cdf68de
95cdb75
 
374588f
 
 
 
 
95cdb75
 
b482b16
593a090
 
0a372e8
 
b482b16
593a090
268baab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593a090
0a372e8
b482b16
 
 
 
 
 
0a372e8
593a090
0a372e8
 
374588f
 
 
 
 
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
import os
import gradio as gr
from src.const.agent_response_constants import *
from src.rag.agent_chain import ExecutiveAgentChain
from src.rag.utilclasses import LeadAgentQueryResponse
from src.utils.logging import get_logger
from src.cache.cache import Cache

logger = get_logger("chatbot_app")
cache_logger = get_logger("cache_chatbot_app")

class ChatbotApplication:
    def __init__(self, language: str = 'de') -> None:
        self._app = gr.Blocks()
        self._language = language
        self._cache = Cache.get_cache()

        with self._app:
            agent_state = gr.State(None)
            lang_state = gr.State(language)

            with gr.Row():
                lang_selector = gr.Radio(
                    choices=["Deutsch", "English"],
                    value="English" if language == 'en' else 'Deutsch',
                    label="Selected Language",
                    interactive=True,
                )
                reset_button = gr.Button("Reset Conversation")

            chatbot = gr.Chatbot(
                height=600,
                type='messages',
                label="Executive Education Adviser"
            )
            chat = gr.ChatInterface(
                fn=lambda msg, history, agent: self._chat(
                    message=msg,
                    history=history,
                    agent=agent,
                ),
                additional_inputs=[agent_state],
                title="Executive Education Adviser",
                type='messages',
                chatbot=chatbot,
                fill_height=True
            )

            def clear_chat_immediate():
                return [], ""

            def on_lang_change(language):
                lang_code = 'en' if language == 'English' else 'de'
                return switch_language(lang_code)

            def initalize_agent(language):
                agent = ExecutiveAgentChain(language=language)
                greeting = agent.generate_greeting()
                return agent, [{"role": "assistant", "content": greeting}]

            def switch_language(new_language):
                new_agent, greeting = initalize_agent(new_language)
                return (
                    new_agent,
                    new_language,
                    greeting,
                    ""
                )

            lang_selector.change(
                fn=clear_chat_immediate,
                outputs=[chat.chatbot_value],
                queue=True,
            )

            lang_selector.change(
                fn=on_lang_change,
                inputs=[lang_selector],
                outputs=[agent_state, lang_state, chat.chatbot_value],
                queue=True,
            )

            reset_button.click(
                fn=clear_chat_immediate,
                outputs=[chat.chatbot_value],
                queue=True,
            )

            reset_button.click(
                fn=switch_language,
                inputs=[lang_state],
                outputs=[agent_state, lang_state, chat.chatbot_value],
                queue=True,
            )

            # Initialize the agent chain on the app startup
            self._app.load(
                fn=lambda: initalize_agent(self._language),
                outputs=[agent_state, chat.chatbot_value],
            )

    @property
    def app(self) -> gr.Blocks:
        """Expose underlying Gradio Blocks for external runners (e.g., HF Spaces)."""
        return self._app

    def _chat(self, message: str, history: list[dict], agent: ExecutiveAgentChain):
        if agent is None:
            logger.error("Agent not initialized")
            return ["I apologize, but the chatbot is not properly initialized."]

        answers = []
        try:
            logger.info(f"Processing user query: {message[:100]}...")

            preprocess_resp = agent.preprocess_query(message)
            final_response: LeadAgentQueryResponse = None

            current_lang = preprocess_resp.language
            processed_q = preprocess_resp.processed_query

            if preprocess_resp.response:
                # Response comes from preprocessing step
                final_response = preprocess_resp

            elif Cache._settings["enabled"]:
                cached_data = self._cache.get(processed_q, language=current_lang)

                if cached_data:
                    # Cache Hit — restore response with metadata
                    if isinstance(cached_data, dict):
                        final_response = LeadAgentQueryResponse(
                            response=cached_data["response"],
                            language=current_lang,
                            appointment_requested=cached_data.get("appointment_requested", False),
                            relevant_programs=cached_data.get("relevant_programs", []),
                        )
                    else:
                        # Legacy: plain string cache entry
                        final_response = LeadAgentQueryResponse(
                            response=cached_data,
                            language=current_lang,
                        )

            if not final_response:
                # Response needs to be generated by the agent
                final_response = agent.agent_query(processed_q)

            answers.append(final_response.response)
            self._language = final_response.language

            if final_response.confidence_fallback or final_response.max_turns_reached or final_response.appointment_requested:
                html_code = get_booking_widget(language=self._language, programs=final_response.relevant_programs)
                answers.append(gr.HTML(value=html_code))

            if final_response.should_cache and Cache._settings["enabled"]:
                # Caching response with metadata
                self._cache.set(
                    key=processed_q,
                    value={
                        "response": final_response.response,
                        "appointment_requested": final_response.appointment_requested,
                        "relevant_programs": final_response.relevant_programs,
                    },
                    language=current_lang
                )

        except Exception as e:
            logger.error(f"Error processing query: {e}", exc_info=True)
            error_message = (
                "I apologize, but I encountered an error processing your request. "
                "Please try rephrasing your question or contact our admissions team for assistance."
            )
            answers.append(error_message)

        return answers

    def run(self):
        self._app.launch(
            share=os.getenv("GRADIO_SHARE", "false").lower() == "true",
            server_name=os.getenv("SERVER_NAME", "0.0.0.0"),
            server_port=int(os.getenv("PORT", 7860)),
        )