opex792 commited on
Commit
f13beaa
·
verified ·
1 Parent(s): 71bb70a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -144
app.py CHANGED
@@ -13,7 +13,6 @@ import logging
13
  from sklearn.preprocessing import normalize
14
  from concurrent.futures import ThreadPoolExecutor
15
  import requests
16
- import voyageai
17
 
18
  # Настройка логирования
19
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -39,13 +38,10 @@ logging.info(f"Загрузка модели {model_name}...")
39
  model = SentenceTransformer(model_name)
40
  logging.info("Модель загружена успешно.")
41
 
42
- # Voyage AI API Key
43
- VOYAGE_API_KEY = os.environ.get("VOYAGE_API_KEY")
44
- if VOYAGE_API_KEY is None:
45
- raise ValueError("VOYAGE_API_KEY environment variable not set.")
46
-
47
- # Инициализация клиента Voyage AI
48
- vo = voyageai.Client(api_key=VOYAGE_API_KEY)
49
 
50
  # Имена таблиц
51
  embeddings_table = "movie_embeddings"
@@ -82,20 +78,8 @@ batch_size = 32
82
  # Количество потоков для параллельной обработки
83
  num_threads = 5
84
 
85
- # Количество потоков для параллельного реранкинга
86
- rerank_threads = 3 # Ограничено лимитом RPM
87
-
88
- # Лимиты Voyage AI (запросов в минуту, токенов в минуту) - БЕСПЛАТНЫЙ АККАУНТ
89
- RPM_LIMIT = 3
90
- TPM_LIMIT = 10000
91
-
92
- # Переменные для отслеживания текущего использования
93
- current_rpm = 0
94
- current_tpm = 0
95
- last_reset_time = time.time()
96
-
97
- # Среднее количество токенов на описание фильма (можно вычислить один раз при запуске)
98
- avg_tokens_per_movie = 150 # Замените на более точное значение, если оно известно
99
 
100
  def get_db_connection():
101
  """Устанавливает соединение с базой данных."""
@@ -314,147 +298,55 @@ def get_movie_embeddings(conn):
314
  logging.error(f"Ошибка при загрузке эмбеддингов фильмов: {e}")
315
  return movie_embeddings
316
 
317
- def check_and_wait_for_limits():
318
- """Проверяет лимиты RPM и TPM и ожидает, если они исчерпаны."""
319
- global current_rpm, current_tpm, last_reset_time
320
-
321
- elapsed_time = time.time() - last_reset_time
322
-
323
- if elapsed_time >= 60:
324
- current_rpm = 0
325
- current_tpm = 0
326
- last_reset_time = time.time()
327
- logging.info("Лимиты RPM и TPM сброшены.")
328
-
329
- if current_rpm >= RPM_LIMIT or current_tpm >= TPM_LIMIT:
330
- wait_time = 60 - elapsed_time
331
- logging.warning(f"Превышены лимиты RPM ({current_rpm}/{RPM_LIMIT}) или TPM ({current_tpm}/{TPM_LIMIT}). Ожидание {wait_time:.2f} секунд...")
332
- time.sleep(max(0, wait_time))
333
- current_rpm = 0
334
- current_tpm = 0
335
- last_reset_time = time.time()
336
- logging.info("Лимиты RPM и TPM сброшены после ожидания.")
337
-
338
- def create_optimized_batches(query, results, max_tokens_per_batch=TPM_LIMIT):
339
- """Создает батчи для реранкинга, оптимизированные по количеству токенов."""
340
- global avg_tokens_per_movie
341
-
342
- batches = []
343
- current_batch = []
344
- current_batch_tokens = 0
345
-
346
- query_tokens = vo.count_tokens([query], model="rerank-2")
347
-
348
- for movie_id, _ in results:
349
- movie = next((m for m in movies_data if m['id'] == movie_id), None)
350
- if movie:
351
- movie_info = f"Название: {movie['name']}\nГод: {movie['year']}\nЖанры: {movie['genreslist']}\nОписание: {movie['description']}"
352
-
353
- # Считаем токены, но не отправляем запрос если лимит уже исчерпан
354
- estimated_movie_tokens = avg_tokens_per_movie
355
-
356
- if (current_batch_tokens + query_tokens + estimated_movie_tokens) <= max_tokens_per_batch:
357
- current_batch.append((movie_id, _))
358
- current_batch_tokens += estimated_movie_tokens
359
- else:
360
- batches.append(current_batch)
361
- current_batch = [(movie_id, _)]
362
- current_batch_tokens = estimated_movie_tokens
363
-
364
- if current_batch:
365
- batches.append(current_batch)
366
-
367
- return batches
368
-
369
- def rerank_batch_voyage(query, batch):
370
- """Переранжирует пакет результатов с помощью Voyage AI."""
371
- global current_rpm, current_tpm
372
-
373
- check_and_wait_for_limits()
374
-
375
- url = "https://api.voyageai.com/v1/rerank"
376
  headers = {
377
- "Authorization": f"Bearer {VOYAGE_API_KEY}",
378
- "content-type": "application/json"
379
  }
380
-
381
  documents = []
382
- movie_ids = []
383
- for movie_id, _ in batch:
384
  movie = next((m for m in movies_data if m['id'] == movie_id), None)
385
  if movie:
386
  movie_info = f"Название: {movie['name']}\nГод: {movie['year']}\nЖанры: {movie['genreslist']}\nОписание: {movie['description']}"
387
- documents.append(movie_info)
388
- movie_ids.append(movie_id)
389
 
390
-
391
-
392
- payload = {
393
  "query": query,
394
- "documents": documents,
395
- "model": "rerank-2", # Можно использовать rerank-2-lite для более быстрой, но менее точной модели
396
- "return_documents": False,
397
- "truncation": True
398
  }
399
-
 
 
400
  try:
401
- batch_tokens = vo.count_tokens([query] + documents, model="rerank-2")
402
-
403
- current_rpm += 1
404
- current_tpm += batch_tokens
405
-
406
- logging.info(f"Отправка запроса к Voyage AI. RPM: {current_rpm}/{RPM_LIMIT}, TPM: {current_tpm}/{TPM_LIMIT}, Токенов в запросе: {batch_tokens}")
407
-
408
- response = requests.post(url, headers=headers, json=payload)
409
- response.raise_for_status() # Проверка на ошибки HTTP
410
  response_json = response.json()
411
 
412
  reranked_results = []
413
- for item in response_json['data']:
414
- reranked_results.append((movie_ids[item['index']], item['relevance_score']))
415
-
416
- logging.info(f"Voyage AI: Успешно переранжирован батч. Задействовано токенов: {response_json['usage']['total_tokens']}")
417
  return reranked_results
418
 
419
  except requests.exceptions.RequestException as e:
420
- logging.error(f"Ошибка запроса к Voyage AI: {e}")
421
-
422
- if response.status_code == 429: # Too Many Requests
423
- logging.warning("Слишком много запросов к Voyage AI. Ожидание сброса лимитов...")
424
- check_and_wait_for_limits()
425
- return rerank_batch_voyage(query, batch) # Повторная попытка после ожидания
426
-
427
  return []
428
  except KeyError as e:
429
- logging.error(f"Ошибка обработки ответа от Voyage AI: {e}. Полный ответ: {response_json}")
430
  return []
431
 
432
- def rerank_results(query, results):
433
- """Переранжирует результаты поиска с помощью Voyage AI."""
434
  logging.info(f"Начало переранжирования для запроса: '{query}'")
435
-
436
- # Создаем оптимизированные батчи
437
- batches = create_optimized_batches(query, results)
438
-
439
- reranked_results = []
440
-
441
- with ThreadPoolExecutor(max_workers=rerank_threads) as executor:
442
- futures = []
443
- batch_num = 0
444
- for batch in batches:
445
- logging.info(f"Отправка на переранжирование батча {batch_num+1} ({len(batch)} фильмов)")
446
- future = executor.submit(rerank_batch_voyage, query, batch)
447
- futures.append(future)
448
- batch_num += 1
449
-
450
- # Сбор результатов
451
- for i, future in enumerate(futures):
452
- try:
453
- batch_result = future.result()
454
- reranked_results.extend(batch_result)
455
- logging.info(f"Завершен реранк батча {i+1}")
456
- except Exception as e:
457
- logging.error(f"Ошибка при переранжировании батча {i+1}: {e}")
458
 
459
  reranked_results = sorted(reranked_results, key=lambda x: x[1], reverse=True)
460
  logging.info("Переранжирование завершено.")
@@ -512,7 +404,7 @@ def search_movies(query, top_k=20):
512
  results = []
513
 
514
  # Переранжируем результаты
515
- reranked_results = rerank_results(query, results)
516
 
517
  output = ""
518
  for movie_id, score in reranked_results[:top_k]:
@@ -522,7 +414,7 @@ def search_movies(query, top_k=20):
522
  output += f"<h3>{movie['name']} ({movie['year']})</h3>\n"
523
  output += f"<p><strong>Жанры:</strong> {movie['genreslist']}</p>\n"
524
  output += f"<p><strong>Описание:</strong> {movie['description']}</p>\n"
525
- output += f"<p><strong>Релевантность (Voyage AI reranker score):</strong> {score:.4f}</p>\n"
526
  output += "<hr>\n"
527
 
528
  search_time = time.time() - start_time
 
13
  from sklearn.preprocessing import normalize
14
  from concurrent.futures import ThreadPoolExecutor
15
  import requests
 
16
 
17
  # Настройка логирования
18
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
38
  model = SentenceTransformer(model_name)
39
  logging.info("Модель загружена успешно.")
40
 
41
+ # Jina AI API Key
42
+ JINA_API_KEY = os.environ.get("JINA_API_KEY")
43
+ if JINA_API_KEY is None:
44
+ raise ValueError("JINA_API_KEY environment variable not set.")
 
 
 
45
 
46
  # Имена таблиц
47
  embeddings_table = "movie_embeddings"
 
78
  # Количество потоков для параллельной обработки
79
  num_threads = 5
80
 
81
+ # Количество потоков для параллельного реранкинга (Jina AI не имеет жестких ограничений)
82
+ rerank_threads = 5
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
  def get_db_connection():
85
  """Устанавливает соединение с базой данных."""
 
298
  logging.error(f"Ошибка при загрузке эмбеддингов фильмов: {e}")
299
  return movie_embeddings
300
 
301
+ def rerank_with_jina(query, results, top_n):
302
+ """Переранжирует результаты с помощью Jina AI."""
303
+ url = 'https://api.jina.ai/v1/rerank'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  headers = {
305
+ 'Content-Type': 'application/json',
306
+ 'Authorization': f'Bearer {JINA_API_KEY}'
307
  }
308
+
309
  documents = []
310
+ for movie_id, _ in results:
 
311
  movie = next((m for m in movies_data if m['id'] == movie_id), None)
312
  if movie:
313
  movie_info = f"Название: {movie['name']}\nГод: {movie['year']}\nЖанры: {movie['genreslist']}\nОписание: {movie['description']}"
314
+ documents.append({"text": movie_info})
 
315
 
316
+ data = {
317
+ "model": "jina-reranker-v2-base-multilingual",
 
318
  "query": query,
319
+ "top_n": top_n,
320
+ "documents": documents
 
 
321
  }
322
+
323
+ logging.info(f"Отправка запроса к Jina AI для переранжирования {len(documents)} документов...")
324
+
325
  try:
326
+ response = requests.post(url, headers=headers, json=data)
327
+ response.raise_for_status()
 
 
 
 
 
 
 
328
  response_json = response.json()
329
 
330
  reranked_results = []
331
+ for result in response_json['results']:
332
+ reranked_results.append((results[result['index']][0], result['relevance_score']))
333
+
334
+ logging.info(f"Jina AI: Успешно переранжировано. Задействовано токенов: {response_json['usage']['total_tokens']}")
335
  return reranked_results
336
 
337
  except requests.exceptions.RequestException as e:
338
+ logging.error(f"Ошибка запроса к Jina AI: {e}")
 
 
 
 
 
 
339
  return []
340
  except KeyError as e:
341
+ logging.error(f"Ошибка обработки ответа от Jina AI: {e}. Полный ответ: {response_json}")
342
  return []
343
 
344
+ def rerank_results(query, results, top_k):
345
+ """Переранжирует результаты поиска с помощью Jina AI."""
346
  logging.info(f"Начало переранжирования для запроса: '{query}'")
347
+
348
+ # Jina AI не имее�� жестких ограничений, поэтому можем обрабатывать все результаты за раз
349
+ reranked_results = rerank_with_jina(query, results, top_k)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
 
351
  reranked_results = sorted(reranked_results, key=lambda x: x[1], reverse=True)
352
  logging.info("Переранжирование завершено.")
 
404
  results = []
405
 
406
  # Переранжируем результаты
407
+ reranked_results = rerank_results(query, results, top_k)
408
 
409
  output = ""
410
  for movie_id, score in reranked_results[:top_k]:
 
414
  output += f"<h3>{movie['name']} ({movie['year']})</h3>\n"
415
  output += f"<p><strong>Жанры:</strong> {movie['genreslist']}</p>\n"
416
  output += f"<p><strong>Описание:</strong> {movie['description']}</p>\n"
417
+ output += f"<p><strong>Релевантность (Jina AI reranker score):</strong> {score:.4f}</p>\n"
418
  output += "<hr>\n"
419
 
420
  search_time = time.time() - start_time