Spaces:
Sleeping
Sleeping
| import os | |
| from dotenv import load_dotenv | |
| from openai import OpenAI | |
| import gradio as gr | |
| import time | |
| from datetime import datetime | |
| import calendar | |
| from docx import Document | |
| import tempfile | |
| import random # для случайного выбора цитаты | |
| from app.quotes import quotes | |
| # Загрузка переменных окружения | |
| load_dotenv() | |
| api_key = os.getenv("API_KEY_openai") | |
| client = OpenAI(api_key=api_key) | |
| #API_KEY = os.getenv("API_KEY") | |
| #BASE_URL = os.getenv("BASE_URL") | |
| #client = OpenAI(api_key=API_KEY, base_url=BASE_URL) | |
| # Функция генерации плана занятия | |
| def generate_lesson_plan_interface( | |
| нарушение, возраст_ребенка, особые_условия, | |
| формат_занятия, количество_детей, цель_занятия, тема, длительность_занятия, | |
| инвентарь, наличие_ДЗ, разрешен_web_search, web_sources, текущий_месяц=None | |
| ): | |
| # Логика работы с источниками - если есть, передаем | |
| источники = [web_sources] if разрешен_web_search else [] | |
| # Логика определения месяца | |
| if not текущий_месяц: | |
| текущий_месяц = datetime.now().month | |
| # все параметры | |
| params = { | |
| "нарушение": нарушение, | |
| "возраст_ребенка": возраст_ребенка, | |
| "цель_занятия": цель_занятия, | |
| "формат_занятия": формат_занятия, | |
| "количество_детей": количество_детей, | |
| "тема": тема, | |
| "особые_условия": особые_условия, | |
| "инвентарь": инвентарь, | |
| "длительность_занятия": длительность_занятия, | |
| "наличие_ДЗ": наличие_ДЗ, | |
| "разрешен_web_search": разрешен_web_search, | |
| "источники": источники, | |
| "текущий_месяц": текущий_месяц | |
| } | |
| prompt = f""" | |
| Ты — эксперт в области логопедии, специализирующийся на разработке занятий для детей с речевыми нарушениями. На вход ты получаешь следующие параметры: | |
| - **Основное нарушение:** {params['нарушение']} | |
| - **Возраст ребенка:** {params['возраст_ребенка']} | |
| - **Цель занятия:** {params['цель_занятия']} | |
| - **Тема:** {params['тема'] or "не указано. Придумай сам, опираясь на данные о ребенке и цели занятия"} | |
| - **Формат занятия:** {params['формат_занятия']} ({params['количество_детей']} детей) | |
| - **Инвентарь:** {params['инвентарь'] or "не указано"} | |
| - **Особые условия:** {params['особые_условия'] or "не указано"} | |
| - **Длительность занятия:** {params['длительность_занятия']} минут | |
| - **Наличие домашнего задания:** {params['наличие_ДЗ']} | |
| - **Разрешен web search:** {params['разрешен_web_search']} | |
| - **Текущий месяц:** {params['текущий_месяц']} | |
| На основе этих параметров составь конспект занятия, который должен включать: | |
| 1. **Тема занятия, цель и задачи** | |
| 2. **Необходимые материалы** | |
| 3. **Ход занятия**. Разбей занятие на логически последовательные этапы. | |
| В каждый этап встрой конкретные упражнения с достаточным количеством примеров. | |
| ВАЖНО: если разрешен web search: True, то выстрой занятие на базе упражнений из релевантных источников. | |
| Обязательно приведи ссылки на источники источники в текст ответа). | |
| 5. **Домашнее задание** (только если параметр "наличие домашнего задания: True") | |
| 6. **Рекомендации по особым условиям** (если указан параметр "Особые условия") | |
| """ | |
| tools = [] | |
| tool_choice = None | |
| if params['разрешен_web_search']: | |
| tools.append({ | |
| "type": "web_search_preview", | |
| "search_context_size": "medium", | |
| "user_location": { | |
| "type": "approximate", | |
| "country": "RU" | |
| } | |
| }) | |
| tool_choice = {"type": "web_search_preview"} | |
| response = client.responses.create( | |
| model="gpt-4o-mini", | |
| input=prompt, | |
| tools=tools if tools else None, | |
| tool_choice=tool_choice, | |
| max_output_tokens=2000 | |
| ) | |
| return response.output_text | |
| # response = client.chat.completions.create( | |
| # model="gpt-4o-mini", | |
| # messages=[ | |
| # {"role": "system", "content": "Ты — эксперт в области логопедии."}, | |
| # {"role": "user", "content": prompt} | |
| # ], | |
| # max_tokens=2000 | |
| # ) | |
| # return response.choices[0].message.content | |
| # Интерфейс Gradio | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## 🧠 Генератор логопедических занятий") | |
| with gr.Row(): | |
| # Первый блок настройки (Ребенок) | |
| with gr.Column(scale=1): | |
| quote_box = gr.Markdown(random.choice(quotes), elem_classes=["quote-block"]) | |
| gr.Markdown("### 🧒 Ребёнок", elem_classes=["block-title"]) | |
| нарушение = gr.Textbox(label="Основное нарушение*", | |
| placeholder="Пример: Дислалия (свистящие), ОНР II уровня") | |
| возраст = gr.Textbox(label="Возраст ребенка*", placeholder="Пример: 5 лет, 6-7 лет") | |
| особые_условия = gr.Textbox(label="Особые условия", placeholder="Пример: гиперактивность, РАС") | |
| gr.Markdown("### 📄 Занятие", elem_classes=["block-title"]) | |
| формат = gr.Radio(["Индивидуальное", "Групповое"], label="Формат занятия", value="Индивидуальное") | |
| количество_детей = gr.Slider( | |
| label="Количество детей в группе", minimum=2, maximum=10, value=2, step=1, visible=False | |
| ) | |
| def toggle_group_slider(selected_format): | |
| return gr.update(visible=(selected_format == "Групповое")) | |
| формат.change(fn=toggle_group_slider, inputs=формат, outputs=количество_детей) | |
| цель = gr.Textbox(label="Цель занятия*", placeholder="Пример: Автоматизация звука [Р] в слогах") | |
| тема = gr.Textbox(label="Тема", placeholder="Пример: Животные, Транспорт") | |
| длительность = gr.Slider(label="Длительность занятия (мин)", minimum=15, maximum=60, value=30, step=5) | |
| инвентарь = gr.Textbox(label="Инвентарь (введите через запятую)", | |
| placeholder="Пример: Зеркало, Карточки, Игрушки") | |
| дз = gr.Checkbox(label="Домашнее задание") | |
| # Выделенный блок для профессиональных ресурсов | |
| gr.Markdown("---") # Горизонтальная разделительная линия | |
| #gr.Markdown("### 🔍 Дополнительные ресурсы", elem_classes=["block-title", "pro-title"]) | |
| with gr.Row(variant="panel"): # Вариант "panel" добавляет фоновый оттенок | |
| web = gr.Checkbox( | |
| label="📚 Поиск упражнений в логопедических базах", | |
| info="Упражнения подбираются из проверенных источников со ссылками", | |
| interactive=True | |
| ) | |
| web_sources = gr.Textbox( | |
| label="Уточнить источники (необязательно):", | |
| placeholder="Например: Logopedy.ru, Maam.ru, ...", | |
| visible=False, | |
| ) | |
| # Логика отображения поля источников | |
| web.change( | |
| fn=lambda x: gr.update(visible=x), | |
| inputs=web, | |
| outputs=web_sources | |
| ) | |
| # Кнопка создания конспекта | |
| btn = gr.Button("Создать конспект", variant="primary") | |
| # Правая колонка — результат (output) | |
| with gr.Column(scale=2): # Правая колонка — результат | |
| download_btn = gr.DownloadButton( | |
| label="⬇️ Скачать .docx", | |
| visible=False | |
| ) | |
| output = gr.Markdown("") # Поле для вывода конспекта | |
| # Ввод параметров | |
| all_inputs = [ | |
| нарушение, возраст, особые_условия, | |
| формат, количество_детей, цель, тема, длительность, | |
| инвентарь, дз, web, web_sources | |
| ] | |
| # Функция для генерации docx файла | |
| def generate_docx(text: str): | |
| doc = Document() | |
| for line in text.split("\n"): | |
| doc.add_paragraph(line) | |
| tmp_dir = tempfile.gettempdir() | |
| file_path = os.path.join(tmp_dir, "Конспект_занятия.docx") | |
| doc.save(file_path) | |
| return file_path | |
| def on_submit_with_spinner(*args): | |
| # Проверка обязательных полей | |
| if not args[0] or not args[1] or not args[5]: | |
| yield ( | |
| *[gr.update(interactive=True) for _ in all_inputs], | |
| gr.update(value="❗Заполните обязательные поля: нарушение, возраст, цель занятия"), | |
| gr.update(visible=False) | |
| ) | |
| return | |
| # Показываем спиннер | |
| yield ( | |
| *[gr.update(interactive=False) for _ in all_inputs], | |
| gr.update(value="⏳ Конспект создается..."), | |
| gr.update(visible=False) | |
| ) | |
| # Генерация конспекта | |
| result = generate_lesson_plan_interface(*args) | |
| file_path = generate_docx(result) | |
| random_quote = random.choice(quotes) | |
| yield ( | |
| *[gr.update(interactive=True) for _ in all_inputs], | |
| gr.update(value=result), | |
| gr.update(visible=True, value=file_path), # Передаём путь к файлу прямо в DownloadButton | |
| ) | |
| # Привязываем обработчики | |
| btn.click( | |
| fn=on_submit_with_spinner, | |
| inputs=all_inputs, | |
| outputs=[* all_inputs, output, download_btn] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(share=True) |