timeki commited on
Commit
d09f2e9
·
2 Parent(s): 76bfa07 1305361

Merge branch 'feature/clean_code' into dev

Browse files
app.py CHANGED
@@ -1,54 +1,38 @@
1
- from climateqa.engine.embeddings import get_embeddings_function
2
- embeddings_function = get_embeddings_function()
3
-
4
- from sentence_transformers import CrossEncoder
5
-
6
- # reranker = CrossEncoder("mixedbread-ai/mxbai-rerank-xsmall-v1")
7
-
8
- import gradio as gr
9
- from gradio_modal import Modal
10
- import pandas as pd
11
- import numpy as np
12
  import os
 
13
  import time
14
  import re
15
- import json
16
-
17
- from gradio import ChatMessage
18
-
19
- # from gradio_modal import Modal
20
-
21
- from io import BytesIO
22
  import base64
23
-
24
  from datetime import datetime
25
- from azure.storage.fileshare import ShareServiceClient
26
-
27
- from utils import create_user_id
28
 
 
 
 
 
29
  from gradio_modal import Modal
 
 
30
 
31
- from PIL import Image
32
-
33
- from langchain_core.runnables.schema import StreamEvent
34
-
35
- # ClimateQ&A imports
36
  from climateqa.engine.llm import get_llm
37
  from climateqa.engine.vectorstore import get_pinecone_vectorstore
38
- # from climateqa.knowledge.retriever import ClimateQARetriever
39
  from climateqa.engine.reranker import get_reranker
40
- from climateqa.engine.embeddings import get_embeddings_function
41
- from climateqa.engine.chains.prompts import audience_prompts
42
  from climateqa.sample_questions import QUESTIONS
43
- from climateqa.constants import POSSIBLE_REPORTS, OWID_CATEGORIES
44
  from climateqa.utils import get_image_from_azure_blob_storage
45
  from climateqa.engine.graph import make_graph_agent
46
- from climateqa.engine.embeddings import get_embeddings_function
47
  from climateqa.engine.chains.retrieve_papers import find_papers
48
-
49
- from front.utils import serialize_docs,process_figures
50
-
51
- from climateqa.event_handler import init_audience, handle_retrieved_documents, stream_answer,handle_retrieved_owid_graphs
 
 
 
 
52
 
53
  # Load environment variables in local mode
54
  try:
@@ -57,8 +41,6 @@ try:
57
  except Exception as e:
58
  pass
59
 
60
- import requests
61
-
62
  # Set up Gradio Theme
63
  theme = gr.themes.Base(
64
  primary_hue="blue",
@@ -66,15 +48,26 @@ theme = gr.themes.Base(
66
  font=[gr.themes.GoogleFont("Poppins"), "ui-sans-serif", "system-ui", "sans-serif"],
67
  )
68
 
 
 
 
 
 
 
 
 
 
69
 
 
 
70
 
71
- init_prompt = ""
 
72
 
73
- system_template = {
74
- "role": "system",
75
- "content": init_prompt,
76
- }
77
 
 
78
  account_key = os.environ["BLOB_ACCOUNT_KEY"]
79
  if len(account_key) == 86:
80
  account_key += "=="
@@ -91,7 +84,7 @@ share_client = service.get_share_client(file_share_name)
91
 
92
  user_id = create_user_id()
93
 
94
-
95
  CITATION_LABEL = "BibTeX citation for ClimateQ&A"
96
  CITATION_TEXT = r"""@misc{climateqa,
97
  author={Théo Alves Da Costa, Timothée Bohe},
@@ -106,41 +99,73 @@ CITATION_TEXT = r"""@misc{climateqa,
106
  }
107
  """
108
 
109
-
110
-
111
  # Create vectorstore and retriever
112
- vectorstore = get_pinecone_vectorstore(embeddings_function, index_name = os.getenv("PINECONE_API_INDEX"))
113
- vectorstore_graphs = get_pinecone_vectorstore(embeddings_function, index_name = os.getenv("PINECONE_API_INDEX_OWID"), text_key="description")
 
114
 
115
  llm = get_llm(provider="openai",max_tokens = 1024,temperature = 0.0)
116
- reranker = get_reranker("large")
117
 
118
  agent = make_graph_agent(llm=llm, vectorstore_ipcc=vectorstore, vectorstore_graphs=vectorstore_graphs, reranker=reranker)
119
 
 
120
  def update_config_modal_visibility(config_open):
121
  new_config_visibility_status = not config_open
122
  return gr.update(visible=new_config_visibility_status), new_config_visibility_status
123
 
124
- async def chat(query, history, audience, sources, reports, relevant_content_sources, search_only):
125
- """taking a query and a message history, use a pipeline (reformulation, retriever, answering) to yield a tuple of:
126
- (messages in gradio format, messages in langchain format, source documents)"""
127
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  date_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
129
  print(f">> NEW QUESTION ({date_now}) : {query}")
130
 
131
  audience_prompt = init_audience(audience)
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
- # Prepare default values
134
- if sources is None or len(sources) == 0:
135
- sources = ["IPCC", "IPBES", "IPOS"]
136
-
137
- if reports is None or len(reports) == 0:
138
- reports = []
139
-
140
- inputs = {"user_input": query,"audience": audience_prompt,"sources_input":sources, "relevant_content_sources" : relevant_content_sources, "search_only": search_only, "reports": reports}
141
- result = agent.astream_events(inputs,version = "v1")
142
-
143
 
 
144
  docs = []
145
  related_contents = []
146
  docs_html = ""
@@ -149,84 +174,95 @@ async def chat(query, history, audience, sources, reports, relevant_content_sour
149
  output_keywords = ""
150
  start_streaming = False
151
  graphs_html = ""
152
- figures = '<div class="figures-container"><p></p> </div>'
 
153
 
 
154
  steps_display = {
155
- "categorize_intent":("🔄️ Analyzing user message",True),
156
- "transform_query":("🔄️ Thinking step by step to answer the question",True),
157
- "retrieve_documents":("🔄️ Searching in the knowledge base",False),
158
  }
159
-
160
- used_documents = []
161
- answer_message_content = ""
162
  try:
 
163
  async for event in result:
164
  if "langgraph_node" in event["metadata"]:
165
  node = event["metadata"]["langgraph_node"]
166
 
167
- if event["event"] == "on_chain_end" and event["name"] == "retrieve_documents" and event["data"]["output"] != None:# when documents are retrieved
168
- docs, docs_html, history, used_documents, related_contents = handle_retrieved_documents(event, history, used_documents)
169
-
170
- elif event["event"] == "on_chain_end" and node == "categorize_intent" and event["name"] == "_write": # when the query is transformed
171
-
 
 
 
 
 
172
  intent = event["data"]["output"]["intent"]
173
- if "language" in event["data"]["output"]:
174
- output_language = event["data"]["output"]["language"]
175
- else :
176
- output_language = "English"
177
- history[-1].content = f"Language identified : {output_language} \n Intent identified : {intent}"
178
-
179
-
180
- elif event["name"] in steps_display.keys() and event["event"] == "on_chain_start": #display steps
181
  event_description, display_output = steps_display[node]
182
- if not hasattr(history[-1], 'metadata') or history[-1].metadata["title"] != event_description: # if a new step begins
183
- history.append(ChatMessage(role="assistant", content = "", metadata={'title' :event_description}))
184
-
185
- elif event["name"] != "transform_query" and event["event"] == "on_chat_model_stream" and node in ["answer_rag", "answer_rag_no_docs","answer_search","answer_chitchat"]:# if streaming answer
186
- history, start_streaming, answer_message_content = stream_answer(history, event, start_streaming, answer_message_content)
 
 
 
 
 
 
 
 
 
 
187
 
 
188
  elif event["name"] in ["retrieve_graphs", "retrieve_graphs_ai"] and event["event"] == "on_chain_end":
189
  graphs_html = handle_retrieved_owid_graphs(event, graphs_html)
190
 
 
 
 
 
 
191
 
192
- if event["name"] == "transform_query" and event["event"] =="on_chain_end":
193
- if hasattr(history[-1],"content"):
194
- history[-1].content += "Decompose question into sub-questions: \n\n - " + "\n - ".join([q["question"] for q in event["data"]["output"]["remaining_questions"]])
195
-
196
- if event["name"] == "categorize_intent" and event["event"] == "on_chain_start":
197
- print("X")
198
-
199
- yield history, docs_html, output_query, output_language, related_contents , graphs_html, #,output_query,output_keywords
200
-
201
- except Exception as e:
202
- print(event, "has failed")
203
- raise gr.Error(f"{e}")
204
 
 
 
 
205
 
206
  try:
207
- # Log answer on Azure Blob Storage
208
  if os.getenv("GRADIO_ENV") != "local":
209
  timestamp = str(datetime.now().timestamp())
210
- file = timestamp + ".json"
211
  prompt = history[1]["content"]
212
  logs = {
213
  "user_id": str(user_id),
214
  "prompt": prompt,
215
  "query": prompt,
216
- "question":output_query,
217
- "sources":sources,
218
- "docs":serialize_docs(docs),
219
  "answer": history[-1].content,
220
  "time": timestamp,
221
  }
222
- log_on_azure(file, logs, share_client)
223
  except Exception as e:
224
  print(f"Error logging on Azure Blob Storage: {e}")
225
- raise gr.Error(f"ClimateQ&A Error: {str(e)[:100]} - The error has been noted, try another question and if the error remains, you can contact us :)")
 
226
 
227
  yield history, docs_html, output_query, output_language, related_contents, graphs_html
228
 
229
-
230
  def save_feedback(feed: str, user_id):
231
  if len(feed) > 1:
232
  timestamp = str(datetime.now().timestamp())
@@ -239,9 +275,7 @@ def save_feedback(feed: str, user_id):
239
  log_on_azure(file, logs, share_client)
240
  return "Feedback submitted, thank you!"
241
 
242
-
243
-
244
-
245
  def log_on_azure(file, logs, share_client):
246
  logs = json.dumps(logs)
247
  file_client = share_client.get_file_client(file)
@@ -256,25 +290,6 @@ def log_on_azure(file, logs, share_client):
256
  # --------------------------------------------------------------------
257
 
258
 
259
- init_prompt = """
260
- Hello, I am ClimateQ&A, a conversational assistant designed to help you understand climate change and biodiversity loss. I will answer your questions by **sifting through the IPCC and IPBES scientific reports**.
261
-
262
- ❓ How to use
263
- - **Language**: You can ask me your questions in any language.
264
- - **Audience**: You can specify your audience (children, general public, experts) to get a more adapted answer.
265
- - **Sources**: You can choose to search in the IPCC or IPBES reports, or both.
266
- - **Relevant content sources**: You can choose to search for figures, papers, or graphs that can be relevant for your question.
267
-
268
- ⚠️ Limitations
269
- *Please note that the AI is not perfect and may sometimes give irrelevant answers. If you are not satisfied with the answer, please ask a more specific question or report your feedback to help us improve the system.*
270
-
271
- 🛈 Information
272
- Please note that we log your questions for meta-analysis purposes, so avoid sharing any sensitive or personal information.
273
-
274
-
275
- What do you want to learn ?
276
- """
277
-
278
 
279
  def vote(data: gr.LikeData):
280
  if data.liked:
@@ -291,293 +306,312 @@ def save_graph(saved_graphs_state, embedding, category):
291
  return saved_graphs_state, gr.Button("Graph Saved")
292
 
293
 
 
 
 
 
 
294
 
295
- with gr.Blocks(title="Climate Q&A", css_paths=os.getcwd()+ "/style.css", theme=theme,elem_id = "main-component") as demo:
296
- chat_completed_state = gr.State(0)
297
- current_graphs = gr.State([])
298
- saved_graphs = gr.State({})
299
- config_open = gr.State(False)
300
 
301
-
302
- with gr.Tab("ClimateQ&A"):
 
303
 
304
- with gr.Row(elem_id="chatbot-row"):
305
- with gr.Column(scale=2):
306
- chatbot = gr.Chatbot(
307
- value = [ChatMessage(role="assistant", content=init_prompt)],
308
- type = "messages",
309
- show_copy_button=True,
310
- show_label = False,
311
- elem_id="chatbot",
312
- layout = "panel",
313
- avatar_images = (None,"https://i.ibb.co/YNyd5W2/logo4.png"),
314
- max_height="80vh",
315
- height="100vh"
316
- )
317
-
318
- # bot.like(vote,None,None)
319
 
 
320
 
321
-
322
- with gr.Row(elem_id = "input-message"):
323
- textbox = gr.Textbox(
324
- placeholder="Ask me anything here!",
325
- show_label=False,
326
- scale=12,
327
- lines=1,
328
- interactive=True,
329
- elem_id="input-textbox"
330
- )
331
-
332
- config_button = gr.Button(
333
- "",
334
- elem_id="config-button"
335
- )
336
-
337
-
338
-
339
- with gr.Column(scale=2, variant="panel",elem_id = "right-panel"):
340
 
341
 
342
- with gr.Tabs(elem_id = "right_panel_tab") as tabs:
343
- with gr.TabItem("Examples",elem_id = "tab-examples",id = 0):
344
-
345
- examples_hidden = gr.Textbox(visible = False)
346
- first_key = list(QUESTIONS.keys())[0]
347
- dropdown_samples = gr.Dropdown(QUESTIONS.keys(),value = first_key,interactive = True,show_label = True,label = "Select a category of sample questions",elem_id = "dropdown-samples")
348
 
349
- samples = []
350
- for i,key in enumerate(QUESTIONS.keys()):
351
-
352
- examples_visible = True if i == 0 else False
353
-
354
- with gr.Row(visible = examples_visible) as group_examples:
355
-
356
- examples_questions = gr.Examples(
357
- QUESTIONS[key],
358
- [examples_hidden],
359
- examples_per_page=8,
360
- run_on_click=False,
361
- elem_id=f"examples{i}",
362
- api_name=f"examples{i}",
363
- # label = "Click on the example question or enter your own",
364
- # cache_examples=True,
365
- )
366
-
367
- samples.append(group_examples)
368
-
369
- # with gr.Tab("Configuration", id = 10, ) as tab_config:
370
- # # gr.Markdown("Reminders: You can talk in any language, ClimateQ&A is multi-lingual!")
371
-
372
- # pass
373
-
374
- # with gr.Row():
375
-
376
- # dropdown_sources = gr.CheckboxGroup(
377
- # ["IPCC", "IPBES","IPOS"],
378
- # label="Select source",
379
- # value=["IPCC"],
380
- # interactive=True,
381
- # )
382
- # dropdown_external_sources = gr.CheckboxGroup(
383
- # ["IPCC figures","OpenAlex", "OurWorldInData"],
384
- # label="Select database to search for relevant content",
385
- # value=["IPCC figures"],
386
- # interactive=True,
387
- # )
388
-
389
- # dropdown_reports = gr.Dropdown(
390
- # POSSIBLE_REPORTS,
391
- # label="Or select specific reports",
392
- # multiselect=True,
393
- # value=None,
394
- # interactive=True,
395
- # )
396
-
397
- # search_only = gr.Checkbox(label="Search only without chating", value=False, interactive=True, elem_id="checkbox-chat")
398
-
399
-
400
- # dropdown_audience = gr.Dropdown(
401
- # ["Children","General public","Experts"],
402
- # label="Select audience",
403
- # value="Experts",
404
- # interactive=True,
405
- # )
406
-
407
-
408
- # after = gr.Slider(minimum=1950,maximum=2023,step=1,value=1960,label="Publication date",show_label=True,interactive=True,elem_id="date-papers", visible=False)
409
-
410
-
411
- # output_query = gr.Textbox(label="Query used for retrieval",show_label = True,elem_id = "reformulated-query",lines = 2,interactive = False, visible= False)
412
- # output_language = gr.Textbox(label="Language",show_label = True,elem_id = "language",lines = 1,interactive = False, visible= False)
413
-
414
-
415
- # dropdown_external_sources.change(lambda x: gr.update(visible = True ) if "OpenAlex" in x else gr.update(visible=False) , inputs=[dropdown_external_sources], outputs=[after])
416
- # # dropdown_external_sources.change(lambda x: gr.update(visible = True ) if "OpenAlex" in x else gr.update(visible=False) , inputs=[dropdown_external_sources], outputs=[after], visible=True)
417
-
418
-
419
- with gr.Tab("Sources",elem_id = "tab-sources",id = 1) as tab_sources:
420
- sources_textbox = gr.HTML(show_label=False, elem_id="sources-textbox")
421
-
422
-
423
-
424
- with gr.Tab("Recommended content", elem_id="tab-recommended_content",id=2) as tab_recommended_content:
425
- with gr.Tabs(elem_id = "group-subtabs") as tabs_recommended_content:
426
 
427
- with gr.Tab("Figures",elem_id = "tab-figures",id = 3) as tab_figures:
428
- sources_raw = gr.State([])
429
- new_figures = gr.State([])
430
- used_figures = gr.State([])
431
-
432
- with Modal(visible=False, elem_id="modal_figure_galery") as figure_modal:
433
- gallery_component = gr.Gallery(object_fit='scale-down',elem_id="gallery-component", height="80vh")
434
-
435
- show_full_size_figures = gr.Button("Show figures in full size",elem_id="show-figures",interactive=True)
436
- show_full_size_figures.click(lambda : Modal(visible=True),None,figure_modal)
437
-
438
- figures_cards = gr.HTML(show_label=False, elem_id="sources-figures")
439
-
440
-
441
-
442
- with gr.Tab("Papers",elem_id = "tab-citations",id = 4) as tab_papers:
443
- # btn_summary = gr.Button("Summary")
444
- # Fenêtre simulée pour le Summary
445
- with gr.Accordion(visible=True, elem_id="papers-summary-popup", label= "See summary of relevant papers", open= False) as summary_popup:
446
- papers_summary = gr.Markdown("", visible=True, elem_id="papers-summary")
447
-
448
- # btn_relevant_papers = gr.Button("Relevant papers")
449
- # Fenêtre simulée pour les Relevant Papers
450
- with gr.Accordion(visible=True, elem_id="papers-relevant-popup",label= "See relevant papers", open= False) as relevant_popup:
451
- papers_html = gr.HTML(show_label=False, elem_id="papers-textbox")
452
-
453
- btn_citations_network = gr.Button("Explore papers citations network")
454
- # Fenêtre simulée pour le Citations Network
455
- with Modal(visible=False) as papers_modal:
456
- citations_network = gr.HTML("<h3>Citations Network Graph</h3>", visible=True, elem_id="papers-citations-network")
457
- btn_citations_network.click(lambda: Modal(visible=True), None, papers_modal)
458
-
459
-
460
-
461
- with gr.Tab("Graphs", elem_id="tab-graphs", id=5) as tab_graphs:
462
-
463
- graphs_container = gr.HTML("<h2>There are no graphs to be displayed at the moment. Try asking another question.</h2>",elem_id="graphs-container")
464
- current_graphs.change(lambda x : x, inputs=[current_graphs], outputs=[graphs_container])
465
-
466
- with Modal(visible=False,elem_id="modal-config") as config_modal:
467
- gr.Markdown("Reminders: You can talk in any language, ClimateQ&A is multi-lingual!")
468
-
469
-
470
- # with gr.Row():
471
-
472
- dropdown_sources = gr.CheckboxGroup(
473
- ["IPCC", "IPBES","IPOS"],
474
- label="Select source (by default search in all sources)",
475
- value=["IPCC"],
476
- interactive=True,
477
- )
478
-
479
- dropdown_reports = gr.Dropdown(
480
- POSSIBLE_REPORTS,
481
- label="Or select specific reports",
482
- multiselect=True,
483
- value=None,
484
- interactive=True,
485
- )
486
-
487
- dropdown_external_sources = gr.CheckboxGroup(
488
- ["Figures (IPCC/IPBES)","Papers (OpenAlex)", "Graphs (OurWorldInData)"],
489
- label="Select database to search for relevant content",
490
- value=["Figures (IPCC/IPBES)"],
491
- interactive=True,
492
- )
493
-
494
- search_only = gr.Checkbox(label="Search only for recommended content without chating", value=False, interactive=True, elem_id="checkbox-chat")
495
-
496
-
497
- dropdown_audience = gr.Dropdown(
498
- ["Children","General public","Experts"],
499
- label="Select audience",
500
- value="Experts",
501
- interactive=True,
502
- )
503
-
504
-
505
- after = gr.Slider(minimum=1950,maximum=2023,step=1,value=1960,label="Publication date",show_label=True,interactive=True,elem_id="date-papers", visible=False)
506
-
507
-
508
- output_query = gr.Textbox(label="Query used for retrieval",show_label = True,elem_id = "reformulated-query",lines = 2,interactive = False, visible= False)
509
- output_language = gr.Textbox(label="Language",show_label = True,elem_id = "language",lines = 1,interactive = False, visible= False)
510
-
511
-
512
- dropdown_external_sources.change(lambda x: gr.update(visible = True ) if "Papers (OpenAlex)" in x else gr.update(visible=False) , inputs=[dropdown_external_sources], outputs=[after])
513
-
514
- close_config_modal = gr.Button("Validate and Close",elem_id="close-config-modal")
515
- close_config_modal.click(fn=update_config_modal_visibility, inputs=[config_open], outputs=[config_modal, config_open])
516
- # dropdown_external_sources.change(lambda x: gr.update(visible = True ) if "OpenAlex" in x else gr.update(visible=False) , inputs=[dropdown_external_sources], outputs=[after], visible=True)
517
-
518
-
519
-
520
- config_button.click(fn=update_config_modal_visibility, inputs=[config_open], outputs=[config_modal, config_open])
521
-
522
- # with gr.Tab("OECD",elem_id = "tab-oecd",id = 6):
523
- # oecd_indicator = "RIVER_FLOOD_RP100_POP_SH"
524
- # oecd_topic = "climate"
525
- # oecd_latitude = "46.8332"
526
- # oecd_longitude = "5.3725"
527
- # oecd_zoom = "5.6442"
528
- # # Create the HTML content with the iframe
529
- # iframe_html = f"""
530
- # <iframe src="https://localdataportal.oecd.org/maps.html?indicator={oecd_indicator}&topic={oecd_topic}&latitude={oecd_latitude}&longitude={oecd_longitude}&zoom={oecd_zoom}"
531
- # width="100%" height="600" frameborder="0" style="border:0;" allowfullscreen></iframe>
532
- # """
533
- # oecd_textbox = gr.HTML(iframe_html, show_label=False, elem_id="oecd-textbox")
534
-
535
-
536
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
537
 
538
- #---------------------------------------------------------------------------------------
539
- # OTHER TABS
540
- #---------------------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
 
542
- # with gr.Tab("Settings",elem_id = "tab-config",id = 2):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
543
 
544
- # gr.Markdown("Reminder: You can talk in any language, ClimateQ&A is multi-lingual!")
 
 
 
 
 
 
545
 
 
 
 
 
 
546
 
547
- # dropdown_sources = gr.CheckboxGroup(
548
- # ["IPCC", "IPBES","IPOS", "OpenAlex"],
549
- # label="Select source",
550
- # value=["IPCC"],
551
- # interactive=True,
552
- # )
553
 
554
- # dropdown_reports = gr.Dropdown(
555
- # POSSIBLE_REPORTS,
556
- # label="Or select specific reports",
557
- # multiselect=True,
558
- # value=None,
559
- # interactive=True,
560
- # )
561
 
562
- # dropdown_audience = gr.Dropdown(
563
- # ["Children","General public","Experts"],
564
- # label="Select audience",
565
- # value="Experts",
566
- # interactive=True,
567
- # )
568
 
 
 
 
569
 
570
- # output_query = gr.Textbox(label="Query used for retrieval",show_label = True,elem_id = "reformulated-query",lines = 2,interactive = False)
571
- # output_language = gr.Textbox(label="Language",show_label = True,elem_id = "language",lines = 1,interactive = False)
 
 
 
 
 
 
 
 
 
572
 
 
573
 
574
- with gr.Tab("About",elem_classes = "max-height other-tabs"):
 
575
  with gr.Row():
576
  with gr.Column(scale=1):
577
-
578
-
579
-
580
-
581
  gr.Markdown(
582
  """
583
  ### More info
@@ -587,8 +621,7 @@ with gr.Blocks(title="Climate Q&A", css_paths=os.getcwd()+ "/style.css", theme=t
587
  ### Citation
588
  """
589
  )
590
- with gr.Accordion(CITATION_LABEL,elem_id="citation", open = False,):
591
- # # Display citation label and text)
592
  gr.Textbox(
593
  value=CITATION_TEXT,
594
  label="",
@@ -597,103 +630,45 @@ with gr.Blocks(title="Climate Q&A", css_paths=os.getcwd()+ "/style.css", theme=t
597
  lines=len(CITATION_TEXT.split('\n')),
598
  )
599
 
600
-
601
-
602
- def start_chat(query,history,search_only):
603
- history = history + [ChatMessage(role="user", content=query)]
604
- if not search_only:
605
- return (gr.update(interactive = False),gr.update(selected=1),history, [])
606
- else:
607
- return (gr.update(interactive = False),gr.update(selected=2),history, [])
608
-
609
- def finish_chat():
610
- return gr.update(interactive = True,value = "")
611
 
612
- # Initialize visibility states
613
- summary_visible = False
614
- relevant_visible = False
615
-
616
- # Functions to toggle visibility
617
- def toggle_summary_visibility():
618
- global summary_visible
619
- summary_visible = not summary_visible
620
- return gr.update(visible=summary_visible)
621
-
622
- def toggle_relevant_visibility():
623
- global relevant_visible
624
- relevant_visible = not relevant_visible
625
- return gr.update(visible=relevant_visible)
626
-
627
-
628
- def change_completion_status(current_state):
629
- current_state = 1 - current_state
630
- return current_state
631
 
632
- def update_sources_number_display(sources_textbox, figures_cards, current_graphs, papers_html):
633
- sources_number = sources_textbox.count("<h2>")
634
- figures_number = figures_cards.count("<h2>")
635
- graphs_number = current_graphs.count("<iframe")
636
- papers_number = papers_html.count("<h2>")
637
- sources_notif_label = f"Sources ({sources_number})"
638
- figures_notif_label = f"Figures ({figures_number})"
639
- graphs_notif_label = f"Graphs ({graphs_number})"
640
- papers_notif_label = f"Papers ({papers_number})"
641
- recommended_content_notif_label = f"Recommended content ({figures_number + graphs_number + papers_number})"
642
-
643
- return gr.update(label = recommended_content_notif_label), gr.update(label = sources_notif_label), gr.update(label = figures_notif_label), gr.update(label = graphs_notif_label), gr.update(label = papers_notif_label)
644
 
645
  (textbox
646
- .submit(start_chat, [textbox, chatbot, search_only],
647
- [textbox, tabs, chatbot, sources_raw],
648
- queue=False,
649
- api_name="start_chat_textbox")
650
- .then(chat, [textbox, chatbot, dropdown_audience, dropdown_sources,
651
- dropdown_reports, dropdown_external_sources, search_only],
652
- [chatbot, sources_textbox, output_query, output_language,
653
- new_figures, current_graphs],
654
- concurrency_limit=8,
655
- api_name="chat_textbox")
656
- .then(finish_chat, None, [textbox],
657
- api_name="finish_chat_textbox")
658
  )
659
 
660
 
661
 
662
  (examples_hidden
663
- .change(start_chat, [examples_hidden,chatbot, search_only], [textbox,tabs,chatbot, sources_raw],queue = False,api_name = "start_chat_examples")
664
- .then(chat, [examples_hidden,chatbot,dropdown_audience, dropdown_sources,dropdown_reports, dropdown_external_sources, search_only] ,[chatbot,sources_textbox,output_query,output_language, new_figures, current_graphs],concurrency_limit = 8,api_name = "chat_textbox")
665
- .then(finish_chat, None, [textbox],api_name = "finish_chat_examples")
666
- # .then(update_sources_number_display, [sources_textbox, figures_cards, current_graphs,papers_html],[tab_sources, tab_figures, tab_graphs, tab_papers] )
667
  )
668
 
669
-
670
- def change_sample_questions(key):
671
- index = list(QUESTIONS.keys()).index(key)
672
- visible_bools = [False] * len(samples)
673
- visible_bools[index] = True
674
- return [gr.update(visible=visible_bools[i]) for i in range(len(samples))]
675
-
676
-
677
  new_figures.change(process_figures, inputs=[sources_raw, new_figures], outputs=[sources_raw, figures_cards, gallery_component])
678
-
679
- # update sources numbers
680
- sources_textbox.change(update_sources_number_display, [sources_textbox, figures_cards, current_graphs,papers_html],[tab_recommended_content, tab_sources, tab_figures, tab_graphs, tab_papers])
681
- figures_cards.change(update_sources_number_display, [sources_textbox, figures_cards, current_graphs,papers_html],[tab_recommended_content, tab_sources, tab_figures, tab_graphs, tab_papers])
682
- current_graphs.change(update_sources_number_display, [sources_textbox, figures_cards, current_graphs,papers_html],[tab_recommended_content, tab_sources, tab_figures, tab_graphs, tab_papers])
683
- papers_html.change(update_sources_number_display, [sources_textbox, figures_cards, current_graphs,papers_html],[tab_recommended_content, tab_sources, tab_figures, tab_graphs, tab_papers])
684
 
685
- # other questions examples
686
- dropdown_samples.change(change_sample_questions,dropdown_samples,samples)
 
 
 
687
 
688
- # search for papers
689
- textbox.submit(find_papers,[textbox,after, dropdown_external_sources], [papers_html,citations_network,papers_summary])
690
- examples_hidden.change(find_papers,[examples_hidden,after,dropdown_external_sources], [papers_html,citations_network,papers_summary])
691
 
692
- # btn_summary.click(toggle_summary_visibility, outputs=summary_popup)
693
- # btn_relevant_papers.click(toggle_relevant_visibility, outputs=relevant_popup)
 
694
 
695
  demo.queue()
696
-
697
-
698
-
699
  demo.launch(ssr_mode=False)
 
1
+ # Import necessary libraries
 
 
 
 
 
 
 
 
 
 
2
  import os
3
+ import json
4
  import time
5
  import re
 
 
 
 
 
 
 
6
  import base64
 
7
  from datetime import datetime
8
+ from io import BytesIO
 
 
9
 
10
+ import numpy as np
11
+ import pandas as pd
12
+ import gradio as gr
13
+ from gradio import ChatMessage
14
  from gradio_modal import Modal
15
+ from sentence_transformers import CrossEncoder
16
+ from azure.storage.fileshare import ShareServiceClient
17
 
18
+ # Import custom modules
19
+ from climateqa.engine.embeddings import get_embeddings_function
 
 
 
20
  from climateqa.engine.llm import get_llm
21
  from climateqa.engine.vectorstore import get_pinecone_vectorstore
 
22
  from climateqa.engine.reranker import get_reranker
 
 
23
  from climateqa.sample_questions import QUESTIONS
24
+ from climateqa.constants import POSSIBLE_REPORTS
25
  from climateqa.utils import get_image_from_azure_blob_storage
26
  from climateqa.engine.graph import make_graph_agent
 
27
  from climateqa.engine.chains.retrieve_papers import find_papers
28
+ from front.utils import serialize_docs, process_figures
29
+ from climateqa.event_handler import (
30
+ init_audience,
31
+ handle_retrieved_documents,
32
+ stream_answer,
33
+ handle_retrieved_owid_graphs
34
+ )
35
+ from utils import create_user_id
36
 
37
  # Load environment variables in local mode
38
  try:
 
41
  except Exception as e:
42
  pass
43
 
 
 
44
  # Set up Gradio Theme
45
  theme = gr.themes.Base(
46
  primary_hue="blue",
 
48
  font=[gr.themes.GoogleFont("Poppins"), "ui-sans-serif", "system-ui", "sans-serif"],
49
  )
50
 
51
+ # Initialize prompt and system template
52
+ init_prompt = """
53
+ Hello, I am ClimateQ&A, a conversational assistant designed to help you understand climate change and biodiversity loss. I will answer your questions by **sifting through the IPCC and IPBES scientific reports**.
54
+
55
+ ❓ How to use
56
+ - **Language**: You can ask me your questions in any language.
57
+ - **Audience**: You can specify your audience (children, general public, experts) to get a more adapted answer.
58
+ - **Sources**: You can choose to search in the IPCC or IPBES reports, or both.
59
+ - **Relevant content sources**: You can choose to search for figures, papers, or graphs that can be relevant for your question.
60
 
61
+ ⚠️ Limitations
62
+ *Please note that the AI is not perfect and may sometimes give irrelevant answers. If you are not satisfied with the answer, please ask a more specific question or report your feedback to help us improve the system.*
63
 
64
+ 🛈 Information
65
+ Please note that we log your questions for meta-analysis purposes, so avoid sharing any sensitive or personal information.
66
 
67
+ What do you want to learn ?
68
+ """
 
 
69
 
70
+ # Azure Blob Storage credentials
71
  account_key = os.environ["BLOB_ACCOUNT_KEY"]
72
  if len(account_key) == 86:
73
  account_key += "=="
 
84
 
85
  user_id = create_user_id()
86
 
87
+ # Citation information
88
  CITATION_LABEL = "BibTeX citation for ClimateQ&A"
89
  CITATION_TEXT = r"""@misc{climateqa,
90
  author={Théo Alves Da Costa, Timothée Bohe},
 
99
  }
100
  """
101
 
 
 
102
  # Create vectorstore and retriever
103
+ embeddings_function = get_embeddings_function()
104
+ vectorstore = get_pinecone_vectorstore(embeddings_function, index_name=os.getenv("PINECONE_API_INDEX"))
105
+ vectorstore_graphs = get_pinecone_vectorstore(embeddings_function, index_name=os.getenv("PINECONE_API_INDEX_OWID"), text_key="description")
106
 
107
  llm = get_llm(provider="openai",max_tokens = 1024,temperature = 0.0)
108
+ reranker = get_reranker("nano")
109
 
110
  agent = make_graph_agent(llm=llm, vectorstore_ipcc=vectorstore, vectorstore_graphs=vectorstore_graphs, reranker=reranker)
111
 
112
+ # Function to update modal visibility
113
  def update_config_modal_visibility(config_open):
114
  new_config_visibility_status = not config_open
115
  return gr.update(visible=new_config_visibility_status), new_config_visibility_status
116
 
117
+ # Main chat function
118
+ async def chat(
119
+ query: str,
120
+ history: list[ChatMessage],
121
+ audience: str,
122
+ sources: list[str],
123
+ reports: list[str],
124
+ relevant_content_sources_selection: list[str],
125
+ search_only: bool
126
+ ) -> tuple[list, str, str, str, list, str]:
127
+ """Process a chat query and return response with relevant sources and visualizations.
128
+
129
+ Args:
130
+ query (str): The user's question
131
+ history (list): Chat message history
132
+ audience (str): Target audience type
133
+ sources (list): Knowledge base sources to search
134
+ reports (list): Specific reports to search within sources
135
+ relevant_content_sources_selection (list): Types of content to retrieve (figures, papers, etc)
136
+ search_only (bool): Whether to only search without generating answer
137
+
138
+ Yields:
139
+ tuple: Contains:
140
+ - history: Updated chat history
141
+ - docs_html: HTML of retrieved documents
142
+ - output_query: Processed query
143
+ - output_language: Detected language
144
+ - related_contents: Related content
145
+ - graphs_html: HTML of relevant graphs
146
+ """
147
+ # Log incoming question
148
  date_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
149
  print(f">> NEW QUESTION ({date_now}) : {query}")
150
 
151
  audience_prompt = init_audience(audience)
152
+ sources = sources or ["IPCC", "IPBES", "IPOS"]
153
+ reports = reports or []
154
+
155
+ # Prepare inputs for agent
156
+ inputs = {
157
+ "user_input": query,
158
+ "audience": audience_prompt,
159
+ "sources_input": sources,
160
+ "relevant_content_sources_selection": relevant_content_sources_selection,
161
+ "search_only": search_only,
162
+ "reports": reports
163
+ }
164
 
165
+ # Get streaming events from agent
166
+ result = agent.astream_events(inputs, version="v1")
 
 
 
 
 
 
 
 
167
 
168
+ # Initialize state variables
169
  docs = []
170
  related_contents = []
171
  docs_html = ""
 
174
  output_keywords = ""
175
  start_streaming = False
176
  graphs_html = ""
177
+ used_documents = []
178
+ answer_message_content = ""
179
 
180
+ # Define processing steps
181
  steps_display = {
182
+ "categorize_intent": ("🔄️ Analyzing user message", True),
183
+ "transform_query": ("🔄️ Thinking step by step to answer the question", True),
184
+ "retrieve_documents": ("🔄️ Searching in the knowledge base", False),
185
  }
186
+
 
 
187
  try:
188
+ # Process streaming events
189
  async for event in result:
190
  if "langgraph_node" in event["metadata"]:
191
  node = event["metadata"]["langgraph_node"]
192
 
193
+ # Handle document retrieval
194
+ if event["event"] == "on_chain_end" and event["name"] == "retrieve_documents" and event["data"]["output"] != None:
195
+ docs, docs_html, history, used_documents, related_contents = handle_retrieved_documents(
196
+ event, history, used_documents
197
+ )
198
+
199
+ # Handle intent categorization
200
+ elif (event["event"] == "on_chain_end" and
201
+ node == "categorize_intent" and
202
+ event["name"] == "_write"):
203
  intent = event["data"]["output"]["intent"]
204
+ output_language = event["data"]["output"].get("language", "English")
205
+ history[-1].content = f"Language identified: {output_language}\nIntent identified: {intent}"
206
+
207
+ # Handle processing steps display
208
+ elif event["name"] in steps_display and event["event"] == "on_chain_start":
 
 
 
209
  event_description, display_output = steps_display[node]
210
+ if (not hasattr(history[-1], 'metadata') or
211
+ history[-1].metadata["title"] != event_description):
212
+ history.append(ChatMessage(
213
+ role="assistant",
214
+ content="",
215
+ metadata={'title': event_description}
216
+ ))
217
+
218
+ # Handle answer streaming
219
+ elif (event["name"] != "transform_query" and
220
+ event["event"] == "on_chat_model_stream" and
221
+ node in ["answer_rag","answer_rag_no_docs", "answer_search", "answer_chitchat"]):
222
+ history, start_streaming, answer_message_content = stream_answer(
223
+ history, event, start_streaming, answer_message_content
224
+ )
225
 
226
+ # Handle graph retrieval
227
  elif event["name"] in ["retrieve_graphs", "retrieve_graphs_ai"] and event["event"] == "on_chain_end":
228
  graphs_html = handle_retrieved_owid_graphs(event, graphs_html)
229
 
230
+ # Handle query transformation
231
+ if event["name"] == "transform_query" and event["event"] == "on_chain_end":
232
+ if hasattr(history[-1], "content"):
233
+ sub_questions = [q["question"] for q in event["data"]["output"]["remaining_questions"]]
234
+ history[-1].content += "Decompose question into sub-questions:\n\n - " + "\n - ".join(sub_questions)
235
 
236
+ yield history, docs_html, output_query, output_language, related_contents, graphs_html
 
 
 
 
 
 
 
 
 
 
 
237
 
238
+ except Exception as e:
239
+ print(f"Event {event} has failed")
240
+ raise gr.Error(str(e))
241
 
242
  try:
243
+ # Log interaction to Azure if not in local environment
244
  if os.getenv("GRADIO_ENV") != "local":
245
  timestamp = str(datetime.now().timestamp())
 
246
  prompt = history[1]["content"]
247
  logs = {
248
  "user_id": str(user_id),
249
  "prompt": prompt,
250
  "query": prompt,
251
+ "question": output_query,
252
+ "sources": sources,
253
+ "docs": serialize_docs(docs),
254
  "answer": history[-1].content,
255
  "time": timestamp,
256
  }
257
+ log_on_azure(f"{timestamp}.json", logs, share_client)
258
  except Exception as e:
259
  print(f"Error logging on Azure Blob Storage: {e}")
260
+ error_msg = f"ClimateQ&A Error: {str(e)[:100]} - The error has been noted, try another question and if the error remains, you can contact us :)"
261
+ raise gr.Error(error_msg)
262
 
263
  yield history, docs_html, output_query, output_language, related_contents, graphs_html
264
 
265
+ # Function to save feedback
266
  def save_feedback(feed: str, user_id):
267
  if len(feed) > 1:
268
  timestamp = str(datetime.now().timestamp())
 
275
  log_on_azure(file, logs, share_client)
276
  return "Feedback submitted, thank you!"
277
 
278
+ # Function to log data on Azure
 
 
279
  def log_on_azure(file, logs, share_client):
280
  logs = json.dumps(logs)
281
  file_client = share_client.get_file_client(file)
 
290
  # --------------------------------------------------------------------
291
 
292
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
  def vote(data: gr.LikeData):
295
  if data.liked:
 
306
  return saved_graphs_state, gr.Button("Graph Saved")
307
 
308
 
309
+ # Functions to toggle visibility
310
+ def toggle_summary_visibility():
311
+ global summary_visible
312
+ summary_visible = not summary_visible
313
+ return gr.update(visible=summary_visible)
314
 
315
+ def toggle_relevant_visibility():
316
+ global relevant_visible
317
+ relevant_visible = not relevant_visible
318
+ return gr.update(visible=relevant_visible)
 
319
 
320
+ def change_completion_status(current_state):
321
+ current_state = 1 - current_state
322
+ return current_state
323
 
324
+ def update_sources_number_display(sources_textbox, figures_cards, current_graphs, papers_html):
325
+ sources_number = sources_textbox.count("<h2>")
326
+ figures_number = figures_cards.count("<h2>")
327
+ graphs_number = current_graphs.count("<iframe")
328
+ papers_number = papers_html.count("<h2>")
329
+ sources_notif_label = f"Sources ({sources_number})"
330
+ figures_notif_label = f"Figures ({figures_number})"
331
+ graphs_notif_label = f"Graphs ({graphs_number})"
332
+ papers_notif_label = f"Papers ({papers_number})"
333
+ recommended_content_notif_label = f"Recommended content ({figures_number + graphs_number + papers_number})"
 
 
 
 
 
334
 
335
+ return gr.update(label=recommended_content_notif_label), gr.update(label=sources_notif_label), gr.update(label=figures_notif_label), gr.update(label=graphs_notif_label), gr.update(label=papers_notif_label)
336
 
337
+ def change_sample_questions(key):
338
+ index = list(QUESTIONS.keys()).index(key)
339
+ visible_bools = [False] * len(samples)
340
+ visible_bools[index] = True
341
+ return [gr.update(visible=visible_bools[i]) for i in range(len(samples))]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
 
 
 
 
 
 
 
344
 
345
+ # Chat functions
346
+ def start_chat(query, history, search_only):
347
+ history = history + [ChatMessage(role="user", content=query)]
348
+ if not search_only:
349
+ return (gr.update(interactive=False), gr.update(selected=1), history, [])
350
+ else:
351
+ return (gr.update(interactive=False), gr.update(selected=2), history, [])
352
+
353
+ def finish_chat():
354
+ return gr.update(interactive=True, value="")
355
+
356
+ # Initialize visibility states
357
+ summary_visible = False
358
+ relevant_visible = False
359
+
360
+ # UI Layout Components
361
+ def create_chat_interface():
362
+ chatbot = gr.Chatbot(
363
+ value=[ChatMessage(role="assistant", content=init_prompt)],
364
+ type="messages",
365
+ show_copy_button=True,
366
+ show_label=False,
367
+ elem_id="chatbot",
368
+ layout="panel",
369
+ avatar_images=(None, "https://i.ibb.co/YNyd5W2/logo4.png"),
370
+ max_height="80vh",
371
+ height="100vh"
372
+ )
373
+
374
+ with gr.Row(elem_id="input-message"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
 
376
+ textbox = gr.Textbox(
377
+ placeholder="Ask me anything here!",
378
+ show_label=False,
379
+ scale=12,
380
+ lines=1,
381
+ interactive=True,
382
+ elem_id="input-textbox"
383
+ )
384
+
385
+ config_button = gr.Button("", elem_id="config-button")
386
+
387
+ return chatbot, textbox, config_button
388
+
389
+ def create_examples_tab():
390
+ examples_hidden = gr.Textbox(visible=False)
391
+ first_key = list(QUESTIONS.keys())[0]
392
+ dropdown_samples = gr.Dropdown(
393
+ choices=QUESTIONS.keys(),
394
+ value=first_key,
395
+ interactive=True,
396
+ label="Select a category of sample questions",
397
+ elem_id="dropdown-samples"
398
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
 
400
+ samples = []
401
+ for i, key in enumerate(QUESTIONS.keys()):
402
+ examples_visible = (i == 0)
403
+ with gr.Row(visible=examples_visible) as group_examples:
404
+ examples_questions = gr.Examples(
405
+ examples=QUESTIONS[key],
406
+ inputs=[examples_hidden],
407
+ examples_per_page=8,
408
+ run_on_click=False,
409
+ elem_id=f"examples{i}",
410
+ api_name=f"examples{i}"
411
+ )
412
+ samples.append(group_examples)
413
+
414
+ return examples_hidden, dropdown_samples, samples
415
 
416
+ def create_figures_tab():
417
+ sources_raw = gr.State()
418
+ new_figures = gr.State([])
419
+ used_figures = gr.State([])
420
+
421
+ with Modal(visible=False, elem_id="modal_figure_galery") as figure_modal:
422
+ gallery_component = gr.Gallery(
423
+ object_fit='scale-down',
424
+ elem_id="gallery-component",
425
+ height="80vh"
426
+ )
427
+
428
+ show_full_size_figures = gr.Button(
429
+ "Show figures in full size",
430
+ elem_id="show-figures",
431
+ interactive=True
432
+ )
433
+ show_full_size_figures.click(
434
+ lambda: Modal(visible=True),
435
+ None,
436
+ figure_modal
437
+ )
438
 
439
+ figures_cards = gr.HTML(show_label=False, elem_id="sources-figures")
440
+
441
+ return sources_raw, new_figures, used_figures, gallery_component, figures_cards, figure_modal
442
+
443
+ def create_papers_tab():
444
+ with gr.Accordion(
445
+ visible=True,
446
+ elem_id="papers-summary-popup",
447
+ label="See summary of relevant papers",
448
+ open=False
449
+ ) as summary_popup:
450
+ papers_summary = gr.Markdown("", visible=True, elem_id="papers-summary")
451
+
452
+ with gr.Accordion(
453
+ visible=True,
454
+ elem_id="papers-relevant-popup",
455
+ label="See relevant papers",
456
+ open=False
457
+ ) as relevant_popup:
458
+ papers_html = gr.HTML(show_label=False, elem_id="papers-textbox")
459
+
460
+ btn_citations_network = gr.Button("Explore papers citations network")
461
+ with Modal(visible=False) as papers_modal:
462
+ citations_network = gr.HTML(
463
+ "<h3>Citations Network Graph</h3>",
464
+ visible=True,
465
+ elem_id="papers-citations-network"
466
+ )
467
+ btn_citations_network.click(
468
+ lambda: Modal(visible=True),
469
+ None,
470
+ papers_modal
471
+ )
472
+
473
+ return papers_summary, papers_html, citations_network, papers_modal
474
+
475
+ def create_config_modal(config_open):
476
+ with Modal(visible=False, elem_id="modal-config") as config_modal:
477
+ gr.Markdown("Reminders: You can talk in any language, ClimateQ&A is multi-lingual!")
478
+
479
+ dropdown_sources = gr.CheckboxGroup(
480
+ choices=["IPCC", "IPBES", "IPOS"],
481
+ label="Select source (by default search in all sources)",
482
+ value=["IPCC"],
483
+ interactive=True
484
+ )
485
+
486
+ dropdown_reports = gr.Dropdown(
487
+ choices=POSSIBLE_REPORTS,
488
+ label="Or select specific reports",
489
+ multiselect=True,
490
+ value=None,
491
+ interactive=True
492
+ )
493
+
494
+ dropdown_external_sources = gr.CheckboxGroup(
495
+ choices=["Figures (IPCC/IPBES)", "Papers (OpenAlex)", "Graphs (OurWorldInData)"],
496
+ label="Select database to search for relevant content",
497
+ value=["Figures (IPCC/IPBES)"],
498
+ interactive=True
499
+ )
500
+
501
+ search_only = gr.Checkbox(
502
+ label="Search only for recommended content without chating",
503
+ value=False,
504
+ interactive=True,
505
+ elem_id="checkbox-chat"
506
+ )
507
+
508
+ dropdown_audience = gr.Dropdown(
509
+ choices=["Children", "General public", "Experts"],
510
+ label="Select audience",
511
+ value="Experts",
512
+ interactive=True
513
+ )
514
+
515
+ after = gr.Slider(
516
+ minimum=1950,
517
+ maximum=2023,
518
+ step=1,
519
+ value=1960,
520
+ label="Publication date",
521
+ show_label=True,
522
+ interactive=True,
523
+ elem_id="date-papers",
524
+ visible=False
525
+ )
526
+
527
+ output_query = gr.Textbox(
528
+ label="Query used for retrieval",
529
+ show_label=True,
530
+ elem_id="reformulated-query",
531
+ lines=2,
532
+ interactive=False,
533
+ visible=False
534
+ )
535
+
536
+ output_language = gr.Textbox(
537
+ label="Language",
538
+ show_label=True,
539
+ elem_id="language",
540
+ lines=1,
541
+ interactive=False,
542
+ visible=False
543
+ )
544
+
545
+ dropdown_external_sources.change(
546
+ lambda x: gr.update(visible="Papers (OpenAlex)" in x),
547
+ inputs=[dropdown_external_sources],
548
+ outputs=[after]
549
+ )
550
+
551
+ close_config_modal = gr.Button("Validate and Close", elem_id="close-config-modal")
552
+ close_config_modal.click(
553
+ fn=update_config_modal_visibility,
554
+ inputs=[config_open],
555
+ outputs=[config_modal, config_open]
556
+ )
557
+
558
+ return (config_modal, dropdown_sources, dropdown_reports, dropdown_external_sources,
559
+ search_only, dropdown_audience, after, output_query, output_language)
560
 
561
+ # Main UI Assembly
562
+ with gr.Blocks(title="Climate Q&A", css_paths=os.getcwd()+ "/style.css", theme=theme, elem_id="main-component") as demo:
563
+ # State variables
564
+ chat_completed_state = gr.State(0)
565
+ current_graphs = gr.State([])
566
+ saved_graphs = gr.State({})
567
+ config_open = gr.State(False)
568
 
569
+ with gr.Tab("ClimateQ&A"):
570
+ with gr.Row(elem_id="chatbot-row"):
571
+ # Left column - Chat interface
572
+ with gr.Column(scale=2):
573
+ chatbot, textbox, config_button = create_chat_interface()
574
 
575
+ # Right column - Content panels
576
+ with gr.Column(scale=2, variant="panel", elem_id="right-panel"):
577
+ with gr.Tabs(elem_id="right_panel_tab") as tabs:
578
+ # Examples tab
579
+ with gr.TabItem("Examples", elem_id="tab-examples", id=0):
580
+ examples_hidden, dropdown_samples, samples = create_examples_tab()
581
 
582
+ # Sources tab
583
+ with gr.Tab("Sources", elem_id="tab-sources", id=1) as tab_sources:
584
+ sources_textbox = gr.HTML(show_label=False, elem_id="sources-textbox")
 
 
 
 
585
 
586
+ # Recommended content tab
587
+ with gr.Tab("Recommended content", elem_id="tab-recommended_content", id=2) as tab_recommended_content:
588
+ with gr.Tabs(elem_id="group-subtabs") as tabs_recommended_content:
589
+ # Figures subtab
590
+ with gr.Tab("Figures", elem_id="tab-figures", id=3) as tab_figures:
591
+ sources_raw, new_figures, used_figures, gallery_component, figures_cards, figure_modal = create_figures_tab()
592
 
593
+ # Papers subtab
594
+ with gr.Tab("Papers", elem_id="tab-citations", id=4) as tab_papers:
595
+ papers_summary, papers_html, citations_network, papers_modal = create_papers_tab()
596
 
597
+ # Graphs subtab
598
+ with gr.Tab("Graphs", elem_id="tab-graphs", id=5) as tab_graphs:
599
+ graphs_container = gr.HTML(
600
+ "<h2>There are no graphs to be displayed at the moment. Try asking another question.</h2>",
601
+ elem_id="graphs-container"
602
+ )
603
+ current_graphs.change(
604
+ lambda x: x,
605
+ inputs=[current_graphs],
606
+ outputs=[graphs_container]
607
+ )
608
 
609
+
610
 
611
+ # Other tabs
612
+ with gr.Tab("About", elem_classes="max-height other-tabs"):
613
  with gr.Row():
614
  with gr.Column(scale=1):
 
 
 
 
615
  gr.Markdown(
616
  """
617
  ### More info
 
621
  ### Citation
622
  """
623
  )
624
+ with gr.Accordion(CITATION_LABEL, elem_id="citation", open=False):
 
625
  gr.Textbox(
626
  value=CITATION_TEXT,
627
  label="",
 
630
  lines=len(CITATION_TEXT.split('\n')),
631
  )
632
 
633
+ # Event handlers
634
+ config_modal, dropdown_sources, dropdown_reports, dropdown_external_sources, search_only, dropdown_audience, after, output_query, output_language = create_config_modal(config_open)
 
 
 
 
 
 
 
 
 
635
 
636
+ config_button.click(
637
+ fn=update_config_modal_visibility,
638
+ inputs=[config_open],
639
+ outputs=[config_modal, config_open]
640
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
 
 
 
 
 
 
 
 
 
 
 
 
 
642
 
643
  (textbox
644
+ .submit(start_chat, [textbox, chatbot, search_only], [textbox, tabs, chatbot, sources_raw], queue=False, api_name="start_chat_textbox")
645
+ .then(chat, [textbox, chatbot, dropdown_audience, dropdown_sources, dropdown_reports, dropdown_external_sources, search_only], [chatbot, sources_textbox, output_query, output_language, new_figures, current_graphs], concurrency_limit=8, api_name="chat_textbox")
646
+ .then(finish_chat, None, [textbox], api_name="finish_chat_textbox")
 
 
 
 
 
 
 
 
 
647
  )
648
 
649
 
650
 
651
  (examples_hidden
652
+ .change(start_chat, [examples_hidden, chatbot, search_only], [textbox, tabs, chatbot, sources_raw], queue=False, api_name="start_chat_examples")
653
+ .then(chat, [examples_hidden, chatbot, dropdown_audience, dropdown_sources, dropdown_reports, dropdown_external_sources, search_only], [chatbot, sources_textbox, output_query, output_language, new_figures, current_graphs], concurrency_limit=8, api_name="chat_textbox")
654
+ .then(finish_chat, None, [textbox], api_name="finish_chat_examples")
 
655
  )
656
 
 
 
 
 
 
 
 
 
657
  new_figures.change(process_figures, inputs=[sources_raw, new_figures], outputs=[sources_raw, figures_cards, gallery_component])
 
 
 
 
 
 
658
 
659
+ # Update sources numbers
660
+ sources_textbox.change(update_sources_number_display, [sources_textbox, figures_cards, current_graphs, papers_html], [tab_recommended_content, tab_sources, tab_figures, tab_graphs, tab_papers])
661
+ figures_cards.change(update_sources_number_display, [sources_textbox, figures_cards, current_graphs, papers_html], [tab_recommended_content, tab_sources, tab_figures, tab_graphs, tab_papers])
662
+ current_graphs.change(update_sources_number_display, [sources_textbox, figures_cards, current_graphs, papers_html], [tab_recommended_content, tab_sources, tab_figures, tab_graphs, tab_papers])
663
+ papers_html.change(update_sources_number_display, [sources_textbox, figures_cards, current_graphs, papers_html], [tab_recommended_content, tab_sources, tab_figures, tab_graphs, tab_papers])
664
 
665
+ # Other questions examples
666
+ dropdown_samples.change(change_sample_questions, dropdown_samples, samples)
 
667
 
668
+ # Search for papers
669
+ textbox.submit(find_papers, [textbox, after, dropdown_external_sources], [papers_html, citations_network, papers_summary])
670
+ examples_hidden.change(find_papers, [examples_hidden, after, dropdown_external_sources], [papers_html, citations_network, papers_summary])
671
 
672
  demo.queue()
673
+
 
 
674
  demo.launch(ssr_mode=False)
climateqa/engine/chains/retrieve_documents.py CHANGED
@@ -213,20 +213,10 @@ async def retrieve_documents(state,config, vectorstore,reranker,llm,rerank_by_qu
213
  dict: The updated state containing the retrieved and reranked documents, related content, and remaining questions.
214
  """
215
  print("---- Retrieve documents ----")
 
 
216
 
217
- # Get the documents from the state
218
- if "documents" in state and state["documents"] is not None:
219
- docs = state["documents"]
220
- else:
221
- docs = []
222
-
223
- # Get the related_content from the state
224
- if "related_content" in state and state["related_content"] is not None:
225
- related_content = state["related_content"]
226
- else:
227
- related_content = []
228
-
229
- search_figures = "Figures (IPCC/IPBES)" in state["relevant_content_sources"]
230
  search_only = state["search_only"]
231
 
232
  reports = state["reports"]
 
213
  dict: The updated state containing the retrieved and reranked documents, related content, and remaining questions.
214
  """
215
  print("---- Retrieve documents ----")
216
+ docs = state.get("documents", [])
217
+ related_content = state.get("related_content", [])
218
 
219
+ search_figures = "Figures (IPCC/IPBES)" in state["relevant_content_sources_selection"]
 
 
 
 
 
 
 
 
 
 
 
 
220
  search_only = state["search_only"]
221
 
222
  reports = state["reports"]
climateqa/engine/chains/retrieve_papers.py CHANGED
@@ -32,8 +32,8 @@ def generate_keywords(query):
32
  return keywords
33
 
34
 
35
- async def find_papers(query,after, relevant_content_sources, reranker= reranker):
36
- if "Papers (OpenAlex)" in relevant_content_sources:
37
  summary = ""
38
  keywords = generate_keywords(query)
39
  df_works = oa.search(keywords,after = after)
 
32
  return keywords
33
 
34
 
35
+ async def find_papers(query,after, relevant_content_sources_selection, reranker= reranker):
36
+ if "Papers (OpenAlex)" in relevant_content_sources_selection:
37
  summary = ""
38
  keywords = generate_keywords(query)
39
  df_works = oa.search(keywords,after = after)
climateqa/engine/graph.py CHANGED
@@ -36,12 +36,12 @@ class GraphState(TypedDict):
36
  answer: str
37
  audience: str = "experts"
38
  sources_input: List[str] = ["IPCC","IPBES"]
39
- relevant_content_sources: List[str] = ["Figures (IPCC/IPBES)"]
40
  sources_auto: bool = True
41
  min_year: int = 1960
42
  max_year: int = None
43
  documents: List[Document]
44
- related_contents : Dict[str,Document]
45
  recommended_content : List[Document]
46
  search_only : bool = False
47
  reports : List[str] = []
@@ -159,7 +159,7 @@ def make_graph_agent(llm, vectorstore_ipcc, vectorstore_graphs, reranker, thresh
159
  )
160
  workflow.add_conditional_edges(
161
  "transform_query",
162
- lambda state : "retrieve_graphs" if "Graphs (OurWorldInData)" in state["relevant_content_sources"] else END,
163
  make_id_dict(["retrieve_graphs", END])
164
  )
165
 
 
36
  answer: str
37
  audience: str = "experts"
38
  sources_input: List[str] = ["IPCC","IPBES"]
39
+ relevant_content_sources_selection: List[str] = ["Figures (IPCC/IPBES)"]
40
  sources_auto: bool = True
41
  min_year: int = 1960
42
  max_year: int = None
43
  documents: List[Document]
44
+ related_contents : List[Document]
45
  recommended_content : List[Document]
46
  search_only : bool = False
47
  reports : List[str] = []
 
159
  )
160
  workflow.add_conditional_edges(
161
  "transform_query",
162
+ lambda state : "retrieve_graphs" if "Graphs (OurWorldInData)" in state["relevant_content_sources_selection"] else END,
163
  make_id_dict(["retrieve_graphs", END])
164
  )
165
 
style.css CHANGED
@@ -1,13 +1,28 @@
1
-
2
  /* :root {
3
  --user-image: url('https://ih1.redbubble.net/image.4776899543.6215/st,small,507x507-pad,600x600,f8f8f8.jpg');
4
- } */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- #tab-recommended_content{
7
- padding-top: 0px;
8
- padding-left : 0px;
9
- padding-right: 0px;
10
  }
 
11
  #group-subtabs {
12
  /* display: block; */
13
  position : sticky;
@@ -16,66 +31,97 @@
16
 
17
  }
18
 
 
 
 
19
 
20
- #papers-summary-popup button span{
21
- /* make label of accordio in bold, center, and bigger */
22
- font-size: 16px;
23
  font-weight: bold;
24
- text-align: center;
 
25
 
 
 
26
  }
27
 
28
- #papers-relevant-popup span{
29
- /* make label of accordio in bold, center, and bigger */
30
- font-size: 16px;
31
- font-weight: bold;
32
- text-align: center;
33
  }
34
 
 
 
 
 
 
 
 
 
 
 
35
 
 
 
 
36
 
37
- #tab-citations .button{
38
- padding: 12px 16px;
39
- font-size: 16px;
40
  font-weight: bold;
41
- cursor: pointer;
42
- border: none;
43
- outline: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  text-align: left;
45
- transition: background-color 0.3s ease;
46
  }
47
 
 
 
 
48
 
49
- .gradio-container {
50
- width: 100%!important;
51
- max-width: 100% !important;
52
  }
53
 
54
- /* fix for huggingface infinite growth*/
55
- main.flex.flex-1.flex-col {
56
- max-height: 95vh !important;
57
  }
58
 
59
- button#show-figures{
60
- /* Base styles */
61
- background-color: #f5f5f5;
62
- border: 1px solid #e0e0e0;
63
- border-radius: 4px;
64
- color: #333333;
65
- cursor: pointer;
66
- width: 100%;
67
- text-align: center;
68
  }
69
 
70
- .avatar-container.svelte-1x5p6hu:not(.thumbnail-item) img {
71
- width: 100%;
72
- height: 100%;
73
- object-fit: cover;
74
- border-radius: 50%;
75
- padding: 0px;
76
- margin: 0px;
77
  }
78
 
 
79
  .warning-box {
80
  background-color: #fff3cd;
81
  border: 1px solid #ffeeba;
@@ -85,32 +131,20 @@ button#show-figures{
85
  color: #856404;
86
  display: inline-block;
87
  margin-bottom: 15px;
88
- }
89
-
90
 
91
  .tip-box {
92
  background-color: #f0f9ff;
93
  border: 1px solid #80d4fa;
94
  border-radius: 4px;
95
- margin-top:20px;
96
  padding: 15px 20px;
97
  font-size: 14px;
98
  display: inline-block;
99
- margin-bottom: 15px;
100
  width: auto;
101
- color:black !important;
102
- }
103
-
104
- body.dark .warning-box * {
105
- color:black !important;
106
  }
107
 
108
-
109
- body.dark .tip-box * {
110
- color:black !important;
111
- }
112
-
113
-
114
  .tip-box-title {
115
  font-weight: bold;
116
  font-size: 14px;
@@ -122,116 +156,128 @@ body.dark .tip-box * {
122
  margin-right: 5px;
123
  }
124
 
125
- .gr-box {border-color: #d6c37c}
126
-
127
- #hidden-message{
128
- display:none;
 
 
 
 
 
 
 
129
  }
130
 
131
- .message{
132
- font-size:14px !important;
133
-
134
- }
135
- .card-content img {
136
- display: block;
137
- margin: auto;
138
- max-width: 100%; /* Ensures the image is responsive */
139
- height: auto;
140
  }
141
 
142
- a {
143
- text-decoration: none;
144
- color: inherit;
 
 
 
 
145
  }
146
 
147
- .doc-ref sup{
148
- color:#dc2626!important;
149
- /* margin-right:1px; */
150
  }
151
 
 
 
 
 
152
 
153
- .card {
154
- background-color: white;
155
- border-radius: 10px;
156
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
157
- overflow: hidden;
158
- display: flex;
159
- flex-direction: column;
160
- margin:20px;
161
  }
162
 
163
- .card-content {
164
- padding: 20px;
 
 
165
  }
166
 
167
- .card-content h2 {
168
- font-size: 14px !important;
169
- font-weight: bold;
170
- margin-bottom: 10px;
171
- margin-top:0px !important;
172
- color:#dc2626!important;;
173
  }
174
 
175
- .card-content p {
176
- font-size: 12px;
177
- margin-bottom: 0;
 
 
178
  }
179
 
180
- .card-footer {
181
- background-color: #f4f4f4;
182
- font-size: 10px;
183
  padding: 10px;
 
 
184
  display: flex;
185
- justify-content: space-between;
186
  align-items: center;
 
 
187
  }
188
 
189
- .card-footer span {
190
- flex-grow: 1;
191
- text-align: left;
192
- color: #999 !important;
 
 
 
 
 
193
  }
194
 
195
- .pdf-link {
196
- display: inline-flex;
197
- align-items: center;
198
- margin-left: auto;
199
- text-decoration: none!important;
200
- font-size: 14px;
 
 
 
201
  }
202
 
203
-
204
-
205
- .message.user{
206
- /* background-color:#7494b0 !important; */
207
- border:none;
208
- /* color:white!important; */
209
  }
210
 
211
- .message.bot{
212
- /* background-color:#f2f2f7 !important; */
213
- border:none;
214
  }
215
 
216
-
217
- label.selected{
218
- background: #93c5fd !important;
219
  }
220
 
221
- #submit-button{
222
- padding:0px !important;
223
  }
224
 
225
- #modal-config .block.modal-block.padded {
226
- padding-top: 25px;
227
- height: 100vh;
228
-
229
- }
230
- #modal-config .modal-container{
231
- margin: 0px;
232
- padding: 0px;
233
  }
234
- /* Modal styles */
 
235
  #modal-config {
236
  position: fixed;
237
  top: 0;
@@ -244,28 +290,23 @@ label.selected{
244
  padding: 15px;
245
  transform: none;
246
  }
247
- #modal-config .close{
248
- display: none;
 
 
249
  }
250
 
251
- /* Push main content to the right when modal is open */
252
- /* .modal ~ * {
253
- margin-left: 300px;
254
- transition: margin-left 0.3s ease;
255
- } */
256
 
257
- #modal-config .modal .wrap ul{
258
- position:static;
259
- top: 100%;
260
- left: 0;
261
- /* min-height: 100px; */
262
- height: 100%;
263
- /* margin-top: 0; */
264
- z-index: 9999;
265
- pointer-events: auto;
266
- height: 200px;
267
  }
268
- #config-button{
 
 
269
  background: none;
270
  border: none;
271
  padding: 8px;
@@ -288,155 +329,231 @@ label.selected{
288
  background-color: rgba(0, 0, 0, 0.1);
289
  }
290
 
291
- #checkbox-config{
292
- display: block;
293
- position: absolute;
294
- background: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  border: none;
296
- padding: 8px;
 
 
 
 
 
 
 
 
 
 
297
  cursor: pointer;
298
- width: 40px;
299
- height: 40px;
300
- display: flex;
301
- align-items: center;
302
- justify-content: center;
303
- border-radius: 50%;
304
- transition: background-color 0.2s;
305
- font-size: 20px;
306
  text-align: center;
307
  }
308
- #checkbox-config:checked{
309
- display: block;
 
 
310
  }
311
 
 
 
 
 
312
 
 
 
 
 
313
 
314
- @media screen and (min-width: 1024px) {
315
- /* Additional style for scrollable tab content */
316
- /* div#tab-recommended_content {
317
- overflow-y: auto;
318
- max-height: 80vh;
319
- } */
320
 
321
- .gradio-container {
322
- max-height: calc(100vh - 190px) !important;
323
- overflow: hidden;
324
- }
325
- /* div#chatbot{
326
- height:calc(100vh - 170px) !important;
327
- max-height:calc(100vh - 170px) !important;
328
 
329
- } */
 
 
 
 
330
 
 
 
 
 
331
 
332
-
333
- div#tab-examples{
334
- height:calc(100vh - 190px) !important;
335
- overflow-y: scroll !important;
336
- /* overflow-y: auto; */
337
- }
338
 
339
- div#sources-textbox{
340
- height:calc(100vh - 190px) !important;
341
- overflow-y: scroll !important;
342
- /* overflow-y: auto !important; */
343
- }
344
- div#graphs-container{
345
- height:calc(100vh - 210px) !important;
346
- overflow-y: scroll !important;
347
- }
348
 
349
- div#sources-figures{
350
- height:calc(100vh - 300px) !important;
351
- max-height: 90vh !important;
352
- overflow-y: scroll !important;
353
- }
354
 
355
- div#graphs-container{
356
- height:calc(100vh - 300px) !important;
357
- max-height: 90vh !important;
358
- overflow-y: scroll !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  }
360
 
361
- div#tab-citations{
362
- height:calc(100vh - 300px) !important;
363
- max-height: 90vh !important;
 
364
  overflow-y: scroll !important;
365
  }
366
 
367
- div#tab-config{
368
- height:calc(100vh - 190px) !important;
 
 
 
369
  overflow-y: scroll !important;
370
- /* overflow-y: auto !important; */
371
  }
372
 
373
- /* Force container to respect height limits */
374
- .main-component{
375
- contain: size layout;
376
- overflow: hidden;
377
  }
378
 
379
-
380
- div#chatbot-row{
381
- max-height:calc(100vh - 90px) !important;
382
  }
383
- /*
384
 
385
-
386
- .max-height{
387
- height:calc(100vh - 90px) !important;
388
- max-height:calc(100vh - 90px) !important;
389
  overflow-y: auto;
 
390
  }
391
- */
392
-
393
  }
394
 
395
- footer {
396
- visibility: hidden;
397
- display:none !important;
398
- }
399
-
400
-
401
  @media screen and (max-width: 767px) {
402
- /* Your mobile-specific styles go here */
403
-
404
- div#chatbot{
405
- height:500px !important;
406
  }
407
 
408
- #submit-button{
409
- padding:0px !important;
410
  min-width: 80px;
411
  }
412
 
413
- /* This will hide all list items */
414
  div.tab-nav button {
415
  display: none !important;
416
  }
417
 
418
- /* This will show only the first list item */
419
- div.tab-nav button:first-child {
420
- display: block !important;
421
- }
422
-
423
- /* This will show only the first list item */
424
  div.tab-nav button:nth-child(2) {
425
  display: block !important;
426
  }
427
-
428
- #right-panel button{
429
  display: block !important;
430
  }
431
 
432
- /* ... add other mobile-specific styles ... */
 
 
 
 
 
 
 
 
433
  }
434
 
 
435
  @media (prefers-color-scheme: dark) {
436
- .card{
437
  background-color: #374151;
438
  }
439
- .card-image > .card-content{
 
440
  background-color: rgb(55, 65, 81) !important;
441
  }
442
 
@@ -444,294 +561,48 @@ footer {
444
  background-color: #404652;
445
  }
446
 
447
- .container > .wrap{
448
  background-color: #374151 !important;
449
- color:white !important;
450
- }
451
- .card-content h2{
452
- color:#e7754f !important;
453
  }
454
- .doc-ref sup{
455
- color:rgb(235 109 35)!important;
456
- /* margin-right:1px; */
457
  }
 
458
  .card-footer span {
459
- color:white !important;
460
  }
461
-
462
- }
463
-
464
-
465
- .doc-ref{
466
- color:#dc2626!important;
467
- margin-right:1px;
468
- }
469
-
470
- .tabitem{
471
- border:none !important;
472
- }
473
-
474
- .other-tabs > div{
475
- padding-left:40px;
476
- padding-right:40px;
477
- padding-top:10px;
478
- }
479
-
480
- .gallery-item > div{
481
- white-space: normal !important; /* Allow the text to wrap */
482
- word-break: break-word !important; /* Break words to prevent overflow */
483
- overflow-wrap: break-word !important; /* Break long words if necessary */
484
- }
485
-
486
- span.chatbot > p > img{
487
- margin-top:40px !important;
488
- max-height: none !important;
489
- max-width: 80% !important;
490
- border-radius:0px !important;
491
- }
492
-
493
-
494
- .chatbot-caption{
495
- font-size:11px;
496
- font-style:italic;
497
- color:#508094;
498
- }
499
-
500
- .ai-generated{
501
- font-size:11px!important;
502
- font-style:italic;
503
- color:#73b8d4 !important;
504
- }
505
 
506
- .card-image > .card-content{
507
- background-color:#f1f7fa;
508
- }
509
-
510
-
511
-
512
- .tab-nav > button.selected{
513
- color:#4b8ec3;
514
- font-weight:bold;
515
- border:none;
516
- }
517
-
518
- .tab-nav{
519
- border:none !important;
520
- }
521
-
522
- #input-textbox > label > textarea{
523
- border-radius:40px;
524
- padding-left:30px;
525
- resize:none;
526
- }
527
-
528
- #input-message > div{
529
- border:none;
530
- }
531
-
532
- #dropdown-samples{
533
-
534
- background:none !important;
535
-
536
- }
537
-
538
- #dropdown-samples > .container > .wrap{
539
- background-color:white;
540
- }
541
-
542
-
543
- #tab-examples > div > .form{
544
- border:none;
545
- background:none !important;
546
- }
547
 
548
- .a-doc-ref{
549
- text-decoration: none !important;
 
550
  }
551
 
552
-
553
- .dropdown {
554
- position: relative;
555
- display:inline-block;
556
- margin-bottom: 10px;
557
- }
558
-
559
- .dropdown-toggle {
560
- background-color: #f2f2f2;
561
- color: black;
562
- padding: 10px;
563
- font-size: 16px;
564
- cursor: pointer;
565
- display: block;
566
- width: 400px; /* Adjust width as needed */
567
- position: relative;
568
- display: flex;
569
- align-items: center; /* Vertically center the contents */
570
- justify-content: left;
571
- }
572
-
573
- .dropdown-toggle .caret {
574
- content: "";
575
- position: absolute;
576
- right: 10px;
577
- top: 50%;
578
- border-left: 5px solid transparent;
579
- border-right: 5px solid transparent;
580
- border-top: 5px solid black;
581
- transform: translateY(-50%);
582
- }
583
-
584
- input[type="checkbox"] {
585
- display: none !important;
586
- }
587
-
588
- input[type="checkbox"]:checked + .dropdown-content {
589
  display: block;
590
- }
591
-
592
- #checkbox-chat input[type="checkbox"] {
593
- display: flex !important;
594
- }
595
-
596
- .dropdown-content {
597
- display: none;
598
  position: absolute;
599
- background-color: #f9f9f9;
600
- min-width: 300px;
601
- box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
602
- z-index: 1;
603
- padding: 12px;
604
- border: 1px solid #ccc;
605
- }
606
-
607
- input[type="checkbox"]:checked + .dropdown-toggle + .dropdown-content {
608
- display: block;
609
- }
610
-
611
- input[type="checkbox"]:checked + .dropdown-toggle .caret {
612
- border-top: 0;
613
- border-bottom: 5px solid black;
614
- }
615
-
616
- .loader {
617
- border: 1px solid #d0d0d0 !important; /* Light grey background */
618
- border-top: 1px solid #db3434 !important; /* Blue color */
619
- border-right: 1px solid #3498db !important; /* Blue color */
620
  border-radius: 50%;
621
- width: 20px;
622
- height: 20px;
623
- animation: spin 2s linear infinite;
624
- display:inline-block;
625
- margin-right:10px !important;
626
- }
627
-
628
- .checkmark{
629
- color:green !important;
630
- font-size:18px;
631
- margin-right:10px !important;
632
- }
633
-
634
- @keyframes spin {
635
- 0% { transform: rotate(0deg); }
636
- 100% { transform: rotate(360deg); }
637
- }
638
-
639
-
640
- .relevancy-score{
641
- margin-top:10px !important;
642
- font-size:10px !important;
643
- font-style:italic;
644
- }
645
-
646
- .score-green{
647
- color:green !important;
648
- }
649
-
650
- .score-orange{
651
- color:orange !important;
652
- }
653
-
654
- .score-red{
655
- color:red !important;
656
- }
657
-
658
- /* Mobile specific adjustments */
659
- @media screen and (max-width: 767px) {
660
- div#tab-recommended_content {
661
- max-height: 50vh; /* Reduce height for smaller screens */
662
- overflow-y: auto;
663
- }
664
- }
665
-
666
- /* Additional style for scrollable tab content */
667
- div#tab-saved-graphs {
668
- overflow-y: auto; /* Enable vertical scrolling */
669
- max-height: 80vh; /* Adjust height as needed */
670
- }
671
-
672
- /* Mobile specific adjustments */
673
- @media screen and (max-width: 767px) {
674
- div#tab-saved-graphs {
675
- max-height: 50vh; /* Reduce height for smaller screens */
676
- overflow-y: auto;
677
- }
678
- }
679
- .message-buttons-left.panel.message-buttons.with-avatar {
680
- display: none;
681
- }
682
-
683
-
684
- /* Specific fixes for Hugging Face Space iframe */
685
- .h-full {
686
- height: auto !important;
687
- min-height: 0 !important;
688
  }
689
 
690
- .space-content {
691
- height: auto !important;
692
- max-height: 100vh !important;
693
- overflow: hidden;
694
  }
695
-
696
-
697
- /* Mobile specific modal configuration */
698
- @media screen and (max-width: 767px) {
699
- #modal-config {
700
- width: 100%; /* Full width on mobile */
701
- height: 100vh;
702
- left: 0;
703
- top: 0;
704
- padding: 10px; /* Reduced padding for mobile */
705
- }
706
-
707
- #modal-config .block.modal-block.padded {
708
- padding-top: 15px; /* Reduced top padding */
709
- height: 100vh;
710
- overflow-y: auto; /* Enable scrolling */
711
- }
712
-
713
- #modal-config .modal-container {
714
- width: 100%;
715
- height: 100%;
716
- }
717
-
718
- /* Show close button on mobile */
719
- #modal-config .close {
720
- display: block;
721
- position: absolute;
722
- top: 10px;
723
- right: 10px;
724
- z-index: 1001;
725
- padding: 8px;
726
- font-size: 24px;
727
- background: none;
728
- border: none;
729
- cursor: pointer;
730
- }
731
-
732
- /* Ensure modal content is scrollable on mobile */
733
- #modal-config .modal .wrap ul {
734
- max-height: calc(100vh - 60px); /* Account for header space */
735
- overflow-y: auto;
736
- }
737
- }
 
1
+ /* Root Variables */
2
  /* :root {
3
  --user-image: url('https://ih1.redbubble.net/image.4776899543.6215/st,small,507x507-pad,600x600,f8f8f8.jpg');
4
+ } */
5
+
6
+ /* Layout & Container Styles */
7
+ .gradio-container {
8
+ width: 100% !important;
9
+ max-width: 100% !important;
10
+ }
11
+
12
+ main.flex.flex-1.flex-col {
13
+ max-height: 95vh !important;
14
+ }
15
+
16
+ .main-component {
17
+ contain: size layout;
18
+ overflow: hidden;
19
+ }
20
 
21
+ /* Tab Styles */
22
+ #tab-recommended_content {
23
+ padding: 0;
 
24
  }
25
+
26
  #group-subtabs {
27
  /* display: block; */
28
  position : sticky;
 
31
 
32
  }
33
 
34
+ .tab-nav {
35
+ border: none !important;
36
+ }
37
 
38
+ .tab-nav > button.selected {
39
+ color: #4b8ec3;
 
40
  font-weight: bold;
41
+ border: none;
42
+ }
43
 
44
+ .tabitem {
45
+ border: none !important;
46
  }
47
 
48
+ .other-tabs > div {
49
+ padding: 40px 40px 10px;
 
 
 
50
  }
51
 
52
+ /* Card Styles */
53
+ .card {
54
+ background-color: white;
55
+ border-radius: 10px;
56
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
57
+ overflow: hidden;
58
+ display: flex;
59
+ flex-direction: column;
60
+ margin: 20px;
61
+ }
62
 
63
+ .card-content {
64
+ padding: 20px;
65
+ }
66
 
67
+ .card-content h2 {
68
+ font-size: 14px !important;
 
69
  font-weight: bold;
70
+ margin: 0 0 10px !important;
71
+ color: #dc2626 !important;
72
+ }
73
+
74
+ .card-content p {
75
+ font-size: 12px;
76
+ margin-bottom: 0;
77
+ }
78
+
79
+ .card-content img {
80
+ display: block;
81
+ margin: auto;
82
+ max-width: 100%;
83
+ height: auto;
84
+ }
85
+
86
+ .card-footer {
87
+ background-color: #f4f4f4;
88
+ font-size: 10px;
89
+ padding: 10px;
90
+ display: flex;
91
+ justify-content: space-between;
92
+ align-items: center;
93
+ }
94
+
95
+ .card-footer span {
96
+ flex-grow: 1;
97
  text-align: left;
98
+ color: #999 !important;
99
  }
100
 
101
+ .card-image > .card-content {
102
+ background-color: #f1f7fa;
103
+ }
104
 
105
+ /* Message & Chat Styles */
106
+ .message {
107
+ font-size: 14px !important;
108
  }
109
 
110
+ .message.user, .message.bot {
111
+ border: none;
 
112
  }
113
 
114
+ #input-textbox > label > textarea {
115
+ border-radius: 40px;
116
+ padding-left: 30px;
117
+ resize: none;
 
 
 
 
 
118
  }
119
 
120
+ #input-message > div {
121
+ border: none;
 
 
 
 
 
122
  }
123
 
124
+ /* Alert Boxes */
125
  .warning-box {
126
  background-color: #fff3cd;
127
  border: 1px solid #ffeeba;
 
131
  color: #856404;
132
  display: inline-block;
133
  margin-bottom: 15px;
134
+ }
 
135
 
136
  .tip-box {
137
  background-color: #f0f9ff;
138
  border: 1px solid #80d4fa;
139
  border-radius: 4px;
140
+ margin: 20px 0 15px;
141
  padding: 15px 20px;
142
  font-size: 14px;
143
  display: inline-block;
 
144
  width: auto;
145
+ color: black !important;
 
 
 
 
146
  }
147
 
 
 
 
 
 
 
148
  .tip-box-title {
149
  font-weight: bold;
150
  font-size: 14px;
 
156
  margin-right: 5px;
157
  }
158
 
159
+ /* Loader Animation */
160
+ .loader {
161
+ border: 1px solid #d0d0d0 !important;
162
+ border-top: 1px solid #db3434 !important;
163
+ border-right: 1px solid #3498db !important;
164
+ border-radius: 50%;
165
+ width: 20px;
166
+ height: 20px;
167
+ animation: spin 2s linear infinite;
168
+ display: inline-block;
169
+ margin-right: 10px !important;
170
  }
171
 
172
+ @keyframes spin {
173
+ 0% { transform: rotate(0deg); }
174
+ 100% { transform: rotate(360deg); }
 
 
 
 
 
 
175
  }
176
 
177
+ /* PDF Link Styles */
178
+ .pdf-link {
179
+ display: inline-flex;
180
+ align-items: center;
181
+ margin-left: auto;
182
+ text-decoration: none!important;
183
+ font-size: 14px;
184
  }
185
 
186
+ /* Document Reference Styles */
187
+ .doc-ref sup {
188
+ color: #dc2626!important;
189
  }
190
 
191
+ .doc-ref {
192
+ color: #dc2626!important;
193
+ margin-right: 1px;
194
+ }
195
 
196
+ /* Chatbot & Image Styles */
197
+ span.chatbot > p > img {
198
+ margin-top: 40px !important;
199
+ max-height: none !important;
200
+ max-width: 80% !important;
201
+ border-radius: 0px !important;
 
 
202
  }
203
 
204
+ .chatbot-caption {
205
+ font-size: 11px;
206
+ font-style: italic;
207
+ color: #508094;
208
  }
209
 
210
+ .ai-generated {
211
+ font-size: 11px!important;
212
+ font-style: italic;
213
+ color: #73b8d4 !important;
 
 
214
  }
215
 
216
+ /* Dropdown Styles */
217
+ .dropdown {
218
+ position: relative;
219
+ display: inline-block;
220
+ margin-bottom: 10px;
221
  }
222
 
223
+ .dropdown-toggle {
224
+ background-color: #f2f2f2;
225
+ color: black;
226
  padding: 10px;
227
+ font-size: 16px;
228
+ cursor: pointer;
229
  display: flex;
230
+ width: 400px;
231
  align-items: center;
232
+ justify-content: left;
233
+ position: relative;
234
  }
235
 
236
+ .dropdown-toggle .caret {
237
+ content: "";
238
+ position: absolute;
239
+ right: 10px;
240
+ top: 50%;
241
+ border-left: 5px solid transparent;
242
+ border-right: 5px solid transparent;
243
+ border-top: 5px solid black;
244
+ transform: translateY(-50%);
245
  }
246
 
247
+ .dropdown-content {
248
+ display: none;
249
+ position: absolute;
250
+ background-color: #f9f9f9;
251
+ min-width: 300px;
252
+ box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
253
+ z-index: 1;
254
+ padding: 12px;
255
+ border: 1px solid #ccc;
256
  }
257
 
258
+ /* Checkbox Styles */
259
+ input[type="checkbox"] {
260
+ display: none !important;
 
 
 
261
  }
262
 
263
+ #checkbox-chat input[type="checkbox"] {
264
+ display: flex !important;
 
265
  }
266
 
267
+ input[type="checkbox"]:checked + .dropdown-content {
268
+ display: block;
 
269
  }
270
 
271
+ input[type="checkbox"]:checked + .dropdown-toggle + .dropdown-content {
272
+ display: block;
273
  }
274
 
275
+ input[type="checkbox"]:checked + .dropdown-toggle .caret {
276
+ border-top: 0;
277
+ border-bottom: 5px solid black;
 
 
 
 
 
278
  }
279
+
280
+ /* Modal Styles */
281
  #modal-config {
282
  position: fixed;
283
  top: 0;
 
290
  padding: 15px;
291
  transform: none;
292
  }
293
+
294
+ #modal-config .block.modal-block.padded {
295
+ padding-top: 25px;
296
+ height: 100vh;
297
  }
298
 
299
+ #modal-config .modal-container {
300
+ margin: 0px;
301
+ padding: 0px;
302
+ }
 
303
 
304
+ #modal-config .close {
305
+ display: none;
 
 
 
 
 
 
 
 
306
  }
307
+
308
+ /* Config Button Styles */
309
+ #config-button {
310
  background: none;
311
  border: none;
312
  padding: 8px;
 
329
  background-color: rgba(0, 0, 0, 0.1);
330
  }
331
 
332
+ /* Relevancy Score Styles */
333
+ .relevancy-score {
334
+ margin-top: 10px !important;
335
+ font-size: 10px !important;
336
+ font-style: italic;
337
+ }
338
+
339
+ .score-green {
340
+ color: green !important;
341
+ }
342
+
343
+ .score-orange {
344
+ color: orange !important;
345
+ }
346
+
347
+ .score-red {
348
+ color: red !important;
349
+ }
350
+
351
+ /* Gallery Styles */
352
+ .gallery-item > div {
353
+ white-space: normal !important;
354
+ word-break: break-word !important;
355
+ overflow-wrap: break-word !important;
356
+ }
357
+
358
+ /* Avatar Styles */
359
+ .avatar-container.svelte-1x5p6hu:not(.thumbnail-item) img {
360
+ width: 100%;
361
+ height: 100%;
362
+ object-fit: cover;
363
+ border-radius: 50%;
364
+ padding: 0px;
365
+ margin: 0px;
366
+ }
367
+
368
+ /* Message Button Styles */
369
+ .message-buttons-left.panel.message-buttons.with-avatar {
370
+ display: none;
371
+ }
372
+
373
+ /* Checkmark Styles */
374
+ .checkmark {
375
+ color: green !important;
376
+ font-size: 18px;
377
+ margin-right: 10px !important;
378
+ }
379
+
380
+ /* Papers Summary & Relevant Popup Styles */
381
+ #papers-summary-popup button span,
382
+ #papers-relevant-popup span {
383
+ font-size: 16px;
384
+ font-weight: bold;
385
+ text-align: center;
386
+ }
387
+
388
+ /* Citations Tab Button Style */
389
+ #tab-citations .button {
390
+ padding: 12px 16px;
391
+ font-size: 16px;
392
+ font-weight: bold;
393
+ cursor: pointer;
394
  border: none;
395
+ outline: none;
396
+ text-align: left;
397
+ transition: background-color 0.3s ease;
398
+ }
399
+
400
+ /* Show Figures Button Style */
401
+ button#show-figures {
402
+ background-color: #f5f5f5;
403
+ border: 1px solid #e0e0e0;
404
+ border-radius: 4px;
405
+ color: #333333;
406
  cursor: pointer;
407
+ width: 100%;
 
 
 
 
 
 
 
408
  text-align: center;
409
  }
410
+
411
+ /* Gradio Box Style */
412
+ .gr-box {
413
+ border-color: #d6c37c;
414
  }
415
 
416
+ /* Hidden Message Style */
417
+ #hidden-message {
418
+ display: none;
419
+ }
420
 
421
+ /* Label Selected Style */
422
+ label.selected {
423
+ background: #93c5fd !important;
424
+ }
425
 
426
+ /* Submit Button Style */
427
+ #submit-button {
428
+ padding: 0px !important;
429
+ }
 
 
430
 
431
+ /* Hugging Face Space Fixes */
432
+ .h-full {
433
+ height: auto !important;
434
+ min-height: 0 !important;
435
+ }
 
 
436
 
437
+ .space-content {
438
+ height: auto !important;
439
+ max-height: 100vh !important;
440
+ overflow: hidden;
441
+ }
442
 
443
+ /* Dropdown Samples Style */
444
+ #dropdown-samples {
445
+ background: none !important;
446
+ }
447
 
448
+ #dropdown-samples > .container > .wrap {
449
+ background-color: white;
450
+ }
 
 
 
451
 
452
+ /* Tab Examples Form Style */
453
+ #tab-examples > div > .form {
454
+ border: none;
455
+ background: none !important;
456
+ }
 
 
 
 
457
 
458
+ /* Utility Classes */
459
+ .hidden {
460
+ display: none !important;
461
+ }
 
462
 
463
+ footer {
464
+ display: none !important;
465
+ visibility: hidden;
466
+ }
467
+
468
+ a {
469
+ text-decoration: none;
470
+ color: inherit;
471
+ }
472
+
473
+ .a-doc-ref {
474
+ text-decoration: none !important;
475
+ }
476
+
477
+ /* Media Queries */
478
+ /* Desktop Media Query */
479
+ @media screen and (min-width: 1024px) {
480
+ .gradio-container {
481
+ max-height: calc(100vh - 190px) !important;
482
+ overflow: hidden;
483
  }
484
 
485
+ div#tab-examples,
486
+ div#sources-textbox,
487
+ div#tab-config {
488
+ height: calc(100vh - 190px) !important;
489
  overflow-y: scroll !important;
490
  }
491
 
492
+ div#sources-figures,
493
+ div#graphs-container,
494
+ div#tab-citations {
495
+ height: calc(100vh - 300px) !important;
496
+ max-height: 90vh !important;
497
  overflow-y: scroll !important;
 
498
  }
499
 
500
+ div#chatbot-row {
501
+ max-height: calc(100vh - 90px) !important;
 
 
502
  }
503
 
504
+ div#graphs-container {
505
+ height: calc(100vh - 210px) !important;
506
+ overflow-y: scroll !important;
507
  }
 
508
 
509
+ div#tab-saved-graphs {
 
 
 
510
  overflow-y: auto;
511
+ max-height: 80vh;
512
  }
 
 
513
  }
514
 
515
+ /* Mobile Media Query */
 
 
 
 
 
516
  @media screen and (max-width: 767px) {
517
+ div#chatbot {
518
+ height: 500px !important;
 
 
519
  }
520
 
521
+ #submit-button {
522
+ padding: 0 !important;
523
  min-width: 80px;
524
  }
525
 
 
526
  div.tab-nav button {
527
  display: none !important;
528
  }
529
 
530
+ div.tab-nav button:first-child,
 
 
 
 
 
531
  div.tab-nav button:nth-child(2) {
532
  display: block !important;
533
  }
534
+
535
+ #right-panel button {
536
  display: block !important;
537
  }
538
 
539
+ div#tab-recommended_content {
540
+ max-height: 50vh;
541
+ overflow-y: auto;
542
+ }
543
+
544
+ div#tab-saved-graphs {
545
+ max-height: 50vh;
546
+ overflow-y: auto;
547
+ }
548
  }
549
 
550
+ /* Dark Mode */
551
  @media (prefers-color-scheme: dark) {
552
+ .card {
553
  background-color: #374151;
554
  }
555
+
556
+ .card-image > .card-content {
557
  background-color: rgb(55, 65, 81) !important;
558
  }
559
 
 
561
  background-color: #404652;
562
  }
563
 
564
+ .container > .wrap {
565
  background-color: #374151 !important;
566
+ color: white !important;
 
 
 
567
  }
568
+
569
+ .card-content h2 {
570
+ color: #e7754f !important;
571
  }
572
+
573
  .card-footer span {
574
+ color: white !important;
575
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
 
577
+ body.dark .warning-box *,
578
+ body.dark .tip-box * {
579
+ color: black !important;
580
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
 
582
+ .doc-ref sup {
583
+ color: rgb(235 109 35)!important;
584
+ }
585
  }
586
 
587
+ /* Checkbox Config Style */
588
+ #checkbox-config {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
589
  display: block;
 
 
 
 
 
 
 
 
590
  position: absolute;
591
+ background: none;
592
+ border: none;
593
+ padding: 8px;
594
+ cursor: pointer;
595
+ width: 40px;
596
+ height: 40px;
597
+ display: flex;
598
+ align-items: center;
599
+ justify-content: center;
 
 
 
 
 
 
 
 
 
 
 
 
600
  border-radius: 50%;
601
+ transition: background-color 0.2s;
602
+ font-size: 20px;
603
+ text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
604
  }
605
 
606
+ #checkbox-config:checked {
607
+ display: block;
 
 
608
  }