opex792 commited on
Commit
771b1f8
·
verified ·
1 Parent(s): eb35c9a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -63
app.py CHANGED
@@ -40,29 +40,19 @@ logging.info("Модель загружена успешно.")
40
 
41
  # Jina AI Reranker API
42
  JINA_API_URL = 'https://api.jina.ai/v1/rerank'
43
- JINA_API_KEY = os.environ.get("JINA_API_KEY") # Получение ключа из переменной окружения
44
  if JINA_API_KEY is None:
45
  raise ValueError("JINA_API_KEY environment variable not set.")
46
  JINA_RERANKER_MODEL = "jina-reranker-v2-base-multilingual"
47
 
48
-
49
  # Имена таблиц
50
  embeddings_table = "movie_embeddings"
51
  query_cache_table = "query_cache"
 
52
 
53
  # Максимальный размер таблицы кэша запросов в байтах (50MB)
54
  MAX_CACHE_SIZE = 50 * 1024 * 1024
55
 
56
- # Загружаем данные из файла movies.json
57
- try:
58
- import json
59
- with open("movies.json", "r", encoding="utf-8") as f:
60
- movies_data = json.load(f)
61
- logging.info(f"Загружено {len(movies_data)} фильмов из movies.json")
62
- except FileNotFoundError:
63
- logging.error("Ошибка: Файл movies.json не найден.")
64
- movies_data = []
65
-
66
  # Очередь для необработанных фильмов
67
  movies_queue = queue.Queue()
68
 
@@ -101,32 +91,29 @@ def setup_database():
101
  # Создаем расширение pgvector если его нет
102
  cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
103
 
104
- # Удаляем существующие таблицы если они есть
105
- # cur.execute(f"DROP TABLE IF EXISTS {embeddings_table}, {query_cache_table};")
106
-
107
  # Создаем таблицу для хранения эмбеддингов фильмов
108
  cur.execute(f"""
109
- CREATE TABLE {embeddings_table} (
110
  movie_id INTEGER PRIMARY KEY,
111
  embedding_crc32 BIGINT,
112
  string_crc32 BIGINT,
113
  model_name TEXT,
114
  embedding vector(1024)
115
  );
116
- CREATE INDEX ON {embeddings_table} (string_crc32);
117
  """)
118
 
119
  # Создаем таблицу для кэширования запросов
120
  cur.execute(f"""
121
- CREATE TABLE {query_cache_table} (
122
  query_crc32 BIGINT PRIMARY KEY,
123
  query TEXT,
124
  model_name TEXT,
125
  embedding vector(1024),
126
  created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
127
  );
128
- CREATE INDEX ON {query_cache_table} (query_crc32);
129
- CREATE INDEX ON {query_cache_table} (created_at);
130
  """)
131
 
132
  conn.commit()
@@ -158,14 +145,26 @@ def get_movies_without_embeddings():
158
  movies_to_process = []
159
  try:
160
  with conn.cursor() as cur:
161
- # Получаем список ID фильмов, которые уже есть в базе
162
  cur.execute(f"SELECT movie_id FROM {embeddings_table}")
163
  existing_ids = {row[0] for row in cur.fetchall()}
164
 
165
- # Фильтруем только те фильмы, которых нет в базе
166
- for movie in movies_data:
167
- if movie['id'] not in existing_ids:
168
- movies_to_process.append(movie)
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
  logging.info(f"Найдено {len(movies_to_process)} фильмов для об��аботки.")
171
  except Exception as e:
@@ -215,23 +214,22 @@ def process_batch(batch):
215
  return
216
 
217
  try:
218
- for movie in batch:
219
- embedding_string = f"Название: {movie['name']}\nГод: {movie['year']}\nЖанры: {movie['genreslist']}\nОписание: {movie['description']}"
220
- string_crc32 = calculate_crc32(embedding_string)
221
 
222
  # Проверяем существующий эмбеддинг
223
  existing_embedding = get_embedding_from_db(conn, embeddings_table, "string_crc32", string_crc32, model_name)
224
 
225
  if existing_embedding is None:
226
- embedding = encode_string(embedding_string)
227
  embedding_crc32 = calculate_crc32(str(embedding.tolist()))
228
 
229
- if insert_embedding(conn, embeddings_table, movie['id'], embedding_crc32, string_crc32, embedding):
230
- logging.info(f"Сохранен эмбеддинг для '{movie['name']}'")
231
  else:
232
- logging.error(f"Ошибка сохранения эмбеддинга для '{movie['name']}'")
233
  else:
234
- logging.info(f"Эмбеддинг для '{movie['name']}' уже существует")
235
  except Exception as e:
236
  logging.error(f"Ошибка при обработке пакета фильмов: {e}")
237
  finally:
@@ -281,32 +279,44 @@ def process_movies():
281
  processing_complete = True
282
  logging.info("Обработка фильмов завершена")
283
 
284
- def get_movie_embeddings(conn):
285
- """Загружает все эмбеддинги фильмов из базы данных."""
286
- movie_embeddings = {}
287
  try:
288
  with conn.cursor() as cur:
289
- cur.execute(f"SELECT movie_id, embedding FROM {embeddings_table}")
290
- for movie_id, embedding in cur.fetchall():
291
- # Находим название фильма по ID
292
- for movie in movies_data:
293
- if movie['id'] == movie_id:
294
- movie_embeddings[movie['name']] = normalize(np.array(embedding).reshape(1, -1))[0]
295
- break
296
- logging.info(f"Загружено {len(movie_embeddings)} эмбеддингов фильмов.")
 
 
 
 
297
  except Exception as e:
298
- logging.error(f"Ошибка при загрузке эмбеддингов фильмов: {e}")
299
- return movie_embeddings
300
 
301
  def rerank_with_api(query, results, top_k):
302
  """Переранжирует результаты с помощью Jina AI Reranker API."""
303
  logging.info(f"Начало переранжирования для запроса: '{query}'")
 
 
 
 
 
 
 
304
  documents = []
305
  for movie_id, _ in results:
306
- movie = next((m for m in movies_data if m['id'] == movie_id), None)
307
- if movie:
308
- movie_info = f"Название: {movie['name']}\nГод: {movie['year']}\nЖанры: {movie['genreslist']}\nОписание: {movie['description']}"
309
- documents.append(movie_info)
 
310
 
311
  headers = {
312
  'Content-Type': 'application/json',
@@ -318,18 +328,17 @@ def rerank_with_api(query, results, top_k):
318
  "top_n": top_k,
319
  "documents": documents
320
  }
321
- logging.info(f"Отправка данных на реранжировку (len): {len(data)}")
322
 
323
  try:
324
  response = requests.post(JINA_API_URL, headers=headers, json=data)
325
- response.raise_for_status() # Проверка на ошибки HTTP
326
  result = response.json()
327
- logging.info(f"Ответ от API реранжировщика (len): {len(result)}")
328
 
329
  reranked_results = []
330
  if 'results' in result:
331
  for item in result['results']:
332
-
333
  index = item['index']
334
  movie_id = results[index][0]
335
  reranked_results.append((movie_id, item['relevance_score']))
@@ -337,7 +346,6 @@ def rerank_with_api(query, results, top_k):
337
  logging.warning("Ответ от API не содержит ключа 'results'.")
338
 
339
  logging.info("Переранжирование завершено.")
340
- # time.sleep(0.1)
341
  return reranked_results
342
 
343
  except requests.exceptions.RequestException as e:
@@ -394,20 +402,29 @@ def search_movies(query, top_k=25):
394
  except Exception as e:
395
  logging.error(f"Ошибка при выполнении поискового запроса: {e}")
396
  results = []
 
 
397
 
398
  # Переранжируем результаты с помощью API
399
  reranked_results = rerank_with_api(query, results, top_k)
400
 
 
 
 
 
 
401
  output = ""
402
  for movie_id, score in reranked_results:
403
- # Находим фильм по ID
404
- movie = next((m for m in movies_data if m['id'] == movie_id), None)
405
- if movie:
406
- output += f"<h3>{movie['name']} ({movie['year']})</h3>\n"
407
- output += f"<p><strong>Жанры:</strong> {movie['genreslist']}</p>\n"
408
- output += f"<p><strong>Описание:</strong> {movie['description']}</p>\n"
409
  output += f"<p><strong>Релевантность (reranker score):</strong> {score:.4f}</p>\n"
410
  output += "<hr>\n"
 
 
411
 
412
  search_time = time.time() - start_time
413
  logging.info(f"Поиск выполнен за {search_time:.2f} секунд.")
@@ -419,8 +436,6 @@ def search_movies(query, top_k=25):
419
  return "<p>Произошла ошибка при выполнении поиска.</p>"
420
 
421
  finally:
422
- if conn:
423
- conn.close()
424
  search_in_progress = False
425
 
426
  # Запускаем обработку фильмов в отдельном потоке
 
40
 
41
  # Jina AI Reranker API
42
  JINA_API_URL = 'https://api.jina.ai/v1/rerank'
43
+ JINA_API_KEY = os.environ.get("JINA_API_KEY")
44
  if JINA_API_KEY is None:
45
  raise ValueError("JINA_API_KEY environment variable not set.")
46
  JINA_RERANKER_MODEL = "jina-reranker-v2-base-multilingual"
47
 
 
48
  # Имена таблиц
49
  embeddings_table = "movie_embeddings"
50
  query_cache_table = "query_cache"
51
+ movies_table = "Movies" # Имя таблицы с фильмами
52
 
53
  # Максимальный размер таблицы кэша запросов в байтах (50MB)
54
  MAX_CACHE_SIZE = 50 * 1024 * 1024
55
 
 
 
 
 
 
 
 
 
 
 
56
  # Очередь для необработанных фильмов
57
  movies_queue = queue.Queue()
58
 
 
91
  # Создаем расширение pgvector если его нет
92
  cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
93
 
 
 
 
94
  # Создаем таблицу для хранения эмбеддингов фильмов
95
  cur.execute(f"""
96
+ CREATE TABLE IF NOT EXISTS {embeddings_table} (
97
  movie_id INTEGER PRIMARY KEY,
98
  embedding_crc32 BIGINT,
99
  string_crc32 BIGINT,
100
  model_name TEXT,
101
  embedding vector(1024)
102
  );
103
+ CREATE INDEX IF NOT EXISTS idx_string_crc32 ON {embeddings_table} (string_crc32);
104
  """)
105
 
106
  # Создаем таблицу для кэширования запросов
107
  cur.execute(f"""
108
+ CREATE TABLE IF NOT EXISTS {query_cache_table} (
109
  query_crc32 BIGINT PRIMARY KEY,
110
  query TEXT,
111
  model_name TEXT,
112
  embedding vector(1024),
113
  created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
114
  );
115
+ CREATE INDEX IF NOT EXISTS idx_query_crc32 ON {query_cache_table} (query_crc32);
116
+ CREATE INDEX IF NOT EXISTS idx_created_at ON {query_cache_table} (created_at);
117
  """)
118
 
119
  conn.commit()
 
145
  movies_to_process = []
146
  try:
147
  with conn.cursor() as cur:
148
+ # Получаем список ID фильмов, которые уже есть в таблице эмбеддингов
149
  cur.execute(f"SELECT movie_id FROM {embeddings_table}")
150
  existing_ids = {row[0] for row in cur.fetchall()}
151
 
152
+ # Получаем список всех фильмов из таблицы Movies с подготовленной строкой
153
+ cur.execute(f"""
154
+ SELECT id, data,
155
+ 'Название: ' || data->>'name' ||
156
+ '\\nГод: ' || data->>'year' ||
157
+ '\\nЖанры: ' || (SELECT string_agg(genre->>'name', ', ') FROM jsonb_array_elements(data->'genres') AS genre) ||
158
+ '\\nОписание: ' || COALESCE(data->>'description', '')
159
+ AS prepared_string
160
+ FROM {movies_table}
161
+ """)
162
+ all_movies = cur.fetchall()
163
+
164
+ # Фильтруем только те фильмы, которых нет в таблице эмбеддингов
165
+ for movie_id, movie_data, prepared_string in all_movies:
166
+ if movie_id not in existing_ids:
167
+ movies_to_process.append((movie_id, movie_data, prepared_string))
168
 
169
  logging.info(f"Найдено {len(movies_to_process)} фильмов для об��аботки.")
170
  except Exception as e:
 
214
  return
215
 
216
  try:
217
+ for movie_id, movie_data, prepared_string in batch:
218
+ string_crc32 = calculate_crc32(prepared_string)
 
219
 
220
  # Проверяем существующий эмбеддинг
221
  existing_embedding = get_embedding_from_db(conn, embeddings_table, "string_crc32", string_crc32, model_name)
222
 
223
  if existing_embedding is None:
224
+ embedding = encode_string(prepared_string)
225
  embedding_crc32 = calculate_crc32(str(embedding.tolist()))
226
 
227
+ if insert_embedding(conn, embeddings_table, movie_id, embedding_crc32, string_crc32, embedding):
228
+ logging.info(f"Сохранен эмбеддинг для '{movie_data['name']}' (ID: {movie_id})")
229
  else:
230
+ logging.error(f"Ошибка сохранения эмбеддинга для '{movie_data['name']}' (ID: {movie_id})")
231
  else:
232
+ logging.info(f"Эмбеддинг для '{movie_data['name']}' (ID: {movie_id}) уже существует")
233
  except Exception as e:
234
  logging.error(f"Ошибка при обработке пакета фильмов: {e}")
235
  finally:
 
279
  processing_complete = True
280
  logging.info("Обработка фильмов завершена")
281
 
282
+ def get_movie_data_from_db(conn, movie_ids):
283
+ """Получает данные фильмов из таблицы Movies по списку ID."""
284
+ movie_data_dict = {}
285
  try:
286
  with conn.cursor() as cur:
287
+ cur.execute(f"""
288
+ SELECT id, data,
289
+ 'Название: ' || data->>'name' ||
290
+ '\\nГод: ' || data->>'year' ||
291
+ '\\nЖанры: ' || (SELECT string_agg(genre->>'name', ', ') FROM jsonb_array_elements(data->'genres') AS genre) ||
292
+ '\\nОписание: ' || COALESCE(data->>'description', '')
293
+ AS prepared_string
294
+ FROM {movies_table}
295
+ WHERE id IN %s
296
+ """, (tuple(movie_ids),))
297
+ for movie_id, movie_data, prepared_string in cur.fetchall():
298
+ movie_data_dict[movie_id] = (movie_data, prepared_string)
299
  except Exception as e:
300
+ logging.error(f"Ошибка при получении данных фильмов из БД: {e}")
301
+ return movie_data_dict
302
 
303
  def rerank_with_api(query, results, top_k):
304
  """Переранжирует результаты с помощью Jina AI Reranker API."""
305
  logging.info(f"Начало переранжирования для запроса: '{query}'")
306
+
307
+ # Получаем данные фильмов из БД
308
+ conn = get_db_connection()
309
+ movie_ids = [movie_id for movie_id, _ in results]
310
+ movie_data_dict = get_movie_data_from_db(conn, movie_ids)
311
+ conn.close()
312
+
313
  documents = []
314
  for movie_id, _ in results:
315
+ movie_data, prepared_string = movie_data_dict.get(movie_id, (None, None))
316
+ if movie_data:
317
+ documents.append(prepared_string)
318
+ else:
319
+ logging.warning(f"Данные для фильма с ID {movie_id} не найдены в БД.")
320
 
321
  headers = {
322
  'Content-Type': 'application/json',
 
328
  "top_n": top_k,
329
  "documents": documents
330
  }
331
+ logging.info(f"Отправка данных на реранжировку (documents count): {len(data['documents'])}")
332
 
333
  try:
334
  response = requests.post(JINA_API_URL, headers=headers, json=data)
335
+ response.raise_for_status()
336
  result = response.json()
337
+ logging.info(f"Ответ от API реранжировщика получен.")
338
 
339
  reranked_results = []
340
  if 'results' in result:
341
  for item in result['results']:
 
342
  index = item['index']
343
  movie_id = results[index][0]
344
  reranked_results.append((movie_id, item['relevance_score']))
 
346
  logging.warning("Ответ от API не содержит ключа 'results'.")
347
 
348
  logging.info("Переранжирование завершено.")
 
349
  return reranked_results
350
 
351
  except requests.exceptions.RequestException as e:
 
402
  except Exception as e:
403
  logging.error(f"Ошибка при выполнении поискового запроса: {e}")
404
  results = []
405
+ finally:
406
+ conn.close()
407
 
408
  # Переранжируем результаты с помощью API
409
  reranked_results = rerank_with_api(query, results, top_k)
410
 
411
+ conn = get_db_connection()
412
+ movie_ids = [movie_id for movie_id, _ in reranked_results]
413
+ movie_data_dict = get_movie_data_from_db(conn, movie_ids)
414
+ conn.close()
415
+
416
  output = ""
417
  for movie_id, score in reranked_results:
418
+ # Находим данные фильма
419
+ movie_data, _ = movie_data_dict.get(movie_id, (None, None))
420
+ if movie_data:
421
+ output += f"<h3>{movie_data['name']} ({movie_data['year']})</h3>\n"
422
+ output += f"<p><strong>Жанры:</strong> {', '.join([genre['name'] for genre in movie_data['genres']])}</p>\n"
423
+ output += f"<p><strong>Описание:</strong> {movie_data.get('description', '')}</p>\n"
424
  output += f"<p><strong>Релевантность (reranker score):</strong> {score:.4f}</p>\n"
425
  output += "<hr>\n"
426
+ else:
427
+ logging.warning(f"Данные для фильма с ID {movie_id} не найдены в БД.")
428
 
429
  search_time = time.time() - start_time
430
  logging.info(f"Поиск выполнен за {search_time:.2f} секунд.")
 
436
  return "<p>Произошла ошибка при выполнении поиска.</p>"
437
 
438
  finally:
 
 
439
  search_in_progress = False
440
 
441
  # Запускаем обработку фильмов в отдельном потоке