dolphinium
commited on
Commit
·
f74c067
1
Parent(s):
a4b0896
feat: Integrate dynamic field suggestions from external API into analysis plan generation and UI
Browse files- data_processing.py +23 -6
- extract_results.py +28 -0
- llm_prompts.py +25 -3
- ui.py +32 -17
data_processing.py
CHANGED
|
@@ -26,6 +26,8 @@ from llm_prompts import (
|
|
| 26 |
get_synthesis_report_prompt,
|
| 27 |
get_visualization_code_prompt
|
| 28 |
)
|
|
|
|
|
|
|
| 29 |
|
| 30 |
def parse_suggestions_from_report(report_text):
|
| 31 |
"""Extracts numbered suggestions from the report's markdown text."""
|
|
@@ -35,20 +37,36 @@ def parse_suggestions_from_report(report_text):
|
|
| 35 |
suggestions = re.findall(r"^\s*\d+\.\s*(.*)", suggestions_text, re.MULTILINE)
|
| 36 |
return [s.strip() for s in suggestions]
|
| 37 |
|
|
|
|
| 38 |
def llm_generate_analysis_plan_with_history(llm_model, natural_language_query, chat_history):
|
| 39 |
"""
|
| 40 |
-
Generates a complete analysis plan from a user query, considering chat history
|
|
|
|
| 41 |
"""
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
try:
|
| 44 |
response = llm_model.generate_content(prompt)
|
| 45 |
cleaned_text = re.sub(r'```json\s*|\s*```', '', response.text, flags=re.MULTILINE | re.DOTALL).strip()
|
| 46 |
plan = json.loads(cleaned_text)
|
| 47 |
-
|
|
|
|
| 48 |
except Exception as e:
|
| 49 |
raw_response_text = response.text if 'response' in locals() else 'N/A'
|
| 50 |
print(f"Error in llm_generate_analysis_plan_with_history: {e}\nRaw Response:\n{raw_response_text}")
|
| 51 |
-
return
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
def execute_quantitative_query(solr_client, plan):
|
| 54 |
"""Executes the facet query to get aggregate data."""
|
|
@@ -126,5 +144,4 @@ def execute_viz_code_and_get_path(viz_code, facet_data):
|
|
| 126 |
return None
|
| 127 |
except Exception as e:
|
| 128 |
print(f"ERROR executing visualization code: {e}\n---Code---\n{viz_code}")
|
| 129 |
-
return None
|
| 130 |
-
|
|
|
|
| 26 |
get_synthesis_report_prompt,
|
| 27 |
get_visualization_code_prompt
|
| 28 |
)
|
| 29 |
+
from extract_results import get_search_list_params
|
| 30 |
+
|
| 31 |
|
| 32 |
def parse_suggestions_from_report(report_text):
|
| 33 |
"""Extracts numbered suggestions from the report's markdown text."""
|
|
|
|
| 37 |
suggestions = re.findall(r"^\s*\d+\.\s*(.*)", suggestions_text, re.MULTILINE)
|
| 38 |
return [s.strip() for s in suggestions]
|
| 39 |
|
| 40 |
+
|
| 41 |
def llm_generate_analysis_plan_with_history(llm_model, natural_language_query, chat_history):
|
| 42 |
"""
|
| 43 |
+
Generates a complete analysis plan from a user query, considering chat history
|
| 44 |
+
and dynamic field suggestions from an external API.
|
| 45 |
"""
|
| 46 |
+
search_fields, search_name = [], ""
|
| 47 |
+
try:
|
| 48 |
+
# Call the external API to get dynamic field suggestions
|
| 49 |
+
search_fields, search_name = get_search_list_params(natural_language_query)
|
| 50 |
+
print(f"Successfully retrieved {len(search_fields)} dynamic fields.")
|
| 51 |
+
except Exception as e:
|
| 52 |
+
print(f"Warning: Could not retrieve dynamic search fields. Proceeding without them. Error: {e}")
|
| 53 |
+
|
| 54 |
+
# Generate the prompt, including the (potentially empty) search_fields
|
| 55 |
+
prompt = get_analysis_plan_prompt(natural_language_query, chat_history, search_fields)
|
| 56 |
+
|
| 57 |
try:
|
| 58 |
response = llm_model.generate_content(prompt)
|
| 59 |
cleaned_text = re.sub(r'```json\s*|\s*```', '', response.text, flags=re.MULTILINE | re.DOTALL).strip()
|
| 60 |
plan = json.loads(cleaned_text)
|
| 61 |
+
# Return the plan and the retrieved fields for UI display
|
| 62 |
+
return plan, search_fields
|
| 63 |
except Exception as e:
|
| 64 |
raw_response_text = response.text if 'response' in locals() else 'N/A'
|
| 65 |
print(f"Error in llm_generate_analysis_plan_with_history: {e}\nRaw Response:\n{raw_response_text}")
|
| 66 |
+
# Return None for the plan but still return search_fields for debugging in the UI
|
| 67 |
+
return None, search_fields
|
| 68 |
+
|
| 69 |
+
|
| 70 |
|
| 71 |
def execute_quantitative_query(solr_client, plan):
|
| 72 |
"""Executes the facet query to get aggregate data."""
|
|
|
|
| 144 |
return None
|
| 145 |
except Exception as e:
|
| 146 |
print(f"ERROR executing visualization code: {e}\n---Code---\n{viz_code}")
|
| 147 |
+
return None
|
|
|
extract_results.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
import yaml
|
| 4 |
+
|
| 5 |
+
def get_search_list_params(query, k=20):
|
| 6 |
+
"""
|
| 7 |
+
Returns tuple: (search_fields, search_name)
|
| 8 |
+
"""
|
| 9 |
+
url = "https://aitest.ebalina.com/stream"
|
| 10 |
+
|
| 11 |
+
response = requests.post(url,
|
| 12 |
+
headers={'Content-Type': 'application/json'},
|
| 13 |
+
json={"query": query, "k": k},
|
| 14 |
+
stream=True)
|
| 15 |
+
|
| 16 |
+
for line in response.iter_lines():
|
| 17 |
+
if line and line.startswith(b'data: '):
|
| 18 |
+
try:
|
| 19 |
+
data = json.loads(line[6:])
|
| 20 |
+
if data.get('log_title') == 'Search List Result':
|
| 21 |
+
yaml_data = yaml.safe_load(data['content'])
|
| 22 |
+
# As requested, ignoring 'search_name' and only returning fields
|
| 23 |
+
return yaml_data.get('search_fields', []), yaml_data.get('search_name', '')
|
| 24 |
+
except:
|
| 25 |
+
continue
|
| 26 |
+
|
| 27 |
+
# Return empty list if no valid data is found
|
| 28 |
+
return [], ""
|
llm_prompts.py
CHANGED
|
@@ -11,9 +11,13 @@ import datetime
|
|
| 11 |
import json
|
| 12 |
from solr_metadata import format_metadata_for_prompt
|
| 13 |
|
| 14 |
-
def get_analysis_plan_prompt(natural_language_query, chat_history):
|
| 15 |
"""
|
| 16 |
Generates the prompt for creating a Solr analysis plan from a user query.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
"""
|
| 18 |
formatted_field_info = format_metadata_for_prompt()
|
| 19 |
formatted_history = ""
|
|
@@ -21,6 +25,22 @@ def get_analysis_plan_prompt(natural_language_query, chat_history):
|
|
| 21 |
if user_msg:
|
| 22 |
formatted_history += f"- User: \"{user_msg}\"\n"
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
return f"""
|
| 25 |
You are an expert data analyst and Solr query engineer. Your task is to convert a natural language question into a structured JSON "Analysis Plan". This plan will be used to run two separate, efficient queries: one for aggregate data (facets) and one for finding illustrative examples (grouping).
|
| 26 |
|
|
@@ -43,6 +63,8 @@ You are an expert data analyst and Solr query engineer. Your task is to convert
|
|
| 43 |
### FIELD DEFINITIONS (Your Source of Truth)
|
| 44 |
|
| 45 |
{formatted_field_info}
|
|
|
|
|
|
|
| 46 |
---
|
| 47 |
### CHAT HISTORY
|
| 48 |
{formatted_history}
|
|
@@ -113,7 +135,7 @@ Convert the following user query into a single, raw JSON "Analysis Plan" object,
|
|
| 113 |
|
| 114 |
**Current User Query:** `{natural_language_query}`
|
| 115 |
"""
|
| 116 |
-
|
| 117 |
def get_synthesis_report_prompt(query, quantitative_data, qualitative_data, plan):
|
| 118 |
"""
|
| 119 |
Generates the prompt for synthesizing a final report from the query results.
|
|
@@ -343,4 +365,4 @@ plt.tight_layout()
|
|
| 343 |
|
| 344 |
**Your Task:**
|
| 345 |
Now, generate the Python code.
|
| 346 |
-
"""
|
|
|
|
| 11 |
import json
|
| 12 |
from solr_metadata import format_metadata_for_prompt
|
| 13 |
|
| 14 |
+
def get_analysis_plan_prompt(natural_language_query, chat_history, search_fields=None):
|
| 15 |
"""
|
| 16 |
Generates the prompt for creating a Solr analysis plan from a user query.
|
| 17 |
+
Args:
|
| 18 |
+
natural_language_query (str): The user's query.
|
| 19 |
+
chat_history (list): A list of previous user and bot messages.
|
| 20 |
+
search_fields (list, optional): A list of dictionaries with 'field_name' and 'field_value'.
|
| 21 |
"""
|
| 22 |
formatted_field_info = format_metadata_for_prompt()
|
| 23 |
formatted_history = ""
|
|
|
|
| 25 |
if user_msg:
|
| 26 |
formatted_history += f"- User: \"{user_msg}\"\n"
|
| 27 |
|
| 28 |
+
dynamic_fields_prompt_section = ""
|
| 29 |
+
if search_fields:
|
| 30 |
+
formatted_fields = "\n".join([f" - {field['field_name']}: {field['field_value']}" for field in search_fields])
|
| 31 |
+
dynamic_fields_prompt_section = f"""
|
| 32 |
+
---
|
| 33 |
+
### DYNAMIC FIELD SUGGESTIONS (Use Critically)
|
| 34 |
+
|
| 35 |
+
An external API has suggested the following field-value pairs based on your query.
|
| 36 |
+
**These are only HINTS.** Do NOT use them blindly.
|
| 37 |
+
Critically evaluate if they make sense. For example, a `molecule_name` associated with a `company_name` might be irrelevant or illogical.
|
| 38 |
+
Use only what is logical for the query. Do not construct filters from fields/values that do not make sense.
|
| 39 |
+
|
| 40 |
+
**Suggested Fields:**
|
| 41 |
+
{formatted_fields}
|
| 42 |
+
"""
|
| 43 |
+
|
| 44 |
return f"""
|
| 45 |
You are an expert data analyst and Solr query engineer. Your task is to convert a natural language question into a structured JSON "Analysis Plan". This plan will be used to run two separate, efficient queries: one for aggregate data (facets) and one for finding illustrative examples (grouping).
|
| 46 |
|
|
|
|
| 63 |
### FIELD DEFINITIONS (Your Source of Truth)
|
| 64 |
|
| 65 |
{formatted_field_info}
|
| 66 |
+
{dynamic_fields_prompt_section}
|
| 67 |
+
|
| 68 |
---
|
| 69 |
### CHAT HISTORY
|
| 70 |
{formatted_history}
|
|
|
|
| 135 |
|
| 136 |
**Current User Query:** `{natural_language_query}`
|
| 137 |
"""
|
| 138 |
+
# The other prompt functions remain unchanged.
|
| 139 |
def get_synthesis_report_prompt(query, quantitative_data, qualitative_data, plan):
|
| 140 |
"""
|
| 141 |
Generates the prompt for synthesizing a final report from the query results.
|
|
|
|
| 365 |
|
| 366 |
**Your Task:**
|
| 367 |
Now, generate the Python code.
|
| 368 |
+
"""
|
ui.py
CHANGED
|
@@ -46,6 +46,8 @@ def create_ui(llm_model, solr_client):
|
|
| 46 |
msg_textbox = gr.Textbox(placeholder="Ask a question, e.g., 'Show me the top 5 companies by total deal value in 2023'", label="Your Question", interactive=True)
|
| 47 |
|
| 48 |
with gr.Column(scale=2):
|
|
|
|
|
|
|
| 49 |
with gr.Accordion("Generated Analysis Plan", open=False):
|
| 50 |
plan_display = gr.Markdown("Plan will appear here...", visible=True)
|
| 51 |
with gr.Accordion("Retrieved Quantitative Data", open=False):
|
|
@@ -64,21 +66,31 @@ def create_ui(llm_model, solr_client):
|
|
| 64 |
if history is None:
|
| 65 |
history = []
|
| 66 |
|
| 67 |
-
|
|
|
|
| 68 |
|
| 69 |
query_context = user_input.strip()
|
| 70 |
if not query_context:
|
| 71 |
history.append((user_input, "Please enter a question to analyze."))
|
| 72 |
-
yield (history, state, None, None, None, None, None)
|
| 73 |
return
|
| 74 |
|
| 75 |
history.append((user_input, f"Analyzing: '{query_context}'\n\n*Generating analysis plan...*"))
|
| 76 |
-
yield (history, state, None, None, None, None, None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
-
analysis_plan = llm_generate_analysis_plan_with_history(llm_model, query_context, history)
|
| 79 |
if not analysis_plan:
|
| 80 |
history.append((None, "I'm sorry, I couldn't generate a valid analysis plan. Please try rephrasing."))
|
| 81 |
-
yield (history, state, None, None, None, None, None)
|
| 82 |
return
|
| 83 |
|
| 84 |
history.append((None, "✅ Analysis plan generated!"))
|
|
@@ -89,11 +101,12 @@ def create_ui(llm_model, solr_client):
|
|
| 89 |
"""
|
| 90 |
history.append((None, plan_summary))
|
| 91 |
formatted_plan = f"**Full Analysis Plan:**\n```json\n{json.dumps(analysis_plan, indent=2)}\n```"
|
| 92 |
-
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), None, None)
|
| 93 |
|
| 94 |
history.append((None, "*Executing queries for aggregates and examples...*"))
|
| 95 |
-
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), None, None)
|
| 96 |
|
|
|
|
| 97 |
aggregate_data = None
|
| 98 |
example_data = None
|
| 99 |
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
@@ -104,17 +117,19 @@ def create_ui(llm_model, solr_client):
|
|
| 104 |
|
| 105 |
if not aggregate_data or aggregate_data.get('count', 0) == 0:
|
| 106 |
history.append((None, "No data was found for your query. Please try a different question."))
|
| 107 |
-
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), None, None)
|
| 108 |
return
|
| 109 |
|
|
|
|
| 110 |
formatted_agg_data = f"**Quantitative (Aggregate) Data:**\n```json\n{json.dumps(aggregate_data, indent=2)}\n```"
|
| 111 |
formatted_qual_data = f"**Qualitative (Example) Data:**\n```json\n{json.dumps(example_data, indent=2)}\n```"
|
| 112 |
qual_data_display_update = gr.update(value=formatted_qual_data, visible=True)
|
| 113 |
-
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update)
|
| 114 |
|
| 115 |
history.append((None, "✅ Data retrieved. Generating visualization and final report..."))
|
| 116 |
-
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update)
|
| 117 |
|
|
|
|
| 118 |
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 119 |
viz_future = executor.submit(llm_generate_visualization_code, llm_model, query_context, aggregate_data)
|
| 120 |
|
|
@@ -122,7 +137,7 @@ def create_ui(llm_model, solr_client):
|
|
| 122 |
stream_history = history[:]
|
| 123 |
for chunk in llm_synthesize_enriched_report_stream(llm_model, query_context, aggregate_data, example_data, analysis_plan):
|
| 124 |
report_text += chunk
|
| 125 |
-
yield (stream_history, state, None, gr.update(value=report_text, visible=True), gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update)
|
| 126 |
|
| 127 |
history.append((None, report_text))
|
| 128 |
|
|
@@ -132,13 +147,13 @@ def create_ui(llm_model, solr_client):
|
|
| 132 |
if not plot_path:
|
| 133 |
history.append((None, "*I was unable to generate a plot for this data.*\n"))
|
| 134 |
|
| 135 |
-
yield (history, state, output_plot, report_text, gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update)
|
| 136 |
|
| 137 |
state['query_count'] += 1
|
| 138 |
state['last_suggestions'] = parse_suggestions_from_report(report_text)
|
| 139 |
next_prompt = "Analysis complete. What would you like to explore next?"
|
| 140 |
history.append((None, next_prompt))
|
| 141 |
-
yield (history, state, output_plot, report_text, gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update)
|
| 142 |
|
| 143 |
def reset_all():
|
| 144 |
"""Resets the entire UI for a new analysis session."""
|
|
@@ -150,13 +165,14 @@ def create_ui(llm_model, solr_client):
|
|
| 150 |
gr.update(value=None, visible=False),
|
| 151 |
gr.update(value=None, visible=False),
|
| 152 |
gr.update(value=None, visible=False),
|
|
|
|
| 153 |
gr.update(value=None, visible=False)
|
| 154 |
)
|
| 155 |
|
| 156 |
msg_textbox.submit(
|
| 157 |
fn=process_analysis_flow,
|
| 158 |
inputs=[msg_textbox, chatbot, state],
|
| 159 |
-
outputs=[chatbot, state, plot_display, report_display, plan_display, quantitative_data_display, qualitative_data_display],
|
| 160 |
).then(
|
| 161 |
lambda: gr.update(value=""),
|
| 162 |
None,
|
|
@@ -167,9 +183,8 @@ def create_ui(llm_model, solr_client):
|
|
| 167 |
clear_button.click(
|
| 168 |
fn=reset_all,
|
| 169 |
inputs=None,
|
| 170 |
-
outputs=[chatbot, state, msg_textbox, plot_display, report_display, plan_display, quantitative_data_display, qualitative_data_display],
|
| 171 |
queue=False
|
| 172 |
)
|
| 173 |
|
| 174 |
-
return demo
|
| 175 |
-
|
|
|
|
| 46 |
msg_textbox = gr.Textbox(placeholder="Ask a question, e.g., 'Show me the top 5 companies by total deal value in 2023'", label="Your Question", interactive=True)
|
| 47 |
|
| 48 |
with gr.Column(scale=2):
|
| 49 |
+
with gr.Accordion("Dynamic Field Suggestions", open=False):
|
| 50 |
+
suggestions_display = gr.Markdown("Suggestions from the external API will appear here...", visible=True)
|
| 51 |
with gr.Accordion("Generated Analysis Plan", open=False):
|
| 52 |
plan_display = gr.Markdown("Plan will appear here...", visible=True)
|
| 53 |
with gr.Accordion("Retrieved Quantitative Data", open=False):
|
|
|
|
| 66 |
if history is None:
|
| 67 |
history = []
|
| 68 |
|
| 69 |
+
# Reset all displays at the beginning of a new flow
|
| 70 |
+
yield (history, state, gr.update(value=None, visible=False), gr.update(value=None, visible=False), gr.update(value=None, visible=False), gr.update(value=None, visible=False), gr.update(value=None, visible=False), gr.update(value="Suggestions from the external API will appear here...", visible=False))
|
| 71 |
|
| 72 |
query_context = user_input.strip()
|
| 73 |
if not query_context:
|
| 74 |
history.append((user_input, "Please enter a question to analyze."))
|
| 75 |
+
yield (history, state, None, None, None, None, None, None)
|
| 76 |
return
|
| 77 |
|
| 78 |
history.append((user_input, f"Analyzing: '{query_context}'\n\n*Generating analysis plan...*"))
|
| 79 |
+
yield (history, state, None, None, None, None, None, None)
|
| 80 |
+
|
| 81 |
+
# Generate plan and get search field suggestions
|
| 82 |
+
analysis_plan, search_fields = llm_generate_analysis_plan_with_history(llm_model, query_context, history)
|
| 83 |
+
|
| 84 |
+
# Update and display search field suggestions in its own accordion
|
| 85 |
+
if search_fields:
|
| 86 |
+
suggestions_md = "**External API Suggestions:**\n" + "\n".join([f"- `{field['field_name']}`: `{field['field_value']}`" for field in search_fields])
|
| 87 |
+
suggestions_display_update = gr.update(value=suggestions_md, visible=True)
|
| 88 |
+
else:
|
| 89 |
+
suggestions_display_update = gr.update(value="No suggestions were returned from the external API.", visible=True)
|
| 90 |
|
|
|
|
| 91 |
if not analysis_plan:
|
| 92 |
history.append((None, "I'm sorry, I couldn't generate a valid analysis plan. Please try rephrasing."))
|
| 93 |
+
yield (history, state, None, None, None, None, None, suggestions_display_update)
|
| 94 |
return
|
| 95 |
|
| 96 |
history.append((None, "✅ Analysis plan generated!"))
|
|
|
|
| 101 |
"""
|
| 102 |
history.append((None, plan_summary))
|
| 103 |
formatted_plan = f"**Full Analysis Plan:**\n```json\n{json.dumps(analysis_plan, indent=2)}\n```"
|
| 104 |
+
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), None, None, suggestions_display_update)
|
| 105 |
|
| 106 |
history.append((None, "*Executing queries for aggregates and examples...*"))
|
| 107 |
+
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), None, None, suggestions_display_update)
|
| 108 |
|
| 109 |
+
# Execute queries in parallel
|
| 110 |
aggregate_data = None
|
| 111 |
example_data = None
|
| 112 |
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
|
|
| 117 |
|
| 118 |
if not aggregate_data or aggregate_data.get('count', 0) == 0:
|
| 119 |
history.append((None, "No data was found for your query. Please try a different question."))
|
| 120 |
+
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), None, None, suggestions_display_update)
|
| 121 |
return
|
| 122 |
|
| 123 |
+
# Display retrieved data
|
| 124 |
formatted_agg_data = f"**Quantitative (Aggregate) Data:**\n```json\n{json.dumps(aggregate_data, indent=2)}\n```"
|
| 125 |
formatted_qual_data = f"**Qualitative (Example) Data:**\n```json\n{json.dumps(example_data, indent=2)}\n```"
|
| 126 |
qual_data_display_update = gr.update(value=formatted_qual_data, visible=True)
|
| 127 |
+
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update, suggestions_display_update)
|
| 128 |
|
| 129 |
history.append((None, "✅ Data retrieved. Generating visualization and final report..."))
|
| 130 |
+
yield (history, state, None, None, gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update, suggestions_display_update)
|
| 131 |
|
| 132 |
+
# Generate viz and report
|
| 133 |
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 134 |
viz_future = executor.submit(llm_generate_visualization_code, llm_model, query_context, aggregate_data)
|
| 135 |
|
|
|
|
| 137 |
stream_history = history[:]
|
| 138 |
for chunk in llm_synthesize_enriched_report_stream(llm_model, query_context, aggregate_data, example_data, analysis_plan):
|
| 139 |
report_text += chunk
|
| 140 |
+
yield (stream_history, state, None, gr.update(value=report_text, visible=True), gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update, suggestions_display_update)
|
| 141 |
|
| 142 |
history.append((None, report_text))
|
| 143 |
|
|
|
|
| 147 |
if not plot_path:
|
| 148 |
history.append((None, "*I was unable to generate a plot for this data.*\n"))
|
| 149 |
|
| 150 |
+
yield (history, state, output_plot, gr.update(value=report_text), gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update, suggestions_display_update)
|
| 151 |
|
| 152 |
state['query_count'] += 1
|
| 153 |
state['last_suggestions'] = parse_suggestions_from_report(report_text)
|
| 154 |
next_prompt = "Analysis complete. What would you like to explore next?"
|
| 155 |
history.append((None, next_prompt))
|
| 156 |
+
yield (history, state, output_plot, gr.update(value=report_text), gr.update(value=formatted_plan, visible=True), gr.update(value=formatted_agg_data, visible=True), qual_data_display_update, suggestions_display_update)
|
| 157 |
|
| 158 |
def reset_all():
|
| 159 |
"""Resets the entire UI for a new analysis session."""
|
|
|
|
| 165 |
gr.update(value=None, visible=False),
|
| 166 |
gr.update(value=None, visible=False),
|
| 167 |
gr.update(value=None, visible=False),
|
| 168 |
+
gr.update(value=None, visible=False),
|
| 169 |
gr.update(value=None, visible=False)
|
| 170 |
)
|
| 171 |
|
| 172 |
msg_textbox.submit(
|
| 173 |
fn=process_analysis_flow,
|
| 174 |
inputs=[msg_textbox, chatbot, state],
|
| 175 |
+
outputs=[chatbot, state, plot_display, report_display, plan_display, quantitative_data_display, qualitative_data_display, suggestions_display],
|
| 176 |
).then(
|
| 177 |
lambda: gr.update(value=""),
|
| 178 |
None,
|
|
|
|
| 183 |
clear_button.click(
|
| 184 |
fn=reset_all,
|
| 185 |
inputs=None,
|
| 186 |
+
outputs=[chatbot, state, msg_textbox, plot_display, report_display, plan_display, quantitative_data_display, qualitative_data_display, suggestions_display],
|
| 187 |
queue=False
|
| 188 |
)
|
| 189 |
|
| 190 |
+
return demo
|
|
|