MacKov commited on
Commit
984c51c
·
verified ·
1 Parent(s): 8cad92e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +204 -96
app.py CHANGED
@@ -2,128 +2,236 @@ import os
2
  import gradio as gr
3
  import requests
4
  import pandas as pd
5
- from smolagents import CodeAgent, InferenceClientModel, Tool
 
 
 
 
 
 
 
 
 
6
 
 
7
 
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
-
11
- # --- Define Tools ---
12
- # Tool 1: Simple Grocery Categorizer
13
- class GroceryCategorizerTool(Tool):
14
- name = "grocery_categorizer"
15
- description = "Categorizes grocery items into vegetables, fruits, and others."
16
- inputs = {"items": {"type": "string", "description": "Comma-separated list of grocery items"}}
17
- output_type = "string"
18
-
19
- def forward(self, items: str) -> str:
20
- vegetables = ["bell pepper", "broccoli", "celery", "corn",
21
- "green beans", "sweet potatoes", "zucchini"]
22
- items_list = [item.strip() for item in items.split(",")]
23
- categorized = [item for item in items_list if item in vegetables]
24
- return ", ".join(sorted(categorized)) if categorized else "No vegetables found."
25
-
26
- # Tool 2: Generic reasoning fallback (simulated here)
27
- class ReasoningTool(Tool):
28
- name = "reasoning_tool"
29
- description = "Provides reasoning-based answers for general questions."
30
- inputs = {"question": {"type": "string", "description": "The question to answer"}}
31
- output_type = "string"
32
-
33
- def forward(self, question: str) -> str:
34
- # Здесь можно подключить реальную LLM или веб-поиск
35
- return "Answer generated via SmolAgents reasoning."
36
-
37
- # --- Initialize Agent ---
38
-
39
- hf_api_key = os.environ.get("HF_TOKEN")
40
- agent = CodeAgent(
41
- tools=[GroceryCategorizerTool(), ReasoningTool()],
42
- model=InferenceClientModel(api_key=hf_api_key)
43
- )
44
- print("HF_TOKEN available:", bool(hf_api_key))
45
- # --- Run & Submit Function ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  def run_and_submit_all(profile: gr.OAuthProfile | None):
47
  space_id = os.getenv("SPACE_ID")
48
- if not profile:
49
- return "Please Login to Hugging Face with the button.", None
50
- username = profile.username
 
 
51
 
52
  api_url = DEFAULT_API_URL
53
  questions_url = f"{api_url}/questions"
54
  submit_url = f"{api_url}/submit"
55
 
56
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
57
-
58
- # Fetch questions
59
  try:
60
- response = requests.get(questions_url, timeout=15)
61
- response.raise_for_status()
62
- questions_data = response.json()
63
- if not questions_data:
64
- return "Fetched questions list is empty or invalid format.", None
65
  except Exception as e:
66
- return f"Error fetching questions: {e}", None
67
 
68
- results_log = []
69
- answers_payload = []
70
 
71
- for item in questions_data:
72
- task_id = item.get("task_id")
73
- question_text = item.get("question")
74
- if not task_id or question_text is None:
 
 
 
 
 
 
 
 
 
 
 
 
75
  continue
76
-
77
  try:
78
- # Используем агент для автоматического выбора инструмента
79
- if "vegetables" in question_text.lower() or "grocery" in question_text.lower():
80
- submitted_answer = agent.run(f'GroceryCategorizerTool: {question_text}')
81
- else:
82
- submitted_answer = agent.run(f'ReasoningTool: {question_text}')
83
-
84
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
85
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
86
-
87
  except Exception as e:
88
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
 
 
 
89
 
90
- if not answers_payload:
91
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
92
 
93
- # Submit answers
94
- submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
95
  try:
96
- response = requests.post(submit_url, json=submission_data, timeout=60)
97
- response.raise_for_status()
98
- result_data = response.json()
99
- final_status = (
100
- f"Submission Successful!\n"
101
- f"User: {result_data.get('username')}\n"
102
- f"Overall Score: {result_data.get('score', 'N/A')}% "
103
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
104
- f"Message: {result_data.get('message', 'No message received.')}"
105
  )
106
- return final_status, pd.DataFrame(results_log)
107
  except Exception as e:
108
- return f"Submission failed: {e}", pd.DataFrame(results_log)
109
 
110
- # --- Gradio Interface ---
111
  with gr.Blocks() as demo:
112
- gr.Markdown("# SmolAgents Autonomous Evaluation Runner")
113
- gr.Markdown(
114
- """
115
- **Instructions:**
116
- 1. Log in with your Hugging Face account.
117
- 2. Click 'Run Evaluation & Submit All Answers'.
118
- 3. SmolAgents will autonomously select tools and answer questions.
119
- """
120
- )
121
-
122
  gr.LoginButton()
123
- run_button = gr.Button("Run Evaluation & Submit All Answers")
124
- status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
125
- results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
126
- run_button.click(fn=run_and_submit_all, outputs=[status_output, results_table])
127
 
128
  if __name__ == "__main__":
 
129
  demo.launch(debug=True, share=False)
 
2
  import gradio as gr
3
  import requests
4
  import pandas as pd
5
+ from pathlib import Path
6
+
7
+ from smolagents import (
8
+ CodeAgent,
9
+ InferenceClientModel,
10
+ DuckDuckGoSearchTool,
11
+ VisitWebpageTool,
12
+ PythonInterpreterTool,
13
+ tool
14
+ )
15
 
16
+ from pypdf import PdfReader
17
 
18
  # --- Constants ---
19
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
20
+ MODEL_ID = "mistralai/Mixtral-8x7B-Instruct-v0.1" # ← бесплатная и стабильная модель
21
+
22
+ # --- Advanced Agent ---
23
+ class BasicAgent:
24
+ def __init__(self):
25
+ print("Инициализация агента с Mixtral-8x7B...")
26
+
27
+ self.model = InferenceClientModel(
28
+ model_id=MODEL_ID,
29
+ token=os.getenv("HF_TOKEN"),
30
+ temperature=0.05,
31
+ max_tokens=768, # уменьшено для экономии лимита
32
+ )
33
+
34
+ tools = [
35
+ DuckDuckGoSearchTool(max_results=10),
36
+ VisitWebpageTool(),
37
+ PythonInterpreterTool(),
38
+ ]
39
+
40
+ @tool
41
+ def download_file(url: str) -> str:
42
+ """
43
+ Скачивает файл по URL.
44
+ Args:
45
+ url (str): URL файла
46
+ Returns:
47
+ str: Путь или ошибка
48
+ """
49
+ try:
50
+ downloads = Path("./downloads")
51
+ downloads.mkdir(exist_ok=True)
52
+ fname = url.split("/")[-1].split("?")[0] or "file"
53
+ path = downloads / fname
54
+
55
+ r = requests.get(url, stream=True, timeout=45)
56
+ r.raise_for_status()
57
+ with open(path, "wb") as f:
58
+ for chunk in r.iter_content(8192):
59
+ f.write(chunk)
60
+ return f"Скачано: {path.absolute()}. Теперь используй read_pdf или read_excel для анализа."
61
+ except Exception as e:
62
+ return f"Ошибка скачивания: {str(e)}"
63
+
64
+ @tool
65
+ def read_pdf(path: str) -> str:
66
+ """
67
+ Читает PDF.
68
+ Args:
69
+ path (str): Путь
70
+ Returns:
71
+ str: Текст (до 4000 символов)
72
+ """
73
+ try:
74
+ reader = PdfReader(path)
75
+ text = "\n".join(page.extract_text() or "" for page in reader.pages)
76
+ return text[:4000]
77
+ except Exception as e:
78
+ return f"Ошибка PDF: {str(e)}"
79
+
80
+ @tool
81
+ def read_excel(path: str, sheet: str = None) -> str:
82
+ """
83
+ Читает Excel.
84
+ Args:
85
+ path (str): Путь
86
+ sheet (str, optional): Лист
87
+ Returns:
88
+ str: Таблица или ошибка
89
+ """
90
+ try:
91
+ df = pd.read_excel(path, sheet_name=sheet)
92
+ return df.to_string(max_rows=20, max_cols=10)
93
+ except Exception as e:
94
+ return f"Ошибка Excel: {str(e)}"
95
+
96
+ tools.extend([download_file, read_pdf, read_excel])
97
+
98
+ self.agent = CodeAgent(
99
+ tools=tools,
100
+ model=self.model,
101
+ add_base_tools=True,
102
+ max_steps=12, # уменьшено — меньше запросов к API
103
+ )
104
+ print("Агент готов!")
105
+
106
+ def __call__(self, question: str) -> str:
107
+ print(f"Вопрос: {question[:120]}...")
108
+
109
+ # Обрезка длинных вопросов
110
+ if len(question) > 2000:
111
+ question = question[:2000] + "\n[Обрезано из-за длины. Отвечай кратко.]"
112
+
113
+ # Хак для файлов/аудио/видео/attached
114
+ q = question.lower()
115
+ if any(k in q for k in [".mp3", "audio", "recording", "voice", "youtube.com", "video", "attached", "file", "excel", "pdf", "image", "jpg", "png"]):
116
+ question += "\nЕсли в вопросе упоминается файл, URL или attached — ОБЯЗАТЕЛЬНО используй download_file, затем read_pdf или read_excel. Отвечай ТОЛЬКО по содержимому файла. НЕ ПРИДУМЫВАЙ числа, имена или данные."
117
+
118
+ # Хак для шахмат
119
+ if "chess" in q or "image" in q or ".jpg" in q or ".png" in q:
120
+ question += "\nЕсли есть URL изображения — скачай и опиши позицию или ищи похожую. НЕ ПРИДУМЫВАЙ ход."
121
+
122
+ try:
123
+ result = self.agent.run(question)
124
+ answer = str(result).strip()
125
+
126
+ # Жёсткая очистка ответа
127
+ prefixes = [
128
+ "Final Answer", "Final answer", "Answer:", "The answer is",
129
+ "So the final answer is", "```", "boxed{", "}", "[/INST]", "</s>",
130
+ "Thought:", "Observation:", "Action:"
131
+ ]
132
+ for p in prefixes:
133
+ if p.lower() in answer.lower():
134
+ answer = answer.split(p, 1)[-1].strip(": []{}\n`")
135
+ break
136
+
137
+ if answer.startswith("[") and answer.endswith("]"):
138
+ answer = answer[1:-1].strip()
139
+
140
+ answer = answer.strip()
141
+
142
+ # Защита от длинных/придуманных ответов
143
+ if len(answer) > 250 or "придум" in answer.lower() or answer.count(",") > 15:
144
+ answer = answer[:150] + "..." if len(answer) > 150 else answer
145
+
146
+ print(f"Ответ: {answer[:150]}...")
147
+ return answer or "Нет ответа"
148
+
149
+ except Exception as e:
150
+ err_str = str(e)
151
+ if "402" in err_str or "Payment Required" in err_str:
152
+ return "Лимит API исчерпан. Подожди 10–15 минут или используй модель Mixtral-8x7B."
153
+ err = f"Ошибка: {err_str[:200]}"
154
+ print(err)
155
+ return err
156
+
157
+ # --- run_and_submit_all (без изменений) ---
158
  def run_and_submit_all(profile: gr.OAuthProfile | None):
159
  space_id = os.getenv("SPACE_ID")
160
+ if profile:
161
+ username = profile.username
162
+ print(f"Вход: {username}")
163
+ else:
164
+ return "Войдите в HF", None
165
 
166
  api_url = DEFAULT_API_URL
167
  questions_url = f"{api_url}/questions"
168
  submit_url = f"{api_url}/submit"
169
 
 
 
 
170
  try:
171
+ agent = BasicAgent()
 
 
 
 
172
  except Exception as e:
173
+ return f"Ошибка агента: {e}", None
174
 
175
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
 
176
 
177
+ try:
178
+ resp = requests.get(questions_url, timeout=15)
179
+ resp.raise_for_status()
180
+ questions = resp.json()
181
+ if not questions:
182
+ return "Вопросов нет", None
183
+ print(f"Вопросов: {len(questions)}")
184
+ except Exception as e:
185
+ return f"Ошибка вопросов: {e}", None
186
+
187
+ results = []
188
+ payload = []
189
+ for item in questions:
190
+ tid = item.get("task_id")
191
+ q = item.get("question")
192
+ if not tid or not q:
193
  continue
 
194
  try:
195
+ ans = agent(q)
196
+ payload.append({"task_id": tid, "submitted_answer": ans})
197
+ results.append({"Task ID": tid, "Question": q, "Answer": ans})
 
 
 
 
 
 
198
  except Exception as e:
199
+ results.append({"Task ID": tid, "Question": q, "Answer": f"ERROR: {e}"})
200
+
201
+ if not payload:
202
+ return "Нет ответов", pd.DataFrame(results)
203
 
204
+ data = {"username": username.strip(), "agent_code": agent_code, "answers": payload}
 
205
 
 
 
206
  try:
207
+ resp = requests.post(submit_url, json=data, timeout=60)
208
+ resp.raise_for_status()
209
+ res = resp.json()
210
+ status = (
211
+ f"Успех!\n"
212
+ f"Пользователь: {res.get('username')}\n"
213
+ f"Балл: {res.get('score', 'N/A')}% "
214
+ f"({res.get('correct_count', '?')}/{res.get('total_attempted', '?')})\n"
215
+ f"{res.get('message', '')}"
216
  )
217
+ return status, pd.DataFrame(results)
218
  except Exception as e:
219
+ return f"Ошибка отправки: {e}", pd.DataFrame(results)
220
 
221
+ # --- Gradio ---
222
  with gr.Blocks() as demo:
223
+ gr.Markdown("# Агент для финального задания")
224
+ gr.Markdown("""
225
+ 1. Клонируй и дорабатывай.
226
+ 2. Войди через кнопку.
227
+ 3. Нажми кнопку увидишь score.
228
+ """)
 
 
 
 
229
  gr.LoginButton()
230
+ btn = gr.Button("Запустить оценку и отправить")
231
+ status = gr.Textbox(label="Результат", lines=6)
232
+ table = gr.DataFrame(label="Ответы", wrap=True)
233
+ btn.click(run_and_submit_all, outputs=[status, table])
234
 
235
  if __name__ == "__main__":
236
+ print("Запуск...")
237
  demo.launch(debug=True, share=False)