Spaces:
Sleeping
Sleeping
| import os | |
| import gradio as gr | |
| import requests | |
| import pandas as pd | |
| from pathlib import Path | |
| from smolagents import ( | |
| CodeAgent, | |
| InferenceClientModel, | |
| DuckDuckGoSearchTool, | |
| VisitWebpageTool, | |
| PythonInterpreterTool, | |
| tool | |
| ) | |
| from pypdf import PdfReader | |
| DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space" | |
| MODEL_ID = "Qwen/Qwen2.5-32B-Instruct" | |
| class BasicAgent: | |
| def __init__(self): | |
| print("Инициализация агента...") | |
| self.model = InferenceClientModel( | |
| model_id=MODEL_ID, | |
| token=os.getenv("HF_TOKEN"), | |
| temperature=0.05, | |
| max_tokens=1024, | |
| ) | |
| tools = [ | |
| DuckDuckGoSearchTool(max_results=10), | |
| VisitWebpageTool(), | |
| PythonInterpreterTool(), | |
| ] | |
| def download_file(url: str) -> str: | |
| """ | |
| Скачивает файл по URL. | |
| Args: | |
| url (str): URL файла | |
| Returns: | |
| str: Путь или ошибка | |
| """ | |
| try: | |
| downloads = Path("./downloads") | |
| downloads.mkdir(exist_ok=True) | |
| fname = url.split("/")[-1].split("?")[0] or "file" | |
| path = downloads / fname | |
| r = requests.get(url, stream=True, timeout=45) | |
| r.raise_for_status() | |
| with open(path, "wb") as f: | |
| for chunk in r.iter_content(8192): | |
| f.write(chunk) | |
| return f"Скачано: {path.absolute()}. Анализируй." | |
| except Exception as e: | |
| return f"Ошибка скачивания: {str(e)}" | |
| def read_pdf(path: str) -> str: | |
| """ | |
| Читает PDF. | |
| Args: | |
| path (str): Путь | |
| Returns: | |
| str: Текст (до 4000 символов) | |
| """ | |
| try: | |
| reader = PdfReader(path) | |
| text = "\n".join(page.extract_text() or "" for page in reader.pages) | |
| return text[:4000] | |
| except Exception as e: | |
| return f"Ошибка PDF: {str(e)}" | |
| def read_excel(path: str, sheet: str = None) -> str: | |
| """ | |
| Читает Excel. | |
| Args: | |
| path (str): Путь | |
| sheet (str, optional): Лист | |
| Returns: | |
| str: Таблица или ошибка | |
| """ | |
| try: | |
| df = pd.read_excel(path, sheet_name=sheet) | |
| return df.to_string(max_rows=20, max_cols=10) | |
| except Exception as e: | |
| return f"Ошибка Excel: {str(e)}" | |
| tools.extend([download_file, read_pdf, read_excel]) | |
| self.agent = CodeAgent( | |
| tools=tools, | |
| model=self.model, | |
| add_base_tools=True, | |
| max_steps=18, | |
| ) | |
| print("Агент готов!") | |
| def __call__(self, question: str) -> str: | |
| print(f"Вопрос: {question[:120]}...") | |
| # Обрезка длинных вопросов | |
| if len(question) > 2500: | |
| question = question[:2500] + "\n[Обрезано из-за длины.]" | |
| # Хак для файлов/видео/аудио/attached | |
| q = question.lower() | |
| if any(k in q for k in [".mp3", "audio", "recording", "voice", "youtube.com", "video", "attached", "file", "excel", "pdf", "image", "jpg", "png"]): | |
| question += "\nЕсли есть URL или attached файл — скачай, прочитай и отвечай только по фактам из него. Не придумывай числа, имена или данные." | |
| # Хак для шахмат | |
| if "chess" in q or "image" in q or ".jpg" in q or ".png" in q: | |
| question += "\nЕсли есть URL изображения — скачай и опиши позицию или ищи похожую. Не придумывай ход." | |
| try: | |
| result = self.agent.run(question) | |
| answer = str(result).strip() | |
| # Жёсткая очистка + защита от длинных ответов | |
| prefixes = [ | |
| "Final Answer", "Final answer", "Answer:", "The answer is", | |
| "So the final answer is", "```", "boxed{", "}", "[/INST]", "</s>", | |
| "Thought:", "Observation:", "Action:" | |
| ] | |
| for p in prefixes: | |
| if p.lower() in answer.lower(): | |
| answer = answer.split(p, 1)[-1].strip(": []{}\n`") | |
| break | |
| if answer.startswith("[") and answer.endswith("]"): | |
| answer = answer[1:-1].strip() | |
| answer = answer.strip() | |
| # Если ответ слишком длинный или подозрительный — обрезаем | |
| if len(answer) > 300 or "придум" in answer.lower() or answer.count(",") > 20: | |
| answer = answer[:150] + "..." if len(answer) > 150 else answer | |
| print(f"Ответ: {answer[:150]}...") | |
| return answer or "Нет ответа" | |
| except Exception as e: | |
| err = f"Ошибка: {str(e)[:200]}" | |
| print(err) | |
| return err | |
| # --- run_and_submit_all (без изменений) --- | |
| def run_and_submit_all(profile: gr.OAuthProfile | None): | |
| space_id = os.getenv("SPACE_ID") | |
| if profile: | |
| username = profile.username | |
| print(f"Вход: {username}") | |
| else: | |
| return "Войдите в HF", None | |
| api_url = DEFAULT_API_URL | |
| questions_url = f"{api_url}/questions" | |
| submit_url = f"{api_url}/submit" | |
| try: | |
| agent = BasicAgent() | |
| except Exception as e: | |
| return f"Ошибка агента: {e}", None | |
| agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" | |
| try: | |
| resp = requests.get(questions_url, timeout=15) | |
| resp.raise_for_status() | |
| questions = resp.json() | |
| if not questions: | |
| return "Вопросов нет", None | |
| print(f"Вопросов: {len(questions)}") | |
| except Exception as e: | |
| return f"Ошибка вопросов: {e}", None | |
| results = [] | |
| payload = [] | |
| for item in questions: | |
| tid = item.get("task_id") | |
| q = item.get("question") | |
| if not tid or not q: | |
| continue | |
| try: | |
| ans = agent(q) | |
| payload.append({"task_id": tid, "submitted_answer": ans}) | |
| results.append({"Task ID": tid, "Question": q, "Answer": ans}) | |
| except Exception as e: | |
| results.append({"Task ID": tid, "Question": q, "Answer": f"ERROR: {e}"}) | |
| if not payload: | |
| return "Нет ответов", pd.DataFrame(results) | |
| data = {"username": username.strip(), "agent_code": agent_code, "answers": payload} | |
| try: | |
| resp = requests.post(submit_url, json=data, timeout=60) | |
| resp.raise_for_status() | |
| res = resp.json() | |
| status = ( | |
| f"Успех!\n" | |
| f"Пользователь: {res.get('username')}\n" | |
| f"Балл: {res.get('score', 'N/A')}% " | |
| f"({res.get('correct_count', '?')}/{res.get('total_attempted', '?')})\n" | |
| f"{res.get('message', '')}" | |
| ) | |
| return status, pd.DataFrame(results) | |
| except Exception as e: | |
| return f"Ошибка отправки: {e}", pd.DataFrame(results) | |
| # --- Gradio --- | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Агент для финального задания") | |
| gr.Markdown(""" | |
| 1. Клонируй и дорабатывай. | |
| 2. Войди через кнопку. | |
| 3. Нажми кнопку — увидишь score. | |
| """) | |
| gr.LoginButton() | |
| btn = gr.Button("Запустить оценку и отправить") | |
| status = gr.Textbox(label="Результат", lines=6) | |
| table = gr.DataFrame(label="Ответы", wrap=True) | |
| btn.click(run_and_submit_all, outputs=[status, table]) | |
| if __name__ == "__main__": | |
| print("Запуск...") | |
| demo.launch(debug=True, share=False) |