Nefertury's picture
Update app.py
6dc5a90 verified
import os
import requests
import json
import gradio as gr
import pandas as pd
import matplotlib.pyplot as plt
import pypdf
import warnings
from langchain_google_genai import ChatGoogleGenerativeAI
warnings.filterwarnings("ignore", category=UserWarning)
# --- 1. Настройка констант, API и LLM ---
print("--- Настройка API и LLM ---")
# Определяем константу здесь, чтобы она была доступна всегда
API_BASE_URL = "http://194.113.209.48:8000"
llm = None # Инициализируем llm как None на случай ошибки
try:
# Ключ считывается из "секретов", которые вы задали в настройках Space
GEMINI_API_KEY = os.environ.get('GEMINI_API_KEY')
if GEMINI_API_KEY:
llm = ChatGoogleGenerativeAI(model="gemini-1.5-pro-latest", google_api_key=GEMINI_API_KEY, temperature=0.1)
print("✅ LLM настроена.")
else:
print("❌ ОШИБКА: Секрет 'GEMINI_API_KEY' не найден. Убедитесь, что он добавлен в настройках Space.")
except Exception as e:
print(f"❌ Произошла ошибка при настройке LLM: {e}")
# --- 3. Загрузка данных и контекста ---
# 3.1. Загружаем заранее проанализированный портфель из CSV
print("--- Загрузка данных портфеля ---")
try:
tagged_portfolio_df = pd.read_csv('sber_portfolio_analyzed5.csv')
tagged_portfolio_df['region_name'] = tagged_portfolio_df['region_name'].str.strip()
print(f"✅ Успешно загружен проанализированный портфель из {len(tagged_portfolio_df)} проектов.")
except FileNotFoundError:
print("❌ Ошибка: Файл 'sber_portfolio_analyzed5.csv' не найден. Возможно, нужно сначала запустить ячейку для генерации этого файла.")
tagged_portfolio_df = pd.DataFrame()
# 3.2. Извлекаем текст из PDF-отчета
print("--- Извлечение текста из PDF-отчета ---")
full_report_text = ""
try:
# Убедитесь, что имя файла верное и он загружен
with open('f356d44202ac9361579f509ebf965950.pdf', 'rb') as f:
reader = pypdf.PdfReader(f)
for page in reader.pages:
full_report_text += page.extract_text() + "\n"
print("✅ Текст из PDF-файла успешно извлечен.")
except Exception as e:
print(f"❌ Ошибка при чтении PDF-файла: {e}")
# 3.3. Загружаем справочники
print("--- Загрузка справочников ---")
try:
regions_response = requests.get(f"{API_BASE_URL}/api/regions")
regions_data = regions_response.json()
regions_list = sorted([item['name'] for item in regions_data])
print("✅ Справочник регионов загружен.")
except Exception as e:
print(f"❌ Не удалось загрузить справочник регионов: {e}")
regions_list = sorted(list(tagged_portfolio_df['region_name'].unique()))
# 3.4. Определяем константы (структура Наццелей) - ПОЛНАЯ ВЕРСИЯ
NATIONAL_GOALS_STRUCTURE = {
"Сохранение населения, укрепление здоровья и повышение благополучия людей, поддержка семьи": [
"2.а) повышение суммарного коэффициента рождаемости до 1,6 к 2030 году и до 1,8 к 2036 году",
"2.б) увеличение ожидаемой продолжительности жизни до 78 лет к 2030 году и до 81 года к 2036 году",
"2.в) обеспечение не ниже среднероссийских темпов повышения к 2030 году суммарного коэффициента рождаемости в отстающих субъектах РФ",
"2.г) снижение к 2036 году дифференциации показателей ожидаемой продолжительности жизни не менее чем на 25%",
"2.д) снижение к 2030 году суммарной продолжительности временной нетрудоспособности граждан",
"2.е) повышение к 2030 году уровня удовлетворенности граждан условиями для занятий физкультурой и спортом",
"2.ж) увеличение к 2030 году численности граждан, получающих услуги долговременного ухода, до 500 тыс. человек",
"2.з) повышение к 2030 году уровня удовлетворенности участников СВО условиями для медицинской реабилитации и трудоустройства",
"2.и) создание и запуск к 2030 году цифровой платформы по управлению здоровьем человека",
"2.к) снижение уровня бедности ниже 7% к 2030 году и ниже 5% к 2036 году, в т.ч. среди многодетных семей",
"2.л) снижение коэффициента Джини до 0,37 к 2030 году и до 0,33 к 2036 году",
"2.м) рост МРОТ к 2030 году более чем в два раза, не менее 35 тыс. рублей в месяц",
"2.н) утверждение в 2026 году новых систем оплаты труда работников бюджетной сферы"
],
"Реализация потенциала каждого человека, развитие его талантов, воспитание патриотичной и социально ответственной личности": [
"3.а) создание к 2030 году условий для воспитания гармонично развитой личности на основе традиционных ценностей",
"3.б) увеличение к 2030 году численности иностранных студентов до 500 тыс. человек",
"3.в) увеличение к 2030 году доли молодых людей, участвующих в проектах развития и воспитания, до 75%",
"3.г) увеличение к 2030 году доли молодых людей, верящих в самореализацию в России, до 85%",
"3.д) увеличение к 2030 году доли молодых людей в добровольческой деятельности до 45%",
"3.е) обеспечение к 2030 году функционирования эффективной системы выявления талантов у 100% обучающихся",
"3.ж) обеспечение продвижения традиционных ценностей в не менее чем 70% проектов в сфере культуры к 2030 году",
"3.з) повышение к 2030 году удовлетворенности граждан работой организаций культуры",
"3.и) формирование к 2030 году современной системы профразвития педагогических работников"
],
"Комфортная и безопасная среда для жизни": [
"4.а) улучшение качества среды для жизни в опорных населенных пунктах на 30% к 2030 году",
"4.б) обеспечение граждан жильем общей площадью не менее 33 кв. метров на человека к 2030 году",
"4.в) обновление к 2030 году жилищного фонда не менее чем на 20% по сравнению с 2019 годом",
"4.г) устойчивое сокращение непригодного для проживания жилищного фонда",
"4.д) повышение доступности жилья на первичном рынке",
"4.е) благоустройство не менее чем 30 тыс. общественных территорий к 2030 году",
"4.ж) реализация программы модернизации коммунальной инфраструктуры для 20 млн. человек к 2030 году",
"4.з) строительство и реконструкция не менее чем 2 тыс. объектов питьевого водоснабжения к 2030 году",
"4.и) рост энергоэффективности в ЖКХ и строительстве",
"4.к) обновление парка общественного транспорта до 85% от норматива к 2030 году",
"4.л) приведение в нормативное состояние 85% дорог агломераций и 60% региональных дорог к 2030 году",
"4.м) снижение смертности в ДТП в 1.5 раза к 2030 году",
"4.н) увеличение авиационной подвижности населения на 50% к 2030 году",
"4.о) капитальный ремонт всех нуждающихся зданий школ и детсадов до конца 2030 года",
"4.п) подключение к сетевому газу не менее 1,6 млн домовладений к 2030 году",
"4.р) оснащение 900 центров кинопоказа в малых населенных пунктах к 2030 году"
],
"Экологическое благополучие": [
"5.а) формирование экономики замкнутого цикла: 100% сортировка ТКО, захоронение не более 50%, вовлечение 25% отходов в оборот к 2030 году",
"5.б) снижение в два раза выбросов опасных загрязняющих веществ в городах с высоким уровнем загрязнения к 2036 году",
"5.в) ликвидация до конца 2030 года не менее 50 опасных объектов накопленного вреда",
"5.г) снижение к 2036 году в два раза объема неочищенных сточных вод",
"5.д) сохранение лесов, биоразнообразия и создание условий для экологического туризма"
],
"Устойчивая и динамичная экономика": [
"6.а) темп роста ВВП выше среднемирового, 4-е место в мире по ВВП по ППС к 2030 году",
"6.б) снижение доли импорта до 17% ВВП к 2030 году",
"6.в) увеличение объема инвестиций в основной капитал на 60% к 2030 году (относительно 2020)",
"6.г) рост доходов населения и пенсий не ниже уровня инфляции",
"6.д) реальный рост дохода на работника МСП в 1,2 раза выше роста ВВП",
"6.ж) вхождение в топ-25 стран мира по плотности роботизации к 2030 году",
"6.н) увеличение доли туристской отрасли в ВВП до 5% к 2030 году",
"6.о) прирост экспорта несырьевых неэнергетических товаров на две трети к 2030 году",
"6.п) увеличение производства продукции АПК на 25% к 2030 году (относительно 2021)",
"6.р) увеличение экспорта продукции АПК в 1.5 раза к 2030 году (относительно 2021)"
],
"Технологическое лидерство": [
"7.а) обеспечение технологической независимости (биоэкономика, БАС, ИИ, новые материалы и др.)",
"7.б) увеличение индекса производства в обрабатывающей промышленности на 40% к 2030 году (относительно 2022)",
"7.в) вхождение в топ-10 стран мира по объему научных исследований к 2030 году",
"7.г) увеличение внутренних затрат на исследования до 2% ВВП к 2030 году",
"7.д) увеличение доли отечественных высокотехнологичных товаров в 1.5 раза к 2030 году",
"7.е) увеличение выручки малых технологических компаний в 7 раз к 2030 году"
],
"Цифровая трансформация": [
"8.а) достижение 'цифровой зрелости' госуправления и ключевых отраслей экономики к 2030 году",
"8.б) формирование рынка данных",
"8.в) доступ к высокоскоростному интернету для 97% домохозяйств к 2030 году",
"8.г) рост инвестиций в отечественные ИТ-решения вдвое выше темпа роста ВВП",
"8.д) переход 80% организаций на российское ПО к 2030 году"
]
}
print("✅ Словарь НАЦЦЕЛЕЙ успешно загружен.")
# --- 4. Определение функций приложения ---
print("--- Определение функций приложения ---")
# (Здесь идут полные определения функций generate_regional_briefing, _find_relevant_goals, analyze_new_project)
# --- ФИНАЛЬНАЯ ВЕРСИЯ С ТАБЛИЧНЫМ ПРЕДСТАВЛЕНИЕМ АНАЛИЗА ---
def generate_regional_briefing(region_name, year):
print("---")
print(f"НОВЫЙ ЗАПРОС [Стратегический обзор]:")
print(f"Регион: {region_name}, Год: {year}")
print("---")
if not region_name or not year:
yield "Пожалуйста, выберите регион и год.", None, None
return
if not 'full_report_text' in globals() or not full_report_text:
yield "Ошибка: Текст отчета ESG-индекса не был загружен.", None, None
return
yield "<h3 style='margin-top: 20px;'>⏳ Готовлю финальный отчет, это может занять до 30 секунд...</h3>", None, None
try:
region_id = next((item['id'] for item in regions_data if item['name'] == region_name), None)
params = {"year": int(year)}
response = requests.get(f"{API_BASE_URL}/api/region-performance/{region_id}/all", params=params)
response.raise_for_status()
urfu_data_raw = response.json()
urfu_data_simplified = {indicator.get('name'): indicator.get('region_value') for indicator in urfu_data_raw.get('indicators', []) if indicator.get('name') and indicator.get('region_value') is not None}
except Exception as e:
yield f"Не удалось получить данные от API УрФУ. Ошибка: {e}", None, None
return
region_portfolio = tagged_portfolio_df[tagged_portfolio_df['region_name'] == region_name]
portfolio_summary = "В данном регионе у Сбера отсутствуют проекты из анализируемого портфеля."
fig = None
if not region_portfolio.empty:
total_investment = region_portfolio['amount_bln_rub'].sum()
project_count = len(region_portfolio)
SUMMARIZATION_THRESHOLD = 5
if 'mapped_goal' in region_portfolio.columns and not region_portfolio['mapped_goal'].isnull().all():
if project_count > SUMMARIZATION_THRESHOLD:
summary_parts = []
goal_groups = region_portfolio.groupby('mapped_goal')['amount_bln_rub'].sum().sort_values(ascending=False).reset_index()
for _, row in goal_groups.iterrows():
goal_name = row['mapped_goal']
goal_sum = row['amount_bln_rub']
project_examples = region_portfolio[region_portfolio['mapped_goal'] == goal_name]['project_goal'].head(2).tolist()
examples_text = ", ".join(project_examples)
# ИЗМЕНЕНИЕ: Формируем HTML-элемент списка <li> и используем <strong> для жирности
summary_parts.append(
f"<li><strong>{goal_name}</strong> ({goal_sum:.1f} млрд руб.), ключевые проекты: {examples_text}</li>"
)
# ИЗМЕНЕНИЕ: Собираем полный HTML-список <ul>...</ul>
summary_text = "<ul>" + "".join(summary_parts) + "</ul>"
portfolio_summary = (
f"Портфель Сбера в регионе составляет {total_investment:.1f} млрд руб. и включает проекты, распределенные по следующим национальным целям:{summary_text}"
)
else:
projects_list = [
f"'{row['project_goal']}' ({row['amount_bln_rub']:.1f} млрд руб.), направленный на достижение цели \"{row.get('mapped_goal', 'не определена')}\""
for _, row in region_portfolio.iterrows()
]
projects_text = " и ".join(projects_list)
portfolio_summary = (
f"Текущий портфель Сбера в регионе составляет {total_investment:.1f} млрд руб. "
f"и включает следующие ключевые проекты: {projects_text}."
)
else:
projects_list = [
f"'{row['project_goal']}' ({row['amount_bln_rub']:.1f} млрд руб.)"
for _, row in region_portfolio.iterrows()
]
projects_text = ", ".join(projects_list)
portfolio_summary = (
f"Текущий портфель Сбера в регионе составляет {total_investment:.1f} млрд руб. и включает следующие проекты: {projects_text}."
)
if 'mapped_goal' in region_portfolio.columns and not region_portfolio['mapped_goal'].isnull().all():
goal_distribution = region_portfolio.groupby('mapped_goal')['amount_bln_rub'].sum()
plot_data = goal_distribution.reset_index()
if plot_data is not None and not plot_data.empty:
fig, ax = plt.subplots(figsize=(10, 6)); bars = ax.bar(plot_data['mapped_goal'], plot_data['amount_bln_rub'], color='#4CAF50'); ax.bar_label(bars, fmt='%.0f', padding=3)
plt.xticks(rotation=45, ha="right", fontsize=9); plt.title(f"Распределение портфеля Сбера по Наццелям\nв регионе: {region_name}", fontsize=12)
plt.ylabel("Сумма, млрд руб.", fontsize=10); plt.grid(axis='y', linestyle='--', alpha=0.7); plt.tight_layout()
prompt = f"""
Твоя роль: Ведущий аналитик-стратег ESG-дирекции Сбера.
Твоя задача: Подготовить исчерпывающий и СТРУКТУРИРОВАННЫЙ аналитический отчет по региону, синтезируя информацию из предоставленных источников.
### ИСТОЧНИК 1: Статистические показатели по региону (от УрФУ)
Регион анализа: {region_name}
{json.dumps(urfu_data_simplified, ensure_ascii=False, indent=2)}
### ИСТОЧНИК 2: Полный текст отчета "ESG-индекс городов и регионов" (Сбер, ВЭБ.РФ)
```text
{full_report_text}
```
### ИСТОЧНИК 3: Данные по проектам в регионе
{portfolio_summary}
### ЗАДАЧА И СТРОГИЕ ПРАВИЛА:
1. Подготовь отчет в формате Markdown.
2. Сфокусируй детальный анализ ТОЛЬКО на двух Национальных целях: "Сохранение населения, укрепление здоровья и повышение благополучия людей, поддержка семьи" и "Комфортная и безопасная среда для жизни".
3. Относись ко всем данным как к реальным, не добавляй дисклеймеров.
4. Для раздела "Детальный анализ" **сгенерируй Markdown-таблицы**, как показано в шаблоне.
---
### Аналитический отчет: {region_name}, {year} год
**1. Обзор проектов в регионе**
* На основе ИСТОЧНИКА 3, опиши состав и объем проектов. Начинай с новой строки каждую новую Наццель и по ней проекты.
* **Задача для следующей строки:** На основе ИСТОЧНИКА 2, найди конкретные факты о регионе `{region_name}` (место в рейтинге, баллы, и т.д.).
* **Требование к форматированию:** Начни строку с "ESG-индекс городов и регионов Сбер - ВЭБ.РФ:". Сразу после этого, обычным черным текстом, продолжи своим аналитическим выводом.
**2. Детальный анализ по приоритетным Национальным целям**
* Для каждой из двух приоритетных Наццелей, создай подзаголовок и Markdown-таблицу по следующему образцу. Заполни 2-3 строки для каждой таблицы наиболее показательными данными.
**Национальная цель: "Сохранение населения, укрепление здоровья и повышение благополучия людей, поддержка семьи"**
| Показатель (данные УрФУ) | Значение в регионе | Влияние проектов Сбера |
| :--- | :--- | :--- |
| *[название показателя из ИСТОЧНИКА 1]* | *[значение из ИСТОЧНИКА 1]* | *[твой анализ ИСТОЧНИКА 3]* |
| *[название второго показателя]* | *[его значение]* | *[твой анализ]* |
**Национальная цель: "Комфортная и безопасная среда для жизни"**
| Показатель (данные УрФУ) | Значение в регионе | Влияние проектов Сбера |
| :--- | :--- | :--- |
| *[название показателя из ИСТОЧНИКА 1]* | *[значение из ИСТОЧНИКА 1]* | *[твой анализ ИСТОЧНИКА 3]* |
| *[название второго показателя]* | *[его значение]* | *[твой анализ]* |
**3. Стратегические рекомендации**
* На основе табличного анализа, сформулируй 2-3 ключевых вывода и предложи конкретные типы проектов для инвестиций.
"""
final_report = llm.invoke(prompt).content
yield final_report, fig, region_portfolio.drop(columns=['project_goal'], errors='ignore')
def _find_relevant_goals(project_description: str) -> list:
top_level_goals = list(NATIONAL_GOALS_STRUCTURE.keys())
prompt = f"""
Определи 1-3 наиболее релевантных национальных цели из списка для проекта. Верни ТОЛЬКО JSON-массив строк.
СПИСОК НАЦЦЕЛЕЙ: {json.dumps(top_level_goals, ensure_ascii=False)}
ОПИСАНИЕ ПРОЕКТА: "{project_description}"
Твой JSON-ответ:
"""
try:
response = llm.invoke(prompt)
return json.loads(response.content.strip().replace("```json", "").replace("```", ""))
except Exception:
return top_level_goals
def analyze_new_project(region_name, project_description):
print("---")
print(f"НОВЫЙ ЗАПРОС [Экспресс-оценка]:")
print(f"Регион: {region_name}")
print(f"Описание проекта: {project_description}")
print("---")
if not all([region_name, project_description]):
yield "Пожалуйста, выберите регион и опишите проект."
return
yield "<h3 style='margin-top: 20px;'>⏳ Этап 1/2: Определяю релевантные Наццели...</h3>"
relevant_goals_names = _find_relevant_goals(project_description)
relevant_goals_structure = {goal: NATIONAL_GOALS_STRUCTURE.get(goal, []) for goal in relevant_goals_names}
yield "<h3 style='margin-top: 20px;'>⏳ Этап 2/2: Готовлю детальный анализ, это может занять до 30 секунд...</h3>"
try:
region_id = next((item['id'] for item in regions_data if item['name'] == region_name), None)
params = {"year": 2024} # Можно использовать актуальный год
response = requests.get(f"{API_BASE_URL}/api/region-performance/{region_id}/all", params=params)
response.raise_for_status()
urfu_data_raw = response.json()
urfu_data_simplified = {indicator.get('name'): indicator.get('region_value') for indicator in urfu_data_raw.get('indicators', []) if indicator.get('name') and indicator.get('region_value') is not None}
except Exception as e:
yield f"Не удалось получить данные от API УрФУ. Ошибка: {e}"
return
goals_structure_str = json.dumps(relevant_goals_structure, ensure_ascii=False, indent=2)
# --- НАЧАЛО НОВОГО, РАСШИРЕННОГО ПРОМПТА ---
prompt = f"""
Твоя роль: Ведущий аналитик ESG-дирекции Сбера, обладающий глубокими знаниями в области национальных целей развития РФ.
Твоя задача: Подготовить ДЕТАЛЬНЫЙ и СТРУКТУРИРОВАННЫЙ аналитический отчет по новому инвестиционному проекту. Отчет должен быть выполнен в формате Markdown и содержать глубокий анализ, а не поверхностное описание.
### ВХОДНЫЕ ДАННЫЕ:
1. **Регион реализации:** {region_name}
2. **Описание проекта:** "{project_description}"
3. **Статистический контекст по региону (данные УрФУ):** {json.dumps(urfu_data_simplified, ensure_ascii=False, indent=2)}
4. **Структура релевантных Национальных целей и их задач:** {goals_structure_str}
### СТРОГИЕ ТРЕБОВАНИЯ К СТРУКТУРЕ И СОДЕРЖАНИЮ ОТЧЕТА:
**1. Краткое резюме проекта**
* Опиши суть проекта, его масштаб и основные цели.
**2. Анализ соответствия Национальным целям**
* Для КАЖДОЙ релевантной наццели из предоставленного списка:
* **Обоснуй, почему проект соответствует данной цели.** Четко свяжи деятельность по проекту с конкретными задачами (подпунктами) этой наццели.
* **Используй данные из статистического контекста (ВХОДНЫЕ ДАННЫЕ №3)**, чтобы показать АКТУАЛЬНОСТЬ проекта для региона. Например, если проект влияет на здравоохранение, приведи текущие показатели по ожидаемой продолжительности жизни или заболеваемости в регионе, чтобы подчеркнуть важность инвестиций.
**3. Оценка по ESG-критериям**
* **E (Environmental / Экология):** Оцени потенциальное положительное и отрицательное воздействие проекта на окружающую среду. Какие экологические риски и возможности он несет? (например, снижение выбросов, образование отходов, использование "зеленых" технологий).
* **S (Social / Социальная сфера):** Проанализируй социальный эффект. Создание рабочих мест (сколько, каких?), влияние на качество жизни, доступность услуг, поддержка местных сообществ.
* **G (Governance / Управление):** Опиши потенциальные управленческие аспекты. Необходимость взаимодействия с региональными властями, прозрачность реализации, работа с заинтересованными сторонами.
**4. Предварительные риски и рекомендации**
* На основе всего анализа, выдели 2-3 ключевых риска (технологических, социальных, регуляторных), на которые стоит обратить внимание.
* Дай 1-2 стратегические рекомендации по усилению положительного эффекта от проекта.
**5. Итоговое заключение**
* Сделай общий вывод о стратегической привлекательности проекта для банка с точки зрения вклада в устойчивое развитие и национальные приоритеты.
---
**Начинай твой отчет.**
"""
# --- КОНЕЦ НОВОГО ПРОМПТА ---
analysis_report = llm.invoke(prompt).content
yield analysis_report
# --- 5. Запуск веб-интерфейса Gradio ---
print("🚀 Запускаем Gradio-приложение...")
with gr.Blocks(theme=gr.themes.Soft(primary_hue="green", secondary_hue="lime"), css=".gradio-container {background-color: #f5f5f5} th { white-space: nowrap; }") as demo:
gr.Markdown(
"""<div style="text-align: center;"><img src="https://www.sberbank.com/common/img/uploaded/logo/logo_sber_main_2020_ru.svg" width="200">
<h1>Интерактивный дашборд "Горизонт PRO"</h1><p>Анализ вклада в достижение Национальных целей РФ 2030/2036</p></div>"""
)
with gr.Tabs():
with gr.TabItem("📈 Стратегический обзор региона"):
with gr.Row():
region_input_1 = gr.Dropdown(regions_list, label="Выберите регион")
year_input_1 = gr.Dropdown([2025, 2024, 2023, 2022], label="Выберите год", value=2024)
submit_button_1 = gr.Button("Сформировать стратегическую справку", variant="primary")
gr.Markdown("---")
output_report_1 = gr.Markdown(label="Аналитическая справка")
# Элементы создаются, но не отображаются в интерфейсе
# Это необходимо, чтобы функция click могла в них вернуть значения без ошибки.
output_plot_1 = gr.Plot(visible=False)
output_table_1 = gr.DataFrame(label="Проекты в портфеле", visible=False)
with gr.TabItem("🔎 Экспресс-оценка нового проекта"):
# --- ИЗМЕНЕНИЕ №1: Выносим текст примера в отдельную переменную ---
example_text = "Мусоросортировочный завод на 150 000 тонн стоимостью 3 млрд руб"
gr.Markdown("Введите описание нового инвестиционного проекта для анализа его соответствия Национальным целям и потребностям региона.")
region_input_2 = gr.Dropdown(regions_list, label="Выберите регион реализации проекта")
project_input_2 = gr.Textbox(lines=4, label="Опишите проект", placeholder=f"Пример: {example_text}")
# --- ИЗМЕНЕНИЕ №2: Добавляем две кнопки в одну строку ---
with gr.Row():
# Кнопка для вставки примера
example_button = gr.Button("✍️ Вставить пример", variant="secondary")
# Основная кнопка для анализа
submit_button_2 = gr.Button("Провести экспресс-оценку", variant="primary", scale=2) # scale=2 делает ее в 2 раза шире
gr.Markdown("---")
output_report_2 = gr.Markdown(label="Результат оценки")
# --- Привязка функций к кнопкам (остается без изменений) ---
submit_button_1.click(
fn=generate_regional_briefing,
inputs=[region_input_1, year_input_1],
outputs=[output_report_1, output_plot_1, output_table_1]
)
# --- ИЗМЕНЕНИЕ №3: Добавляем обработчик для новой кнопки ---
example_button.click(
fn=lambda: example_text, # Простая функция, которая просто возвращает текст примера
inputs=None, # У нее нет входных данных
outputs=project_input_2 # Результат (текст) отправляется в наше поле для ввода
)
submit_button_2.click(
fn=analyze_new_project,
inputs=[region_input_2, project_input_2],
outputs=[output_report_2]
)
demo.launch(share=True, debug=True)