File size: 9,438 Bytes
777550f
de23a1a
 
777550f
de23a1a
 
777550f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de23a1a
777550f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27b23e1
777550f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de23a1a
777550f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7739a39
777550f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de23a1a
777550f
 
de23a1a
777550f
 
 
 
 
 
de23a1a
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
237
238
239
240
241
242
243
import gradio as gr
import os
from dotenv import load_dotenv
import uuid

load_dotenv(override=True)

from research_assistant import graph as compiled_graph



def start_research(topic:str, max_analysts:int=3, key:str=""): 
    if key != os.getenv("RESEARCH_KEY"):
        return (
            "❌ Invalid key. Please provide a valid key to use this service.",
            gr.update(visible=True, interactive=True),
            gr.update(visible=True),
            gr.update(visible=True),
            gr.update(visible=False),
            gr.update(visible=True),
            gr.update(value="", interactive=True)  # clear key
        )

    global thread_id
    thread_id = str(uuid.uuid4())

    clean_state = {
    "topic": topic,
    "max_analysts": max_analysts,
    "human_analyst_feedback": "",
    "analysts": [],
    "sections": [],
    "introduction": "",
    "content": "",
    "conclusion": "",
    "final_report": ""
    }

    try: 
        thread = {"configurable": {"thread_id": thread_id}}
        result=compiled_graph.invoke(clean_state, thread)

        return display_analysts_and_request_feedback(result)
    
    except Exception as e:

        return reset_to_start(result, f"❌ Error starting research: {str(e)}")

def display_analysts_and_request_feedback(result):
    analysts= result.get('analysts',[])
    if analysts:
        analysts_display="\n".join([
            f"**{i+1}. {analyst.name}**\n"
            f"   - Role: {analyst.role}\n"
            f"   - Affiliation: {analyst.affiliation}\n"
            f"   - Description: {analyst.description}\n"
            for i, analyst in enumerate(analysts)
        ])

        feedback_prompt = (
            f"## Analysts Generated for: '{result['topic']}'\n\n"
            f"{analysts_display}\n\n"
            f"**Please provide your feedback:**\n"
            f"- Type 'approve' to continue with these analysts\n"
            f"- Or provide specific feedback to regenerate analysts\n"
            f"- Example: 'Add a cybersecurity expert, remove the marketing analyst'"
        )

        return (
            feedback_prompt, 
            gr.update(visible=True, value="", interactive=True),
            gr.update(visible=True),
            gr.update(visible=True),
            gr.update(visible=False),
            gr.update(visible=True),  # reset_btn   
            gr.update(value="", interactive=True)  # clear key
            )
    
    else:
            return reset_to_start(result, "❌ No Analysts generated. Please try again with a different topic.")


def continue_with_feedback(feedback, button_clicked):


    if button_clicked == "research":
        feedback= "approve"

    try:
        thread = {"configurable": {"thread_id": thread_id}}

        compiled_graph.update_state(
            thread,
            {"human_analyst_feedback": feedback},
            as_node="human_feedback"
        )
        
        result=compiled_graph.invoke(None, thread)
       

        state= compiled_graph.get_state(thread)

        if state.next and state.next[0] == "human_feedback":
             return display_analysts_and_request_feedback(result)
        else:
             print("Going to display final report")
             return display_final_report(result)
    except Exception as e:
         return reset_to_start(result, f"❌ Error processing feedback: {str(e)}")
    
def display_final_report(result):
    """Muestra el reporte final y resetea la interfaz"""
    print("Displaying final report...")
    try:
        final_report = result.get("final_report", "")
        print(f"Final report content: {final_report}")
        if final_report:
            return (
                f"## πŸ“„ Final Research Report\n\n{final_report}",
                gr.update(visible=False),  # feedback_input
                gr.update(visible=False),  # continue_btn
                gr.update(visible=False),
                gr.update(visible=False),
                gr.update(visible=True),  # reset_btn
                gr.update(value="", interactive=True)  # clear key_input
            )
        else:
            return reset_to_start(result, "❌ Error: No final report was generated.")
        
    except Exception as e:
        return reset_to_start(result, f"❌ Error displaying final report: {str(e)}")
            
def reset_to_start(result, message=""):
    """Resetea la interfaz al estado inicial"""
    return (
        message,
        gr.update(visible=False, value="", interactive=True),  # feedback_input
        gr.update(visible=False),  # continue_btn
        gr.update(visible=False),
        gr.update(visible=False),
        gr.update(value="", interactive=True),  # clear key_input
        gr.update(visible=False)
                # start_btn
    )

def reset_interface():
 
    return (
        "",  # output1
        gr.update(visible=False, value="", interactive=True),  # feedback_input
        gr.update(visible=False),  # continue_btn
        gr.update(visible=False),  # continue_research_btn
        gr.update(visible=True),   # start_btn
        gr.update(visible=True, value=""),  # topic
        gr.update(visible=True)    # max_analysts
    )

############################################## Gradio UI #########################################################################
with gr.Blocks(title="Research Assistant", theme=gr.themes.Soft()) as app:
    gr.Markdown("<h1 style='font-size:2.8em; margin-bottom: 0.2em;'>πŸ€– Research Assistant</h1>")
    gr.Markdown("Assistant for researching complex topics. Process:\n"
    "1. Provide a research topic and the maximum number of analysts, who will research different subtopics related to your topic.\n"
    "2. The assistant will generate a summary of the analysts and their roles and subtopics in the research.\n"
    "3. Provide feedback on the subtopics and the generated analysts.\n"
    "4. If you agree with the analysts, click on the 'Create Research with Analysts' button \n"
    "5. The assistant will generate a final research report.\n\n")

    with gr.Row():
        with gr.Column(scale=3):
            topic_textbox = gr.Textbox(label="Topic to research", placeholder="Enter the topic you want to research", value="")

        with gr.Column(scale=1):
            max_analysts_slider = gr.Slider(minimum=1, maximum=5, step=1, value=3, label="Max Analysts", info="Maximum number of analysts to select for the research")
    
    with gr.Row():
        key_input = gr.Textbox(label="Key to use this service: ", placeholder="Enter your key here", value="", type="password")

    with gr.Row():
        with gr.Column(scale=3):
            gr.Markdown("#Provide Feedback on the Analysts who will perform the research. If you agree with the analysts, click on the 'Create Research with Analysts' button")
            feedback_input = gr.Textbox(label="Feedback", placeholder="Provide your feedback or click on the 'Create Research with Analysts' button", visible=False, value="")
            
        with gr.Column(scale=1):
            reset_btn = gr.Button("πŸ”„ Reset", variant="secondary", visible=False)

    with gr.Row():
        with gr.Column(scale=1):
            start_button = gr.Button("πŸš€ Start Research", variant="primary", visible=True)
            continue_feedback_btn = gr.Button("βœ… Re-generate Analysts with Feedback", variant="primary", visible=False)
        with gr.Column(scale=1):
            continue_research_btn = gr.Button("➑️ Create Research with Analysts", variant="primary", visible=False)



    output1=gr.Markdown(label="Analysts Summary", value="")



    #########################################################################################

    start_button.click(
        fn=start_research,
        inputs=[topic_textbox, max_analysts_slider, key_input],
        outputs=[output1, feedback_input, continue_feedback_btn, continue_research_btn, start_button, reset_btn, key_input]
    )


    continue_feedback_btn.click(
        fn=lambda feedback: continue_with_feedback(feedback, "feedback"),
        inputs=[feedback_input],
        outputs=[output1, feedback_input, continue_feedback_btn, continue_research_btn, start_button, reset_btn, key_input]
    )

    continue_research_btn.click(
        fn=lambda feedback: continue_with_feedback(feedback, "research"),
        inputs=[feedback_input],
        outputs=[output1, feedback_input, continue_feedback_btn, continue_research_btn, start_button, reset_btn, key_input]
    )


    reset_btn.click(
        reset_interface,
        outputs=[output1, feedback_input, continue_feedback_btn, continue_research_btn, start_button, topic_textbox, max_analysts_slider, key_input]
    )

#################################################################################################################################

if __name__ == "__main__":
    # Get port from environment variable (Railway sets this)
    port = int(os.getenv("PORT", 7860))  # Changed default to match Dockerfile

    
    # Launch the app
    print(f"Starting application on port {port}")
    
    app.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        show_error=True,
        inbrowser=True
    )