Update app.py
Browse files
app.py
CHANGED
|
@@ -29,8 +29,8 @@ town_to_row = {
|
|
| 29 |
valid_towns = list(df_bfs_data["bfs_name"].sort_values().unique())
|
| 30 |
|
| 31 |
|
| 32 |
-
#
|
| 33 |
-
|
| 34 |
def match_town(user_town: str):
|
| 35 |
"""Return the canonical town name from the dataset, or None."""
|
| 36 |
if not user_town or not user_town.strip():
|
|
@@ -50,10 +50,6 @@ def match_town(user_town: str):
|
|
| 50 |
return None
|
| 51 |
|
| 52 |
|
| 53 |
-
# TODO 2 (LLM REQUIRED):
|
| 54 |
-
# Implement one helper to call your chosen LLM and return JSON text.
|
| 55 |
-
# Requirement: raise an error if API key/model is missing instead of using fallback logic.
|
| 56 |
-
# Hint: the Week 1 OpenAI example uses `client.responses.create(...)`.
|
| 57 |
def call_llm_json(system_prompt: str, user_prompt: str) -> str:
|
| 58 |
"""Call LLM with system and user prompts, return JSON response text."""
|
| 59 |
if not LLM_API_KEY or not LLM_MODEL:
|
|
@@ -61,14 +57,16 @@ def call_llm_json(system_prompt: str, user_prompt: str) -> str:
|
|
| 61 |
|
| 62 |
client = OpenAI(api_key=LLM_API_KEY)
|
| 63 |
|
| 64 |
-
response = client.
|
| 65 |
model=LLM_MODEL,
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
| 69 |
)
|
| 70 |
|
| 71 |
-
return (response.
|
| 72 |
|
| 73 |
|
| 74 |
# Validate the LLM response before the rest of the app depends on it.
|
|
@@ -98,8 +96,6 @@ def parse_json_response(raw: str, required_keys: tuple[str, ...]) -> dict:
|
|
| 98 |
return parsed
|
| 99 |
|
| 100 |
|
| 101 |
-
# TODO 3 (LLM REQUIRED):
|
| 102 |
-
# Use your LLM to extract: rooms, area_m2, town from free text.
|
| 103 |
def extract_preferences(user_text: str) -> dict:
|
| 104 |
"""Extract rooms, area_m2, and town from free text using LLM."""
|
| 105 |
system_prompt = """Du bist ein Assistent, der Wohnungswünsche in strukturierte Daten umwandelt.
|
|
@@ -122,10 +118,6 @@ Antworte ausschließlich mit gültigem JSON in diesem Format:
|
|
| 122 |
return parsed
|
| 123 |
|
| 124 |
|
| 125 |
-
# TODO 4:
|
| 126 |
-
# Implement numeric model prediction with exactly these features:
|
| 127 |
-
# [rooms, area_m2, pop, pop_dens, frg_pct, emp, tax_income]
|
| 128 |
-
# The provided model is a pickled scikit-learn regressor loaded above.
|
| 129 |
def predict_apartment_price(rooms: float, area_m2: float, town: str) -> float:
|
| 130 |
"""Predict monthly rent using the loaded model."""
|
| 131 |
# Get town data
|
|
@@ -151,8 +143,6 @@ def predict_apartment_price(rooms: float, area_m2: float, town: str) -> float:
|
|
| 151 |
return round(prediction, 2)
|
| 152 |
|
| 153 |
|
| 154 |
-
# TODO 5 (LLM REQUIRED):
|
| 155 |
-
# Use your LLM to generate a concise explanation with one uncertainty note.
|
| 156 |
def generate_explanation(preferences: dict, prediction: float) -> str:
|
| 157 |
"""Generate a user-friendly explanation using LLM."""
|
| 158 |
system_prompt = "Du bist ein hilfsbereiter Assistent für Immobilienvorhersagen."
|
|
@@ -176,8 +166,6 @@ Antworte ausschließlich mit gültigem JSON in diesem Format:
|
|
| 176 |
return parsed["answer"]
|
| 177 |
|
| 178 |
|
| 179 |
-
# TODO 6:
|
| 180 |
-
# Implement the end-to-end pipeline.
|
| 181 |
def run_pipeline(user_text: str):
|
| 182 |
"""End-to-end pipeline: extract -> predict -> explain."""
|
| 183 |
try:
|
|
|
|
| 29 |
valid_towns = list(df_bfs_data["bfs_name"].sort_values().unique())
|
| 30 |
|
| 31 |
|
| 32 |
+
# Core Pipeline Functions
|
| 33 |
+
|
| 34 |
def match_town(user_town: str):
|
| 35 |
"""Return the canonical town name from the dataset, or None."""
|
| 36 |
if not user_town or not user_town.strip():
|
|
|
|
| 50 |
return None
|
| 51 |
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
def call_llm_json(system_prompt: str, user_prompt: str) -> str:
|
| 54 |
"""Call LLM with system and user prompts, return JSON response text."""
|
| 55 |
if not LLM_API_KEY or not LLM_MODEL:
|
|
|
|
| 57 |
|
| 58 |
client = OpenAI(api_key=LLM_API_KEY)
|
| 59 |
|
| 60 |
+
response = client.chat.completions.create(
|
| 61 |
model=LLM_MODEL,
|
| 62 |
+
messages=[
|
| 63 |
+
{"role": "system", "content": system_prompt},
|
| 64 |
+
{"role": "user", "content": user_prompt}
|
| 65 |
+
],
|
| 66 |
+
max_tokens=500,
|
| 67 |
)
|
| 68 |
|
| 69 |
+
return (response.choices[0].message.content or "").strip()
|
| 70 |
|
| 71 |
|
| 72 |
# Validate the LLM response before the rest of the app depends on it.
|
|
|
|
| 96 |
return parsed
|
| 97 |
|
| 98 |
|
|
|
|
|
|
|
| 99 |
def extract_preferences(user_text: str) -> dict:
|
| 100 |
"""Extract rooms, area_m2, and town from free text using LLM."""
|
| 101 |
system_prompt = """Du bist ein Assistent, der Wohnungswünsche in strukturierte Daten umwandelt.
|
|
|
|
| 118 |
return parsed
|
| 119 |
|
| 120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
def predict_apartment_price(rooms: float, area_m2: float, town: str) -> float:
|
| 122 |
"""Predict monthly rent using the loaded model."""
|
| 123 |
# Get town data
|
|
|
|
| 143 |
return round(prediction, 2)
|
| 144 |
|
| 145 |
|
|
|
|
|
|
|
| 146 |
def generate_explanation(preferences: dict, prediction: float) -> str:
|
| 147 |
"""Generate a user-friendly explanation using LLM."""
|
| 148 |
system_prompt = "Du bist ein hilfsbereiter Assistent für Immobilienvorhersagen."
|
|
|
|
| 166 |
return parsed["answer"]
|
| 167 |
|
| 168 |
|
|
|
|
|
|
|
| 169 |
def run_pipeline(user_text: str):
|
| 170 |
"""End-to-end pipeline: extract -> predict -> explain."""
|
| 171 |
try:
|