| | 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): |
| | 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(), |
| | ] |
| |
|
| | @tool |
| | 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)}" |
| |
|
| | @tool |
| | 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)}" |
| |
|
| | @tool |
| | 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[Обрезано из-за длины.]" |
| |
|
| | q = question.lower() |
| | if any(k in q for k in [".mp3", "audio", "recording", "voice", "youtube.com", "video", "attached", "file", "excel", "pdf", "image"]): |
| | 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(): |
| | 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 |
| |
|
| | |
| | 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) |
| |
|
| | |
| | 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) |