Spaces:
Sleeping
Sleeping
Upload 5 files
Browse files- app.py +44 -0
- config.py +37 -0
- constants.py +60 -0
- llms.py +55 -0
- requirements.txt +18 -0
app.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from config import settings
|
| 3 |
+
import constants
|
| 4 |
+
from llms import Agents
|
| 5 |
+
import datetime
|
| 6 |
+
|
| 7 |
+
agents = Agents(model="gpt-4o", model_type="openai", temperature=0.5, is_structured=False)
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def get_county_info(county_name):
|
| 11 |
+
today = datetime.datetime.today().strftime("%Y-%m-%d")
|
| 12 |
+
user_input = {"county": county_name, "today": today}
|
| 13 |
+
response = agents.generate_tavily_search(f"{user_input}")
|
| 14 |
+
return response["output"], gr.update(visible=False)
|
| 15 |
+
# return "## 👇 Please select a county and click submit."
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# A list of county names for the dropdown.
|
| 19 |
+
county_choices = constants.county_list
|
| 20 |
+
|
| 21 |
+
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 22 |
+
gr.Markdown("# IBHS County Profiler")
|
| 23 |
+
gr.Markdown("#### A specialized research agent that gather up-to-date, authoritative information on residential risk for a county.")
|
| 24 |
+
gr.Markdown("Select a county from the list and click 'Submit' to see the details.")
|
| 25 |
+
|
| 26 |
+
with gr.Column():
|
| 27 |
+
county_dropdown = gr.Dropdown(
|
| 28 |
+
choices=county_choices,
|
| 29 |
+
label="Select a County"
|
| 30 |
+
)
|
| 31 |
+
submit_button = gr.Button("Submit")
|
| 32 |
+
progress = gr.Textbox(label="Progress bar", value="Status: Waiting for input...", interactive=False)
|
| 33 |
+
|
| 34 |
+
output_markdown = gr.Markdown()
|
| 35 |
+
|
| 36 |
+
# The click event of the button now triggers the function
|
| 37 |
+
submit_button.click(
|
| 38 |
+
fn=get_county_info,
|
| 39 |
+
inputs=county_dropdown,
|
| 40 |
+
outputs=[output_markdown, progress]
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
if __name__ == "__main__":
|
| 44 |
+
demo.launch()
|
config.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic_settings import BaseSettings
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class Settings(BaseSettings):
|
| 5 |
+
|
| 6 |
+
COUNTY_RISK_ANALYSIS_PROMPT: str = """
|
| 7 |
+
You are “SafeHaven Analyst,” a specialized research agent that uses the Tavily search API to gather up-to-date, authoritative information on residential risk for a single U.S. county.
|
| 8 |
+
|
| 9 |
+
⮞ **Inputs provided by the user as a dictionary with following items**
|
| 10 |
+
- `county`: full county name plus state abbreviation (e.g., “San Bernardino County, CA”).
|
| 11 |
+
- `today`: ISO date string (YYYY-MM-DD) representing “today.”
|
| 12 |
+
|
| 13 |
+
⮞ **Your task**
|
| 14 |
+
1. Run focused Tavily searches (news, government, academic, and insurer sources) published **within the last 3 years** that mention (`county` AND key terms below).
|
| 15 |
+
- Key terms: *storm*, *flood*, *wildfire*, *hail*, *tornado*, *hurricane*, *drought*, *extreme heat*, *seismic*, *landslide*, *insurance claims*, *roof damage cost*, *window replacement cost*, *door replacement cost*, *home repair cost*.
|
| 16 |
+
2. Analyse the top credible sources to extract:
|
| 17 |
+
**a. Weather & Natural Hazard Landscape** – List the *current* primary hazards (likelihood + seasonal timing).
|
| 18 |
+
**b. Damage-Cost Benchmarks** – Typical 2024-25 cost ranges (USD) in this county for repairing/replacing: roof, exterior doors, windows. Cite the source or methodology briefly in parentheses.
|
| 19 |
+
**c. Overall Residential Risk Profile** – Concise narrative (≈120 words) rating risk *Low / Moderate / High* and explaining drivers (age of local housing stock, hazard frequency, rebuild costs, insurance availability, etc.).
|
| 20 |
+
3. Prioritize clarity: synthesize, do not paste source snippets. If data is scarce, state that and default to statewide or regional averages, labelling them clearly.
|
| 21 |
+
4. **Inline-cite every numeric or factual claim** with a markdown link:
|
| 22 |
+
- Format: `**[$12 k–$18 k, [HomeAdvisor 05/2025](https://source-url.com)]**` – one citation per sentence.
|
| 23 |
+
- Use the article/site name and month/year; the link must point to the live URL returned by Tavily.
|
| 24 |
+
5. Total report length ≤ 300 words, using these headings exactly:
|
| 25 |
+
**Weather & Hazard Snapshot**
|
| 26 |
+
**Damage-Cost Benchmarks (USD)**
|
| 27 |
+
**County Home Risk Profile**
|
| 28 |
+
|
| 29 |
+
⮞ **Style guidelines**
|
| 30 |
+
- Formal but readable business prose.
|
| 31 |
+
- Use bullet lists only inside the two first sections; profile section is paragraph form.
|
| 32 |
+
- No markdown code fences or JSON—return plain text with the three headings only.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
settings = Settings()
|
| 37 |
+
|
constants.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
county_list =[
|
| 2 |
+
"Alameda County, CA",
|
| 3 |
+
"Alpine County, CA",
|
| 4 |
+
"Amador County, CA",
|
| 5 |
+
"Butte County, CA",
|
| 6 |
+
"Calaveras County, CA",
|
| 7 |
+
"Colusa County, CA",
|
| 8 |
+
"Contra Costa County, CA",
|
| 9 |
+
"Del Norte County, CA",
|
| 10 |
+
"El Dorado County, CA",
|
| 11 |
+
"Fresno County, CA",
|
| 12 |
+
"Glenn County, CA",
|
| 13 |
+
"Humboldt County, CA",
|
| 14 |
+
"Imperial County, CA",
|
| 15 |
+
"Inyo County, CA",
|
| 16 |
+
"Kern County, CA",
|
| 17 |
+
"Kings County, CA",
|
| 18 |
+
"Lake County, CA",
|
| 19 |
+
"Lassen County, CA",
|
| 20 |
+
"Los Angeles County, CA",
|
| 21 |
+
"Madera County, CA",
|
| 22 |
+
"Marin County, CA",
|
| 23 |
+
"Mariposa County, CA",
|
| 24 |
+
"Mendocino County, CA",
|
| 25 |
+
"Merced County, CA",
|
| 26 |
+
"Modoc County, CA",
|
| 27 |
+
"Mono County, CA",
|
| 28 |
+
"Monterey County, CA",
|
| 29 |
+
"Napa County, CA",
|
| 30 |
+
"Nevada County, CA",
|
| 31 |
+
"Orange County, CA",
|
| 32 |
+
"Placer County, CA",
|
| 33 |
+
"Plumas County, CA",
|
| 34 |
+
"Riverside County, CA",
|
| 35 |
+
"Sacramento County, CA",
|
| 36 |
+
"San Benito County, CA",
|
| 37 |
+
"San Bernardino County, CA",
|
| 38 |
+
"San Diego County, CA",
|
| 39 |
+
"San Francisco County, CA",
|
| 40 |
+
"San Joaquin County, CA",
|
| 41 |
+
"San Luis Obispo County, CA",
|
| 42 |
+
"San Mateo County, CA",
|
| 43 |
+
"Santa Barbara County, CA",
|
| 44 |
+
"Santa Clara County, CA",
|
| 45 |
+
"Santa Cruz County, CA",
|
| 46 |
+
"Shasta County, CA",
|
| 47 |
+
"Sierra County, CA",
|
| 48 |
+
"Siskiyou County, CA",
|
| 49 |
+
"Solano County, CA",
|
| 50 |
+
"Sonoma County, CA",
|
| 51 |
+
"Stanislaus County, CA",
|
| 52 |
+
"Sutter County, CA",
|
| 53 |
+
"Tehama County, CA",
|
| 54 |
+
"Trinity County, CA",
|
| 55 |
+
"Tulare County, CA",
|
| 56 |
+
"Tuolumne County, CA",
|
| 57 |
+
"Ventura County, CA",
|
| 58 |
+
"Yolo County, CA",
|
| 59 |
+
"Yuba County, CA"
|
| 60 |
+
]
|
llms.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import datetime
|
| 2 |
+
|
| 3 |
+
from langchain.agents import create_openai_tools_agent, AgentExecutor
|
| 4 |
+
from langchain.chat_models import init_chat_model
|
| 5 |
+
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
| 6 |
+
from langchain_openai import ChatOpenAI
|
| 7 |
+
from langchain_tavily import TavilySearch
|
| 8 |
+
from langchain.schema import HumanMessage, SystemMessage
|
| 9 |
+
from langchain_core.prompts import PromptTemplate
|
| 10 |
+
from langchain_openai import ChatOpenAI
|
| 11 |
+
from langchain_anthropic import ChatAnthropic
|
| 12 |
+
from pydantic import BaseModel, Field
|
| 13 |
+
from typing import List
|
| 14 |
+
from enum import Enum
|
| 15 |
+
from langchain_core.output_parsers import PydanticOutputParser
|
| 16 |
+
from langchain_core.output_parsers import JsonOutputParser
|
| 17 |
+
import os
|
| 18 |
+
from dotenv import load_dotenv
|
| 19 |
+
load_dotenv()
|
| 20 |
+
|
| 21 |
+
from config import settings
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class Agents:
|
| 25 |
+
def __init__(self, temperature: float = 0.3, model: str = "gpt-4.1", model_type: str = "openai",
|
| 26 |
+
is_structured: bool = True):
|
| 27 |
+
"""
|
| 28 |
+
Initializes the UserSummaryGenerator with necessary settings and model configuration.
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
self.settings = settings
|
| 32 |
+
self.model_type = model_type
|
| 33 |
+
|
| 34 |
+
self.llm_tavily = init_chat_model(model="gpt-4o", model_provider="openai", temperature=0)
|
| 35 |
+
|
| 36 |
+
self.tavily_search_tool = TavilySearch(max_results=5, topic="general")
|
| 37 |
+
|
| 38 |
+
self.tavily_prompt = ChatPromptTemplate.from_messages([
|
| 39 |
+
("system", self.settings.COUNTY_RISK_ANALYSIS_PROMPT),
|
| 40 |
+
MessagesPlaceholder(variable_name="messages"),
|
| 41 |
+
MessagesPlaceholder(variable_name="agent_scratchpad"), # Required for tool calls
|
| 42 |
+
])
|
| 43 |
+
|
| 44 |
+
self.tavily_agent = create_openai_tools_agent(
|
| 45 |
+
llm=self.llm_tavily,
|
| 46 |
+
tools=[self.tavily_search_tool],
|
| 47 |
+
prompt=self.tavily_prompt
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
self.agent_executor = AgentExecutor(agent=self.tavily_agent, tools=[self.tavily_search_tool], verbose=False)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def generate_tavily_search(self, user_input: str) -> str:
|
| 54 |
+
response = self.agent_executor.invoke({"messages": [HumanMessage(content=user_input)]},)
|
| 55 |
+
return response
|
requirements.txt
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pandas
|
| 2 |
+
openpyxl
|
| 3 |
+
pandas
|
| 4 |
+
pyarrow
|
| 5 |
+
langchain
|
| 6 |
+
langchain_core
|
| 7 |
+
python-dotenv
|
| 8 |
+
langchain-openai
|
| 9 |
+
gradio
|
| 10 |
+
pydantic-settings
|
| 11 |
+
pyreadstat
|
| 12 |
+
plotly
|
| 13 |
+
kaleido
|
| 14 |
+
chardet
|
| 15 |
+
charset-normalizer
|
| 16 |
+
pandas-stubs==2.2.3.250527
|
| 17 |
+
langchain_anthropic
|
| 18 |
+
langchain-tavily
|