Kapex13 commited on
Commit
6f2cd7f
·
verified ·
1 Parent(s): 736e359

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +67 -69
src/streamlit_app.py CHANGED
@@ -11,10 +11,10 @@ import ast
11
  import random
12
  import tempfile
13
 
14
- # 1. Удаляем блок с ручной установкой env vars
15
- # Streamlit должен работать с .streamlit/config.toml, который вы создали
16
 
17
- # 2. Пути к файлам относительно текущего скрипта
18
  HERE = os.path.dirname(os.path.abspath(__file__))
19
  CSV_PATH = os.path.join(HERE, "tvshows_processed2.csv")
20
  EMB_PATH = os.path.join(HERE, "embeddings.npy")
@@ -117,7 +117,7 @@ def load_embeddings_and_index():
117
 
118
  def semantic_search(query, embedder, index, df, genre=None, year=None, country=None, vtype=None, k=5):
119
  if not query.strip():
120
- return pd.DataFrame() # Возвращаем пустой DataFrame, если запрос пуст
121
  query_embedding = embedder.encode([query])
122
  faiss.normalize_L2(query_embedding)
123
  dists, idxs = index.search(query_embedding, max(k*3, k))
@@ -167,110 +167,108 @@ def main():
167
  st.set_page_config(page_title="Поиск фильмов и сериалов + AI", layout="wide")
168
  st.title("Семантический поиск фильмов и сериалов с AI")
169
 
170
- # 3. Инициализация состояния
171
- if "search_clicked" not in st.session_state:
172
- st.session_state.search_clicked = False
173
- if "ai_clicked" not in st.session_state:
174
- st.session_state.ai_clicked = False
175
- if "query_input" not in st.session_state:
176
- st.session_state.query_input = ""
177
- if "genre_filter" not in st.session_state:
178
- st.session_state.genre_filter = "Все"
179
- if "year_filter" not in st.session_state:
180
- st.session_state.year_filter = "Все"
181
- if "country_filter" not in st.session_state:
182
- st.session_state.country_filter = "Все"
183
- if "type_filter" not in st.session_state:
184
- st.session_state.type_filter = "Все"
185
 
186
  df = load_data()
187
  embedder = init_embedder()
188
  _, index = load_embeddings_and_index()
189
  llm = init_groq_llm()
190
 
191
- # 4. Фильтры
192
  colf1, colf2, colf3, colf4 = st.columns(4)
193
  with colf1:
194
  genres = ["Все"] + sorted(set(sum([g.split(", ") for g in df["basic_genres"].dropna().unique()], [])))
195
- st.session_state.genre_filter = st.selectbox("Жанр", genres, key="genre_filter_key")
196
  with colf2:
197
  years = ["Все"] + [str(y) for y in sorted(df["year"].unique())]
198
- st.session_state.year_filter = st.selectbox("Год", years, key="year_filter_key")
199
  with colf3:
200
  countries = ["Все"] + sorted([c for c in df["country"].dropna().unique()])
201
- st.session_state.country_filter = st.selectbox("Страна", countries, key="country_filter_key")
202
  with colf4:
203
  vtypes = ["Все"] + sorted(df["type"].dropna().unique())
204
- st.session_state.type_filter = st.selectbox("Тип", vtypes, key="type_filter_key")
205
 
206
  k = st.slider("Количество результатов:", 1, 20, 5, key="k_slider")
 
 
207
  st.text_input("Введите ключевые слова или сюжет:", key="query_input")
208
 
209
- # 5. Обработка кнопок
210
- def handle_search(query):
211
- st.session_state.query_input = query
212
  st.session_state.search_clicked = True
213
  st.session_state.ai_clicked = False
214
 
215
  nav1, nav2, nav3, nav4 = st.columns(4)
216
  with nav1:
217
  if st.button("Случайный фильм/сериал"):
218
- handle_search(random.choice(df["tvshow_title"]))
219
  with nav2:
220
- if st.button("ТОП по жанру") and st.session_state.genre_filter != "Все":
221
- handle_search(st.session_state.genre_filter)
222
  with nav3:
223
  if st.button("Новинки"):
224
- handle_search(str(max(df["year"])))
225
  with nav4:
226
  if st.button("Искать"):
227
- handle_search(st.session_state.query_input)
 
 
 
228
 
229
  # 6. Отображение результатов
230
- if st.session_state.search_clicked and st.session_state.query_input.strip():
231
  with st.spinner("Поиск..."):
232
- results = semantic_search(
233
- st.session_state.query_input, embedder, index, df,
234
- st.session_state.genre_filter, st.session_state.year_filter,
235
- st.session_state.country_filter, st.session_state.type_filter, k
236
  )
237
 
238
- if results.empty:
239
- st.warning("Ничего не найдено.")
240
- else:
241
- st.success(f"Найдено: {len(results)}")
242
- for _, row in results.iterrows():
243
- col1, col2 = st.columns([1, 3])
244
- with col1:
245
- if row["image_url"]:
246
- try:
247
- st.image(row["image_url"], use_container_width=True)
248
- except Exception as e:
249
- st.info(f"Не удалось загрузить изображение: {e}")
250
- else:
251
- st.info("Нет изображения")
252
- with col2:
253
- st.markdown(f"### {row['tvshow_title']} ({row['year']})")
254
- st.caption(f"{row['basic_genres']} | {row['country'] or '—'} | {row['rating'] or '—'} | {row['type']} | {row['num_seasons']} сез.")
255
- st.write(extract_intro_paragraph(row["description"]))
256
- if row["actors"]:
257
- st.caption(f"Актёры: {row['actors']}")
258
- if row["url"]:
259
- st.markdown(f"[Подробнее]({row['url']})")
260
- st.divider()
261
-
262
- # Кнопка для AI-рекомендаций должна быть вне цикла
263
- if st.button("AI: почему эти подходят и что ещё посмотреть", key="ai_button"):
264
- st.session_state.ai_clicked = True
265
-
266
- # Сброс флага поиска
267
  st.session_state.search_clicked = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
-
270
- if st.session_state.ai_clicked and "results" in locals() and not results.empty and llm is not None:
 
 
 
 
 
271
  st.markdown("### Рекомендации AI:")
272
  with st.spinner("Генерация ответа AI..."):
273
- st.write(generate_rag_response(st.session_state.query_input, results, llm))
274
 
275
  st.sidebar.write(f"Всего записей: {len(df)}")
276
 
 
11
  import random
12
  import tempfile
13
 
14
+ # Удаляем блок с ручной установкой env vars, так как он вызывает проблемы.
15
+ # Streamlit должен работать с .streamlit/config.toml
16
 
17
+ # Пути к файлам относительно текущего скрипта
18
  HERE = os.path.dirname(os.path.abspath(__file__))
19
  CSV_PATH = os.path.join(HERE, "tvshows_processed2.csv")
20
  EMB_PATH = os.path.join(HERE, "embeddings.npy")
 
117
 
118
  def semantic_search(query, embedder, index, df, genre=None, year=None, country=None, vtype=None, k=5):
119
  if not query.strip():
120
+ return pd.DataFrame()
121
  query_embedding = embedder.encode([query])
122
  faiss.normalize_L2(query_embedding)
123
  dists, idxs = index.search(query_embedding, max(k*3, k))
 
167
  st.set_page_config(page_title="Поиск фильмов и сериалов + AI", layout="wide")
168
  st.title("Семантический поиск фильмов и сериалов с AI")
169
 
170
+ # Инициализация состояния
171
+ for key, default in {
172
+ "query_input": "", "button_query": "", "genre_filter_key": "Все",
173
+ "year_filter_key": "Все", "country_filter_key": "Все",
174
+ "type_filter_key": "Все", "search_clicked": False, "ai_clicked": False, "results": pd.DataFrame()
175
+ }.items():
176
+ if key not in st.session_state:
177
+ st.session_state[key] = default
 
 
 
 
 
 
 
178
 
179
  df = load_data()
180
  embedder = init_embedder()
181
  _, index = load_embeddings_and_index()
182
  llm = init_groq_llm()
183
 
184
+ # Фильтры
185
  colf1, colf2, colf3, colf4 = st.columns(4)
186
  with colf1:
187
  genres = ["Все"] + sorted(set(sum([g.split(", ") for g in df["basic_genres"].dropna().unique()], [])))
188
+ st.selectbox("Жанр", genres, key="genre_filter_key")
189
  with colf2:
190
  years = ["Все"] + [str(y) for y in sorted(df["year"].unique())]
191
+ st.selectbox("Год", years, key="year_filter_key")
192
  with colf3:
193
  countries = ["Все"] + sorted([c for c in df["country"].dropna().unique()])
194
+ st.selectbox("Страна", countries, key="country_filter_key")
195
  with colf4:
196
  vtypes = ["Все"] + sorted(df["type"].dropna().unique())
197
+ st.selectbox("Тип", vtypes, key="type_filter_key")
198
 
199
  k = st.slider("Количество результатов:", 1, 20, 5, key="k_slider")
200
+
201
+ # Виджет ввода текста
202
  st.text_input("Введите ключевые слова или сюжет:", key="query_input")
203
 
204
+ # Обработка кнопок
205
+ def handle_button_click(query_from_button):
206
+ st.session_state.button_query = query_from_button
207
  st.session_state.search_clicked = True
208
  st.session_state.ai_clicked = False
209
 
210
  nav1, nav2, nav3, nav4 = st.columns(4)
211
  with nav1:
212
  if st.button("Случайный фильм/сериал"):
213
+ handle_button_click(random.choice(df["tvshow_title"]))
214
  with nav2:
215
+ if st.button("ТОП по жанру") and st.session_state.genre_filter_key != "Все":
216
+ handle_button_click(st.session_state.genre_filter_key)
217
  with nav3:
218
  if st.button("Новинки"):
219
+ handle_button_click(str(max(df["year"])))
220
  with nav4:
221
  if st.button("Искать"):
222
+ handle_button_click(st.session_state.query_input)
223
+
224
+ # Итоговый запрос для поиска - либо из текстового поля, либо от кнопки
225
+ final_query = st.session_state.button_query if st.session_state.search_clicked else st.session_state.query_input
226
 
227
  # 6. Отображение результатов
228
+ if st.session_state.search_clicked and final_query.strip():
229
  with st.spinner("Поиск..."):
230
+ st.session_state.results = semantic_search(
231
+ final_query, embedder, index, df,
232
+ st.session_state.genre_filter_key, st.session_state.year_filter_key,
233
+ st.session_state.country_filter_key, st.session_state.type_filter_key, k
234
  )
235
 
236
+ # Сброс флага после выполнения поиска, чтобы избежать "дрожания"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  st.session_state.search_clicked = False
238
+
239
+ # Отображение результатов поиска
240
+ if not st.session_state.results.empty:
241
+ st.success(f"Найдено: {len(st.session_state.results)}")
242
+ for _, row in st.session_state.results.iterrows():
243
+ col1, col2 = st.columns([1, 3])
244
+ with col1:
245
+ if row["image_url"]:
246
+ try:
247
+ st.image(row["image_url"], use_container_width=True)
248
+ except:
249
+ st.info("Нет изображения или не удалось загрузить")
250
+ else:
251
+ st.info("Нет изображения")
252
+ with col2:
253
+ st.markdown(f"### {row['tvshow_title']} ({row['year']})")
254
+ st.caption(f"{row['basic_genres']} | {row['country'] or '—'} | {row['rating'] or '—'} | {row['type']} | {row['num_seasons']} сез.")
255
+ st.write(extract_intro_paragraph(row["description"]))
256
+ if row["actors"]:
257
+ st.caption(f"Актёры: {row['actors']}")
258
+ if row["url"]:
259
+ st.markdown(f"[Подробнее]({row['url']})")
260
+ st.divider()
261
 
262
+ # Кнопка для AI-рекомендаций должна быть вне цикла
263
+ if st.button("AI: почему эти подходят и что ещё посмотреть", key="ai_button"):
264
+ st.session_state.ai_clicked = True
265
+ elif st.session_state.search_clicked: # Отображаем предупреждение только если был поиск
266
+ st.warning("Ничего не найдено.")
267
+
268
+ if st.session_state.ai_clicked and not st.session_state.results.empty and llm is not None:
269
  st.markdown("### Рекомендации AI:")
270
  with st.spinner("Генерация ответа AI..."):
271
+ st.write(generate_rag_response(final_query, st.session_state.results, llm))
272
 
273
  st.sidebar.write(f"Всего записей: {len(df)}")
274