For_Hadi / app.py
fruitpicker01's picture
Upload app.py
e7e56c1 verified
Raw
History Blame Contribute Delete
34.8 kB
# -*- coding: utf-8 -*-
from __future__ import annotations
import json
import os
import re
import uuid
import inspect
from dataclasses import dataclass
from pathlib import Path
from typing import Any
import requests
ROOT = Path(__file__).resolve().parent
DATA_DIR = ROOT / "data"
AUTH_URL = os.getenv(
"GIGACHAT_AUTH_URL",
"https://ngw.devices.sberbank.ru:9443/api/v2/oauth",
)
CHAT_URL = os.getenv(
"GIGACHAT_CHAT_URL",
"https://gigachat.devices.sberbank.ru/api/v1/chat/completions",
)
DEFAULT_SCOPE = os.getenv("GIGACHAT_SCOPE", "GIGACHAT_API_CORP")
DEFAULT_MODEL = os.getenv("GIGACHAT_MODEL", "GigaChat-2-Max")
MODEL_CHOICES = ["GigaChat-2-Max", "GigaChat-2-Pro", "GigaChat-3-Ultra"]
BANNED_CLOSINGS = (
"рассказать подробнее",
"узнать больше",
"узнать подробности",
"нужна дополнительная информация",
"рассказать подробности",
"узнать об условиях",
"об условиях оформления",
)
BANNED_PROCESS_WORDS = (
"оператор:",
"специалист",
"пока вы ожидаете",
"решаем ваш вопрос",
"текущей проблемы",
)
DEFAULT_PRODUCT_NAME = "Бизнес-карта"
DEFAULT_BIZ_FEATURES = (
"Бизнес-карта — это именная банковская карта, выпускаемая к расчётному счёту "
"юридического лица или индивидуального предпринимателя в рублях, при этом количество "
"выпускаемых карт не ограничено. Карту можно выпустить к действующему счёту начиная со "
"дня активации пакета услуг или позже, но обязательно после его оплаты. К открытому счёту "
"бизнес-карту оформляют либо в отделении банка только на пластиковом носителе, либо через "
"СберБизнес как на пластике, так и в цифровом формате без пластика. Используется платёжная "
"система МИР, и после выпуска необходимо установить лимиты на совершение операций. "
"Продукт обеспечивает экономию времени на банковском обслуживании, эффективное управление "
"подотчётными расходами, анализ расходования средств и контроль оборота наличности, а также "
"доступ к средствам со счёта компании в рублях в удобное время в банкоматах Сбербанка и "
"сторонних банков. Кроме того, бизнес-карта гарантирует сохранность денежных средств благодаря "
"возможности дистанционной блокировки при утере, начисление бонусов СберБизнес Спасибо с "
"конвертацией в рубли на банковский счёт, а также предоставляет скидки на АЗС и контроль за "
"расходами на топливо без выпуска отдельной топливной карты через сервис "
"'Заправить авто для бизнеса'."
)
DEFAULT_BIZ_EXAMPLES = (
"Пример фразы: К расчётному счёту можно подключить бизнес-карту и оформить на себя или "
"сотрудника, настроив лимиты. Это позволит уйти от выдачи денег под отчёт. Рассказать о всех "
"возможностях или перейдем к оформлению?\n"
"Пример фразы: С помощью бизнес-карты можно оплачивать покупки и получать кешбэк, делать "
"переводы без платёжек, для переводов нужен только номер карты или телефон получателя. Вам "
"рассказать о всех возможностях бизнес карты или хотите сразу оформить?\n"
"Пример фразы: У нас есть предложение, которое может сделать снятие наличных для бизнеса более "
"выгодным. Это наша Бизнес-карта. Комиссия по ней от 2% до 4,5%, а через кассу до 10%. Снимать "
"и вносить наличные можно круглосуточно. Рассказать о всех возможностях или перейдем к оформлению?"
)
EARLY_DIALOGS = {
"Командировки и подотчёт": (
"Клиент: Здравствуйте. Подскажите, как удобнее выдавать сотрудникам деньги на командировки, "
"а то постоянно вожусь с авансовыми отчётами."
),
"Снятие наличных": (
"Клиент: Добрый день. Часто снимаю наличные для бизнеса через кассу, комиссия кусается. "
"Есть что-то выгоднее?"
),
"Контроль расходов": (
"Клиент: Здравствуйте. Хочу контролировать, на что сотрудники тратят деньги компании, "
"и видеть аналитику по расходам."
),
"АЗС и топливо": (
"Клиент: Добрый день. У нас разъездная работа, много заправляемся. Можно как-то отслеживать "
"траты на топливо без отдельной топливной карты?"
),
}
AFTER_DIALOGS = {
"Госзакупки": (
"Клиент: Мы участвуем в госзакупках по 44-ФЗ, для обеспечения заявки нужна банковская гарантия. "
"Поможете?"
),
"Свободные деньги": (
"Клиент: У меня на счету лежат свободные деньги, хочу получать процент, но чтобы доступ к ним "
"оставался каждый день."
),
"Зарплата": "Клиент: Платим зарплату сотрудникам, хочется удобнее и без комиссии за переводы.",
"Страхование": "Клиент: Хочу застраховать и бизнес, и сотрудников от основных рисков одним полисом.",
"Подотчёт": "Клиент: Постоянно выдаём сотрудникам деньги под отчёт на расходы, неудобно с отчётами.",
}
FALLBACK_AFTER_PRODUCTS = [
(
"1",
"Особенности предложения: Банковская гарантия — способ обеспечения исполнения обязательств. "
"Размер гарантии определяется заказчиком и составляет от 5 до 30 процентов от начальной "
"максимальной цены контракта. Доступны онлайн-гарантии для 44-ФЗ и 223-ФЗ.",
),
(
"2",
"Особенности предложения: Неснижаемый остаток позволяет получать процентный доход по ставкам, "
"близким к депозитам, и сохранять доступ к средствам в течение дня. Минимальная сумма размещения "
"500 000 рублей, минимальный срок от 7 календарных дней.",
),
(
"3",
"Особенности предложения: Защита бизнеса 360° объединяет страхование имущества, перерыва "
"деятельности, экологической ответственности, киберрисков, персонала и гражданской ответственности. "
"Оформление занимает 5 минут на сайте Сбера.",
),
(
"4",
"Особенности предложения: Бизнес-карта выпускается к расчётному счёту, помогает управлять "
"подотчётными расходами, анализировать расходы, контролировать наличность и настраивать лимиты.",
),
(
"5",
"Особенности предложения: Индивидуальное решение по страховой защите подбирается страховым "
"менеджером под потребности клиента, риски и страховые суммы.",
),
(
"6",
"Особенности предложения: Депозит позволяет получить процентный доход за счёт размещения "
"свободных средств. Минимальная сумма депозита 10 000 рублей, минимальный срок размещения один день.",
),
(
"7",
"Особенности предложения: Зарплатный проект включает онлайн-сервисы, QR-код для выпуска карт, "
"зачисления зарплаты и выплаты самозанятым без комиссии, скорость зачисления от 1 минуты.",
),
]
@dataclass(frozen=True)
class PromptPair:
old_system: str
old_human: str
new_system: str
new_human: str
def read_data_file(name: str) -> str:
path = DATA_DIR / name
if not path.exists():
return ""
return path.read_text(encoding="utf-8")
def decode_source(raw: str) -> str:
return raw.replace("\\n", "\n").replace("\\xa0", " ").replace("\\'", "'").strip()
def select_early_dialog(name: str) -> str:
return EARLY_DIALOGS.get(name, next(iter(EARLY_DIALOGS.values())))
def select_after_dialog(name: str) -> str:
return AFTER_DIALOGS.get(name, next(iter(AFTER_DIALOGS.values())))
def load_local_credentials() -> str:
for path in (ROOT / "GigaChat-2-Max_key.txt", ROOT.parent / "GigaChat-2-Max_key.txt"):
if path.exists():
value = path.read_text(encoding="utf-8").strip()
if value:
return value
return ""
def resolve_credentials(ui_credentials: str | None) -> str:
candidates = [
ui_credentials,
os.getenv("GIGACHAT_CREDENTIALS"),
os.getenv("GC_KEY_CORP"),
os.getenv("GIGACHAT_API_KEY"),
load_local_credentials(),
]
for candidate in candidates:
if candidate and candidate.strip():
return candidate.strip()
return ""
def auth_headers(credentials: str) -> dict[str, str]:
return {
"Authorization": f"Basic {credentials}",
"RqUID": str(uuid.uuid4()),
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
}
def response_excerpt(response: requests.Response) -> str:
text = response.text.replace("\n", " ").strip()
return text[:600]
def disable_ssl_warnings_if_needed(verify_ssl: bool) -> None:
if verify_ssl:
return
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def request_token(credentials: str, scope: str, verify_ssl: bool, timeout: int = 30) -> str:
if not credentials:
raise ValueError(
"Не найден ключ GigaChat. В Space добавьте Secret GIGACHAT_CREDENTIALS "
"или GC_KEY_CORP, либо вставьте ключ в поле интерфейса."
)
disable_ssl_warnings_if_needed(verify_ssl)
response = requests.post(
AUTH_URL,
headers=auth_headers(credentials),
data={"scope": scope or DEFAULT_SCOPE},
timeout=timeout,
verify=verify_ssl,
)
if response.status_code >= 400:
raise RuntimeError(f"OAuth {response.status_code}: {response_excerpt(response)}")
payload = response.json()
token = payload.get("access_token") or payload.get("accessToken")
if not token:
raise RuntimeError("OAuth ответ не содержит access_token.")
return token
def chat_completion(
token: str,
system_prompt: str,
user_prompt: str,
model: str,
temperature: float,
verify_ssl: bool,
timeout: int = 90,
) -> str:
disable_ssl_warnings_if_needed(verify_ssl)
response = requests.post(
CHAT_URL,
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
},
json={
"model": model or DEFAULT_MODEL,
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
"temperature": temperature,
"stream": False,
},
timeout=timeout,
verify=verify_ssl,
)
if response.status_code >= 400:
raise RuntimeError(f"Chat {response.status_code}: {response_excerpt(response)}")
payload = response.json()
return payload["choices"][0]["message"]["content"].strip()
def build_early_prompts(
dialog: str,
product_name: str,
product_features: str,
examples: str,
) -> PromptPair:
product_name = (product_name or DEFAULT_PRODUCT_NAME).strip()
product_features = (product_features or DEFAULT_BIZ_FEATURES).strip()
examples = (examples or DEFAULT_BIZ_EXAMPLES).strip()
dialog = (dialog or select_early_dialog("Командировки и подотчёт")).strip()
old_system = "Ты помощник по продажам оператора поддержки юридических лиц Сбербанка."
old_human = f"""Ниже приведен диалог клиента:
Диалог:
{dialog}
Ты помогаешь продать продукт '{product_name}'.
Особенности предложения: {product_features}
{examples}
При генерации следи, чтобы ни в одном предложении не появлялись слова и фразы:
"ранее клиент уже был", "пока", "специалист", "в процессе", "оператор:", "хочешь",
"могу помочь", "пока вы ожидаете", "пока мы решаем ваш вопрос", "текущей проблемы".
Избегай формулировок, связанных смыслом с наличием этих слов.
Фраза будет сказана оператором без изменений. Вопрос возможен только в конце фразы должен
быть направлен на утверждение, что подключить удобно и просто. Или предложением рассказать
подробнее. напр.: Подключить удобно и просто, рассказать подробнее?
Исключи приветствия и обсуждение проблемы клиента. Исключи информацию для оператора.
Должна быть только фраза продажи для клиента.
Фраза пишется исходя из Особенности предложения выбранного продукта. Фраза целиком должна
подтверждаться информацией из Особенности предложения. Исключи из Фразы все, что не
подтверждено из Особенности предложения.
Напиши фразу (и ничего кроме фразы) с предложением о продаже продукта, учитывая, что нельзя
упоминать процесс решения вопроса клиента. Учти контекст всего диалога и описание продукта.
Фраза должна содержать предложение подключения услуги и объяснение, почему клиенту продукт
может быть выгоден."""
new_system = (
"Ты — оператор поддержки юридических лиц Сбербанка. Ты пишешь короткую фразу, которой "
"оператор предложит клиенту банковский продукт. Фразу произнесут клиенту дословно. "
"Обращайся к клиенту на «вы»."
)
new_human = f"""Диалог с клиентом:
{dialog}
Продукт для предложения: {product_name}.
Факты о продукте (бери информацию и цифры только отсюда):
{product_features}
Как написать фразу:
1. Определи потребность клиента из диалога и выбери 1–2 преимущества продукта, которые её закрывают. Не перечисляй все возможности подряд.
2. Длина — 1–2 предложения, до 250 знаков. Коротко и по делу.
3. Используй только факты из блока о продукте. Если точного значения там нет — не указывай его и не выдумывай.
4. Концовка должна быть привязана к конкретной выгоде продукта или к простоте оформления. Запрещены концовки-отписки вида «рассказать подробнее?», «узнать больше?», «узнать подробности?», «нужна дополнительная информация?». Приглашение к подключению ставь в начало фразы («Подключите…», «Оформите…»), а не в концовку-вопрос.
5. Пиши только фразу для клиента: без приветствий, без обсуждения вопроса клиента, без пояснений для оператора.
Примеры ниже — образец тона и длины, а НЕ текст для копирования. Аргументы подбирай под конкретный диалог, а не переписывай примеры:
{examples}
Напиши только фразу."""
return PromptPair(old_system, old_human, new_system, new_human)
def extract_products_from_sale_prompt(raw_prompt: str) -> list[tuple[str, str]]:
decoded = decode_source(raw_prompt)
catalog_part = decoded.split("Используй информацию")[0]
chunks = re.split(r"Номер продукта:\s*(\d+)\.", catalog_part)
products: list[tuple[str, str]] = []
for index in range(1, len(chunks), 2):
number = chunks[index].strip()
body = chunks[index + 1].strip()
if number and body:
products.append((number, body))
return products
def renumber_catalog(products: list[tuple[str, str]]) -> str:
return "\n\n".join(
f"Номер продукта: {new_number}.\n{body.strip()}"
for new_number, (_old_number, body) in enumerate(products, 1)
)
def default_after_catalogs() -> tuple[str, str]:
products = extract_products_from_sale_prompt(read_data_file("sale_after_fix_chain.txt"))
if not products:
products = FALLBACK_AFTER_PRODUCTS
old_catalog = renumber_catalog(products)
by_number = {number: body for number, body in products}
keep_order = ["6", "2", "3", "4", "5", "7", "8"]
if all(number in by_number for number in keep_order):
cleaned = [(number, by_number[number]) for number in keep_order]
else:
cleaned = products[:]
return old_catalog, renumber_catalog(cleaned)
def textbox_copy_kwargs(textbox_cls: Any) -> dict[str, Any]:
textbox_params = inspect.signature(textbox_cls).parameters
if "show_copy_button" in textbox_params:
return {"show_copy_button": True}
if "buttons" in textbox_params:
return {"buttons": ["copy"]}
return {}
def build_after_prompts(dialog: str, old_catalog: str, new_catalog: str) -> PromptPair:
dialog = (dialog or select_after_dialog("Подотчёт")).strip()
old_catalog = (old_catalog or default_after_catalogs()[0]).strip()
new_catalog = (new_catalog or default_after_catalogs()[1]).strip()
system = "Ты помощник по продажам оператора поддержки юридических лиц Сбербанка."
old_human = f"""Ты помогаешь продать продукты только из списка:
{old_catalog}
Используй информацию и пояснения к продуктам строго в рамках каждого продукта.
Ниже приведен диалог оператора с клиентом.
Диалог:
{dialog}
При генерации избегай слов про процесс решения проблемы клиента ("пока", "специалист", "в процессе" и т.п.).
Продукт предлагается на основе потребностей, озвученных клиентом. Исключи приветствия и обсуждение проблемы клиента.
Фраза в sales_phrase пишется исходя из Особенности предложения выбранного продукта и должна целиком ими подтверждаться. Не указывай значений, которых нет в Особенности предложения.
Фраза будет сказана оператором без изменений. Вопрос возможен только в конце и должен быть направлен на утверждение, что подключить удобно и просто, или предложением рассказать подробнее.
product_number и sales_phrase должны быть заполнены! В ответе верни JSON:
{{"product_number": "номер продукта из списка" (str), "sales_phrase": "несколько предложений для начала продажи выбранного продукта" (str)}}
JSON:"""
new_human = f"""Ты помогаешь продать ОДИН продукт из списка:
{new_catalog}
Используй информацию строго в рамках выбранного продукта.
Диалог оператора с клиентом:
{dialog}
Как выбрать и написать:
1. Выбери один продукт, который закрывает потребность, озвученную клиентом в диалоге.
2. В sales_phrase возьми 1–2 преимущества этого продукта под потребность клиента. Не перечисляй всё подряд.
3. Длина sales_phrase — 1–2 предложения, до 250 знаков.
4. Используй только факты и цифры из «Особенности предложения» выбранного продукта. Нет значения — не выдумывай.
5. Концовка привязана к выгоде продукта или к простоте оформления. Запрещены концовки-отписки («рассказать подробнее?», «узнать больше?»). Приглашение к подключению ставь в начало фразы.
6. sales_phrase — только текст для клиента: без приветствий, без обсуждения проблемы, без слов про процесс её решения.
Верни строго JSON:
{{"product_number": "номер выбранного продукта" (str), "sales_phrase": "фраза для клиента" (str)}}
JSON:"""
return PromptPair(system, old_human, system, new_human)
def parse_json_object(text: str) -> dict[str, Any] | None:
match = re.search(r"\{.*\}", text, flags=re.DOTALL)
if not match:
return None
try:
parsed = json.loads(match.group(0))
except json.JSONDecodeError:
return None
return parsed if isinstance(parsed, dict) else None
def phrase_from_output(text: str, expects_json: bool) -> tuple[str, str, str]:
if not expects_json:
return text.strip(), "n/a", "n/a"
parsed = parse_json_object(text)
if parsed is None:
return text.strip(), "нет", ""
phrase = str(parsed.get("sales_phrase", "")).strip()
product_number = str(parsed.get("product_number", "")).strip()
return phrase, "да", product_number
def metrics_row(label: str, output: str, expects_json: bool) -> list[Any]:
phrase, json_ok, product_number = phrase_from_output(output, expects_json)
lower = phrase.lower()
flags = []
for fragment in BANNED_CLOSINGS:
if fragment in lower:
flags.append(fragment)
for fragment in BANNED_PROCESS_WORDS:
if fragment in lower:
flags.append(fragment)
if len(phrase) > 250:
flags.append(">250")
return [
label,
len(phrase),
"да" if phrase.rstrip().endswith("?") else "нет",
"да" if any(fragment in lower for fragment in BANNED_CLOSINGS) else "нет",
json_ok,
product_number,
", ".join(flags) if flags else "нет",
]
def run_prompt_pair(
pair: PromptPair,
api_credentials: str,
model: str,
temperature: float,
scope: str,
verify_ssl: bool,
) -> tuple[str, str]:
credentials = resolve_credentials(api_credentials)
token = request_token(credentials, scope or DEFAULT_SCOPE, verify_ssl)
old_output = chat_completion(token, pair.old_system, pair.old_human, model, temperature, verify_ssl)
new_output = chat_completion(token, pair.new_system, pair.new_human, model, temperature, verify_ssl)
return old_output, new_output
def compare_early(
api_credentials: str,
model: str,
temperature: float,
scope: str,
verify_ssl: bool,
dialog: str,
product_name: str,
product_features: str,
examples: str,
) -> tuple[str, str, list[list[Any]], str, str]:
pair = build_early_prompts(dialog, product_name, product_features, examples)
try:
old_output, new_output = run_prompt_pair(pair, api_credentials, model, temperature, scope, verify_ssl)
except Exception as exc:
old_output = f"[ОШИБКА] {exc}"
new_output = f"[ОШИБКА] {exc}"
rows = [
metrics_row("Старый", old_output, expects_json=False),
metrics_row("Новый", new_output, expects_json=False),
]
return old_output, new_output, rows, pair.old_human, pair.new_human
def compare_after(
api_credentials: str,
model: str,
temperature: float,
scope: str,
verify_ssl: bool,
dialog: str,
old_catalog: str,
new_catalog: str,
) -> tuple[str, str, list[list[Any]], str, str]:
pair = build_after_prompts(dialog, old_catalog, new_catalog)
try:
old_output, new_output = run_prompt_pair(pair, api_credentials, model, temperature, scope, verify_ssl)
except Exception as exc:
old_output = f"[ОШИБКА] {exc}"
new_output = f"[ОШИБКА] {exc}"
rows = [
metrics_row("Старый", old_output, expects_json=True),
metrics_row("Новый", new_output, expects_json=True),
]
return old_output, new_output, rows, pair.old_human, pair.new_human
def build_demo():
import gradio as gr
def copyable_textbox(**kwargs):
kwargs.update(textbox_copy_kwargs(gr.Textbox))
return gr.Textbox(**kwargs)
old_catalog, new_catalog = default_after_catalogs()
metric_headers = ["Версия", "Символов", "Финальный вопрос", "Отписка", "JSON", "Продукт", "Флаги"]
with gr.Blocks(title="GigaChat sales prompt A/B") as demo:
gr.Markdown("# GigaChat sales prompt A/B")
with gr.Row():
api_credentials = gr.Textbox(
label="GigaChat credentials",
type="password",
placeholder="Space Secret GIGACHAT_CREDENTIALS или GC_KEY_CORP",
scale=2,
)
model = gr.Dropdown(label="Модель", choices=MODEL_CHOICES, value=DEFAULT_MODEL, allow_custom_value=True)
temperature = gr.Slider(label="Temperature", minimum=0, maximum=1.5, value=0.87, step=0.01)
with gr.Accordion("API", open=False):
scope = gr.Textbox(label="Scope", value=DEFAULT_SCOPE)
verify_ssl = gr.Checkbox(label="Проверять SSL-сертификат", value=False)
with gr.Tab("Early proposal"):
early_sample = gr.Dropdown(
label="Сценарий",
choices=list(EARLY_DIALOGS.keys()),
value="Командировки и подотчёт",
)
early_dialog = gr.Textbox(label="Диалог", value=select_early_dialog("Командировки и подотчёт"), lines=4)
early_sample.change(select_early_dialog, inputs=early_sample, outputs=early_dialog)
with gr.Row():
product_name = gr.Textbox(label="Продукт", value=DEFAULT_PRODUCT_NAME)
product_features = gr.Textbox(label="Особенности предложения", value=DEFAULT_BIZ_FEATURES, lines=8)
product_examples = gr.Textbox(label="Примеры", value=DEFAULT_BIZ_EXAMPLES, lines=6)
early_run = gr.Button("Сравнить early", variant="primary")
with gr.Row():
early_old_output = copyable_textbox(label="Старый результат", lines=6)
early_new_output = copyable_textbox(label="Новый результат", lines=6)
early_metrics = gr.Dataframe(headers=metric_headers, label="Метрики", interactive=False)
with gr.Accordion("Собранные промпты", open=False):
early_old_prompt = copyable_textbox(label="Старый human prompt", lines=12)
early_new_prompt = copyable_textbox(label="Новый human prompt", lines=12)
early_run.click(
compare_early,
inputs=[
api_credentials,
model,
temperature,
scope,
verify_ssl,
early_dialog,
product_name,
product_features,
product_examples,
],
outputs=[
early_old_output,
early_new_output,
early_metrics,
early_old_prompt,
early_new_prompt,
],
)
with gr.Tab("After fix"):
after_sample = gr.Dropdown(label="Сценарий", choices=list(AFTER_DIALOGS.keys()), value="Подотчёт")
after_dialog = gr.Textbox(label="Диалог", value=select_after_dialog("Подотчёт"), lines=4)
after_sample.change(select_after_dialog, inputs=after_sample, outputs=after_dialog)
with gr.Accordion("Каталоги", open=False):
old_catalog_box = gr.Textbox(label="Старый каталог", value=old_catalog, lines=12)
new_catalog_box = gr.Textbox(label="Новый каталог", value=new_catalog, lines=12)
after_run = gr.Button("Сравнить after_fix", variant="primary")
with gr.Row():
after_old_output = copyable_textbox(label="Старый JSON", lines=8)
after_new_output = copyable_textbox(label="Новый JSON", lines=8)
after_metrics = gr.Dataframe(headers=metric_headers, label="Метрики", interactive=False)
with gr.Accordion("Собранные промпты", open=False):
after_old_prompt = copyable_textbox(label="Старый human prompt", lines=12)
after_new_prompt = copyable_textbox(label="Новый human prompt", lines=12)
after_run.click(
compare_after,
inputs=[
api_credentials,
model,
temperature,
scope,
verify_ssl,
after_dialog,
old_catalog_box,
new_catalog_box,
],
outputs=[
after_old_output,
after_new_output,
after_metrics,
after_old_prompt,
after_new_prompt,
],
)
return demo
try:
demo = build_demo()
except ModuleNotFoundError as exc:
if exc.name != "gradio":
raise
demo = None
if __name__ == "__main__":
if demo is None:
raise RuntimeError("Gradio is not installed. Run: python -m pip install -r hf_space/requirements.txt")
demo.queue(default_concurrency_limit=2).launch()