olnrso2 commited on
Commit
5474552
·
verified ·
1 Parent(s): 63cc79c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +27 -54
app.py CHANGED
@@ -1,23 +1,21 @@
1
  import os
2
  import json
3
  import glob
4
- import spaces # <--- Обязательно для ZeroGPU на бесплатном тарифе
5
  import requests
6
  import numpy as np
7
  import gradio as gr
8
 
9
- # Библиотеки для парсинга документов
10
  from pypdf import PdfReader
11
  import docx
12
  from pptx import Presentation
13
  import openpyxl
14
  from sentence_transformers import SentenceTransformer
15
 
16
- # 1. Загрузка легкой модели эмбеддингов
17
  print("Loading Embedding Model...")
18
  embedder = SentenceTransformer('cointegrated/rubert-tiny2')
19
 
20
- # Извлечение текста из разных форматов файлов
21
  def extract_text_from_file(file_path):
22
  ext = os.path.splitext(file_path)[1].lower()
23
  text = ""
@@ -47,26 +45,20 @@ def extract_text_from_file(file_path):
47
  with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
48
  text = f.read()
49
  except Exception as e:
50
- print(f"Ошибка чтения файла {file_path}: {e}")
51
  return text
52
 
53
- # Нарезка текста на смысловые фрагменты (чанки)
54
  def chunk_text(text, source_name, chunk_size=700, overlap=100):
55
  chunks = []
56
  for i in range(0, len(text), chunk_size - overlap):
57
  chunk = text[i:i + chunk_size].strip()
58
  if len(chunk) > 40:
59
- chunks.append({
60
- "text": chunk,
61
- "source": source_name
62
- })
63
  return chunks
64
 
65
- # Сканирование и индексация файлов из папки documents/
66
  def build_vector_index(folder="documents"):
67
  os.makedirs(folder, exist_ok=True)
68
  all_chunks = []
69
-
70
  files = glob.glob(f"{folder}/*.*")
71
  print(f"Найдено файлов в {folder}: {len(files)}")
72
 
@@ -83,31 +75,24 @@ def build_vector_index(folder="documents"):
83
 
84
  texts = [c["text"] for c in all_chunks]
85
  embeddings = embedder.encode(texts, convert_to_numpy=True, show_progress_bar=False)
86
-
87
  return all_chunks, embeddings
88
 
89
- # Запускаем индексацию файлов при старте проекта
90
  CHUNKS, EMBEDDINGS = build_vector_index("documents")
91
 
92
- # Функция быстрого векторного поиска по документам
93
  def retrieve_relevant_context(query, top_k=4):
94
  if len(CHUNKS) == 0 or EMBEDDINGS is None:
95
  return ""
96
-
97
  query_emb = embedder.encode([query], convert_to_numpy=True)
98
  scores = np.dot(EMBEDDINGS, query_emb.T).squeeze()
99
-
100
  if np.ndim(scores) == 0:
101
  scores = np.array([scores])
102
-
103
  top_indices = np.argsort(scores)[::-1][:top_k]
104
 
105
  context_blocks = []
106
  for idx in top_indices:
107
- if scores[idx] > 0.25: # Порог релевантности
108
  chunk = CHUNKS[idx]
109
  context_blocks.append(f"[Источник: {chunk['source']}]\n{chunk['text']}")
110
-
111
  return "\n\n---\n\n".join(context_blocks)
112
 
113
  SYSTEM_PROMPT = """Ты — «Инвест-Консультант Щёлково» — ведущий эксперт Администрации г.о. Щёлково.
@@ -120,18 +105,15 @@ SYSTEM_PROMPT = """Ты — «Инвест-Консультант Щёлково
120
 
121
  HF_TOKEN = os.environ.get("HF_TOKEN")
122
 
123
- @spaces.GPU # <--- Обязательный декоратор для ZeroGPU
124
  def respond(message, history):
125
  if not HF_TOKEN:
126
- yield "⚠️ Ошибка: В настройках Space (Settings -> Secrets) не найден секрет HF_TOKEN! Пожалуйста, добавьте HF_TOKEN."
127
  return
128
 
129
  user_text = message.get("text", "") if isinstance(message, dict) else str(message)
 
130
 
131
- # 1. Поиск релевантной информации в ваших документах
132
- retrieved_context = retrieve_relevant_context(user_text, top_k=4)
133
-
134
- # 2. Формирование диалога
135
  messages = [{"role": "system", "content": SYSTEM_PROMPT}]
136
 
137
  if history:
@@ -146,15 +128,11 @@ def respond(message, history):
146
  if r in ["user", "assistant"] and c:
147
  messages.append({"role": r, "content": str(c)})
148
 
149
- # Добавляем контекст из найденных документов к вопросу пользователя
150
- augmented_prompt = f"НАЙДЕННЫЕ ДОКУМЕНТЫ ИЗ БАЗЫ ЗНАНИЙ:\n{retrieved_context}\n\nВОПРОС ПОЛЬЗОВАТЕЛЯ: {user_text}"
151
  messages.append({"role": "user", "content": augmented_prompt})
152
 
153
  url = "https://router.huggingface.co/v1/chat/completions"
154
- headers = {
155
- "Authorization": f"Bearer {HF_TOKEN}",
156
- "Content-Type": "application/json"
157
- }
158
  payload = {
159
  "model": "Qwen/Qwen2.5-Coder-32B-Instruct",
160
  "messages": messages,
@@ -166,35 +144,30 @@ def respond(message, history):
166
  full_response = ""
167
  try:
168
  res = requests.post(url, headers=headers, json=payload, stream=True, timeout=30)
169
-
170
- if res.status_code != 200:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  yield f"⚠️ Ошибка сервера ({res.status_code}): {res.text}"
172
- return
173
-
174
- for line in res.iter_lines():
175
- if line:
176
- line_str = line.decode('utf-8')
177
- if line_str.startswith("data: "):
178
- data_str = line_str[6:].strip()
179
- if data_str == "[DONE]":
180
- break
181
- try:
182
- data_json = json.loads(data_str)
183
- if "choices" in data_json and len(data_json["choices"]) > 0:
184
- delta = data_json["choices"][0]["delta"].get("content", "")
185
- if delta:
186
- full_response += delta
187
- yield full_response
188
- except Exception:
189
- continue
190
  except Exception as e:
191
  yield f"⚠️ Ошибка подключения: {str(e)}"
192
 
193
- # Создаем веб-интерфейс
194
  demo = gr.ChatInterface(
195
  fn=respond,
196
  title="Инвест-Консультант г.о. Щёлково",
197
- description="Умный виртуальный ассистент с автопоиском по загруженным документам (PDF, Word, Excel, PowerPoint).",
198
  examples=[
199
  "Какие субсидии есть на покупку оборудования?",
200
  "Как получить землю за 1 рубль?",
 
1
  import os
2
  import json
3
  import glob
4
+ import spaces
5
  import requests
6
  import numpy as np
7
  import gradio as gr
8
 
 
9
  from pypdf import PdfReader
10
  import docx
11
  from pptx import Presentation
12
  import openpyxl
13
  from sentence_transformers import SentenceTransformer
14
 
15
+ # 1. Загрузка модели эмбеддингов
16
  print("Loading Embedding Model...")
17
  embedder = SentenceTransformer('cointegrated/rubert-tiny2')
18
 
 
19
  def extract_text_from_file(file_path):
20
  ext = os.path.splitext(file_path)[1].lower()
21
  text = ""
 
45
  with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
46
  text = f.read()
47
  except Exception as e:
48
+ print(f"Ошибка чтения {file_path}: {e}")
49
  return text
50
 
 
51
  def chunk_text(text, source_name, chunk_size=700, overlap=100):
52
  chunks = []
53
  for i in range(0, len(text), chunk_size - overlap):
54
  chunk = text[i:i + chunk_size].strip()
55
  if len(chunk) > 40:
56
+ chunks.append({"text": chunk, "source": source_name})
 
 
 
57
  return chunks
58
 
 
59
  def build_vector_index(folder="documents"):
60
  os.makedirs(folder, exist_ok=True)
61
  all_chunks = []
 
62
  files = glob.glob(f"{folder}/*.*")
63
  print(f"Найдено файлов в {folder}: {len(files)}")
64
 
 
75
 
76
  texts = [c["text"] for c in all_chunks]
77
  embeddings = embedder.encode(texts, convert_to_numpy=True, show_progress_bar=False)
 
78
  return all_chunks, embeddings
79
 
 
80
  CHUNKS, EMBEDDINGS = build_vector_index("documents")
81
 
 
82
  def retrieve_relevant_context(query, top_k=4):
83
  if len(CHUNKS) == 0 or EMBEDDINGS is None:
84
  return ""
 
85
  query_emb = embedder.encode([query], convert_to_numpy=True)
86
  scores = np.dot(EMBEDDINGS, query_emb.T).squeeze()
 
87
  if np.ndim(scores) == 0:
88
  scores = np.array([scores])
 
89
  top_indices = np.argsort(scores)[::-1][:top_k]
90
 
91
  context_blocks = []
92
  for idx in top_indices:
93
+ if scores[idx] > 0.25:
94
  chunk = CHUNKS[idx]
95
  context_blocks.append(f"[Источник: {chunk['source']}]\n{chunk['text']}")
 
96
  return "\n\n---\n\n".join(context_blocks)
97
 
98
  SYSTEM_PROMPT = """Ты — «Инвест-Консультант Щёлково» — ведущий эксперт Администрации г.о. Щёлково.
 
105
 
106
  HF_TOKEN = os.environ.get("HF_TOKEN")
107
 
108
+ @spaces.GPU
109
  def respond(message, history):
110
  if not HF_TOKEN:
111
+ yield "⚠️ Ошибка: В настройках (Settings -> Secrets) не найден секрет HF_TOKEN!"
112
  return
113
 
114
  user_text = message.get("text", "") if isinstance(message, dict) else str(message)
115
+ context = retrieve_relevant_context(user_text, top_k=4)
116
 
 
 
 
 
117
  messages = [{"role": "system", "content": SYSTEM_PROMPT}]
118
 
119
  if history:
 
128
  if r in ["user", "assistant"] and c:
129
  messages.append({"role": r, "content": str(c)})
130
 
131
+ augmented_prompt = f"НАЙДЕННЫЕ ДОКУМЕНТЫ ИЗ БАЗЫ ЗНАНИЙ:\n{context}\n\nВОПРОС ПОЛЬЗОВАТЕЛЯ: {user_text}"
 
132
  messages.append({"role": "user", "content": augmented_prompt})
133
 
134
  url = "https://router.huggingface.co/v1/chat/completions"
135
+ headers = {"Authorization": f"Bearer {HF_TOKEN}", "Content-Type": "application/json"}
 
 
 
136
  payload = {
137
  "model": "Qwen/Qwen2.5-Coder-32B-Instruct",
138
  "messages": messages,
 
144
  full_response = ""
145
  try:
146
  res = requests.post(url, headers=headers, json=payload, stream=True, timeout=30)
147
+ if res.status_code == 200:
148
+ for line in res.iter_lines():
149
+ if line:
150
+ line_str = line.decode('utf-8')
151
+ if line_str.startswith("data: "):
152
+ data_str = line_str[6:].strip()
153
+ if data_str == "[DONE]": break
154
+ try:
155
+ data_json = json.loads(data_str)
156
+ if "choices" in data_json and len(data_json["choices"]) > 0:
157
+ delta = data_json["choices"][0]["delta"].get("content", "")
158
+ if delta:
159
+ full_response += delta
160
+ yield full_response
161
+ except Exception: pass
162
+ else:
163
  yield f"⚠️ Ошибка сервера ({res.status_code}): {res.text}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  except Exception as e:
165
  yield f"⚠️ Ошибка подключения: {str(e)}"
166
 
 
167
  demo = gr.ChatInterface(
168
  fn=respond,
169
  title="Инвест-Консультант г.о. Щёлково",
170
+ description="Умный ассистент Отдела по инвестициям с поиском по вашей базе знаний.",
171
  examples=[
172
  "Какие субсидии есть на покупку оборудования?",
173
  "Как получить землю за 1 рубль?",