From f18c564eadbb8ff1b68b8e5df6ef2d52894e5f18 Mon Sep 17 00:00:00 2001 From: dddennnisss Date: Sat, 22 Aug 2026 20:47:57 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82-=D1=80=D0=B5=D0=B6?= =?UTF-8?q?=D0=B8=D0=BC:=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20/api/photo-bot/check/?= =?UTF-8?q?=20=D1=81=D0=B0=D0=B9=D1=82=D0=B0=20(CV+=D0=B3=D1=80=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=D1=81=D1=8B+=D0=98=D0=98),=20=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D1=80=D0=B8=D0=BA=D0=B8=20=D0=B8=D0=B7=20llama.cpp,=20=D1=83?= =?UTF-8?q?=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D1=8F?= =?UTF-8?q?=D0=BC=D0=BE=D0=B3=D0=BE=20llama.cpp-=D0=BA=D0=BB=D0=B8=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/__pycache__/config.cpython-313.pyc | Bin 8605 -> 8789 bytes config/config.py | 6 - .../__pycache__/handlers.cpython-313.pyc | Bin 28218 -> 28383 bytes .../photo_processor.cpython-313.pyc | Bin 0 -> 11532 bytes photo_bot/ai_validator.py | 148 ------------------ photo_bot/handlers.py | 34 ++-- photo_bot/photo_processor.py | 33 ++++ utils/__pycache__/texts.cpython-313.pyc | Bin 49197 -> 50826 bytes utils/texts.py | 101 +++++++----- 9 files changed, 117 insertions(+), 205 deletions(-) create mode 100644 photo_bot/__pycache__/photo_processor.cpython-313.pyc delete mode 100644 photo_bot/ai_validator.py diff --git a/config/__pycache__/config.cpython-313.pyc b/config/__pycache__/config.cpython-313.pyc index 4b3cb153dfc45cb094470d7e59e4978ef6e881f2..d7014fd299623ba8219cc95021df7207a7484286 100644 GIT binary patch delta 978 zcmZ`#OHW!+5I*+`sJ9}b2q=oh_Y-_@6-`aFfPxP|=K5?c0^TZBfeh57Caor2*`!IE zyJ}L?Z5Q1%_!sQTRvOZT_yd}_Gj`>!2d%9&@g(0jllf-mJDENE4@dDYrII7`Eq&a5 z5*>J3tY?g$n_kIlOlSfV)>70_)I%*c(Aa1pP&dV3la!WM@bw_)+1xN8*bEKW0*%-T zP1pv_*bXh&0j+3;Hnc!HcGA2HI zb&K>Yze;Jna>G90rHO(Xc^?kJpcF6A<~$g?6la=Q9DVynCsh|}?>PSUyZ)56oiM`Z zU49bX^b}(-isRtI32@^ijG+g{(M#cj37mpS^us*_@Sp%*oCY7xz!c7cALr;P=fQ#t zbjJgVhZKtd_y`0H(0qv=DG1XTf*B0cxD2x>!Yqf6VGdVd9#^IP!bK(~m2wtlA$6ZS zVhgG)$&I)r9!k|H*JaznNQ-={YB^zE!^mbVyedZ5VnfMr!kmbN!s3P*pKeudY?z}l z6r&*#S8iw(LGxM!;}JTzc|+2MV#z>KOeAmAD>uz_`)X`Gwi??k{$EV}R6oz_>$6%z z!R7uQk++4dVQarl?BcDxHomtz+#Bp(4)%n4yE?`G@Ja5(C;OBl1^I+XuhsKxOCQF>vh=RQy;h9a?-`efK;ZH6={%a-ZVq$I1OxhAX8r-?dSs@+j#i%ND1 zPxYiszoRJX(=VwlZ#G0%tQI=FV*z;rk z+WB$0m=LJrLD5}Mk%M*)IN8B~g7OzqNGpd%y+CA#0~*R-dA%T)eN$^F9~J2ZPIlC+ Zp`795v@@epz+hgcyc%qI8JnEuzX2l43%CFP delta 764 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 asmr$8l_>W}=qf|(RhCiYT~UxBo%&xyrFlF6 delta 4825 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 diff --git a/photo_bot/__pycache__/photo_processor.cpython-313.pyc b/photo_bot/__pycache__/photo_processor.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3cbe4a98abfcfd01a8f97c2129c8fd12b835caf7 GIT binary patch literal 11532 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 literal 0 HcmV?d00001 diff --git a/photo_bot/ai_validator.py b/photo_bot/ai_validator.py deleted file mode 100644 index a3529c6..0000000 --- a/photo_bot/ai_validator.py +++ /dev/null @@ -1,148 +0,0 @@ -# /opt/trueconf_bot/photo_bot/ai_validator.py -# Тестовый мод фото-бота: прямая проверка фото ИИ (llama.cpp). -# Endpoint, модель и промпт — 1:1 из SibCemProfile/PhotoApp/photo_validator.py. -# Фото никуда не отправляется: только в ИИ, ответ возвращается пользователю. -import base64 -import io -import json -import logging -import time -import urllib.request -from PIL import Image - -import config.config as config - -logger = logging.getLogger(__name__) - -# Тег рассуждений thinking-моделей (конкатенация без литерала) -THINK_CLOSE_TAG = '' - - -def _build_prompt() -> str: - """Промпт проверки фото (без подсказки MediaPipe — в тест-моде геометрия не считается).""" - cv_hint = 'ℹ️ ПОДСКАЗКА ОТ ALGORITHM: Геометрическая проверка в тестовом режиме не выполняется, проверяй визуально.' - - return ('''Ты — строгий инспектор пропускной системы. Проверь фотографию сотрудника на соответствие регламенту. - -%s - -ЗАПРЕЩЕНО выводить внутренние рассуждения и теги размышлений. Сразу пиши финальный ответ. - -ПРЯМО СЕЙЧАС ВНИМАТЕЛЬНО РАССМОТРИ ФОТО И ПОСЛЕДОВАТЕЛЬНО ЗАФИКСИРУЙ: -A) РЕАЛЬНОСТЬ: это живое фото, а не ИИ-генерация, рисунок, 3D, мультфильм, фильтр? -B) ГРАФИКА: есть ли рамка, любые буквы/надписи/подписи (включая подпись с именем внизу), водяные знаки, декоративный фон с буквами, коллаж? -C) МИМИКА: высунут ли язык? Скривлены ли губы? Есть ли гримаса, смешная рожа, щёлканье зубами, смех, скос глаз, закатанные глаза? -D) РАКУРС: профиль, затылок, взгляд в сторону? -E) МАСШТАБ: ниже линии груди видны ли живот, талия, подмышки? - -ПРАВИЛО ОТКАЗА: если по любому пункту A–E есть хоть малейшее отклонение — вердикт «Не прошел». -Смех, ржа, рожи, высунутый язык, скривлённые губы — фото НЕ ПРОХОДИТ, даже если лицо анфас и хорошо освещено. -Рамка или буквы/подпись на фоне или по краю кадра — фото НЕ ПРОХОДИТ. - -ИТОГОВЫЙ ВЕРДИКТ: -- «Прошел» — только если все пункты A–E идеальны: нейтральное спокойное лицо, чистый кадр без рамки и букв. -- «Не прошел» — любое отклонение, описанное выше. -- «На модерацию» — только если фото реальное и лицо нейтральное, но масштаб или ракурс на грани нормы. - -ФОРМАТ ОТВЕТА — СТРОГО 4 строки, без других слов: -Мимика: <нейтрально / высунут язык / гримаса / скривленная улыбка / смех — что именно видно> -Графика: <нет / рамка / буквы / подпись / коллаж — что именно видно> -Вердикт: [Прошел / Не прошел / На модерацию] -Причина: [1–2 предложения с указанием конкретного пункта A–E] -''') % cv_hint - - -def validate_photo_with_ai(file_bytes: bytes) -> dict: - """ - Прямая проверка фото ИИ (тестовый мод). - - Возвращает {'ok': bool, 'verdict': str, 'reason': str, 'error': str, - 'metrics': {'total_time', 'prompt_tokens', 'completion_tokens', - 'prompt_per_second', 'predicted_per_second'}, - 'error_time': float} - """ - t0 = time.perf_counter() - try: - # Сжатие (PIL): разрешение настраивается в config (PHOTO_TEST_AI_MAX_SIDE) - side = int(config.PHOTO_TEST_AI_MAX_SIDE) - with Image.open(io.BytesIO(file_bytes)) as img: - img = img.convert('RGB') - img.thumbnail((side, side)) - buffer = io.BytesIO() - img.save(buffer, format='JPEG', quality=85) - b64_img = base64.b64encode(buffer.getvalue()).decode('utf-8') - - payload = { - 'model': config.PHOTO_TEST_AI_MODEL, - 'messages': [{ - 'role': 'user', - 'content': [ - {'type': 'text', 'text': _build_prompt()}, - {'type': 'image_url', 'image_url': {'url': 'data:image/jpeg;base64,' + b64_img}} - ] - }], - 'temperature': 0.1, - 'max_tokens': 1000 - } - - req = urllib.request.Request( - config.PHOTO_TEST_AI_URL, - data=json.dumps(payload).encode('utf-8'), - headers={'Content-Type': 'application/json'} - ) - - with urllib.request.urlopen(req, timeout=config.PHOTO_TEST_AI_TIMEOUT) as resp: - res = json.loads(resp.read().decode('utf-8')) - total_time = time.perf_counter() - t0 - - # llama.cpp в ответ кладёт usage (токены) и timings (скорости) - usage = res.get('usage') or {} - timings = res.get('timings') or {} - metrics = { - 'total_time': total_time, - 'prompt_tokens': usage.get('prompt_tokens'), - 'completion_tokens': usage.get('completion_tokens'), - 'prompt_per_second': timings.get('prompt_per_second'), - 'predicted_per_second': timings.get('predicted_per_second'), - } - - full_content = (res['choices'][0]['message'].get('content') or '').strip() - clean_ans = full_content.split(THINK_CLOSE_TAG)[-1].strip() if THINK_CLOSE_TAG in full_content else full_content.strip() - - text_lower = clean_ans.lower() - if 'вердикт: прошел' in text_lower or ('прошел' in text_lower and 'не прошел' not in text_lower and 'модерац' not in text_lower): - verdict = 'Прошел' - elif 'на модерацию' in text_lower or 'модерац' in text_lower: - verdict = 'На модерацию' - else: - verdict = 'Не прошел' - - reason = '' - mimi = '' - grafika = '' - for line in clean_ans.splitlines(): - ls = line.strip().lower() - if ls.startswith('причина:'): - reason = line.split(':', 1)[1].strip() - elif ls.startswith('мимика:'): - mimi = line.split(':', 1)[1].strip() - elif ls.startswith('графика:'): - grafika = line.split(':', 1)[1].strip() - if not reason: - reason = clean_ans.replace('\n', ' ').strip() - - logger.info( - f"[TEST MODE] ИИ-вердикт: {verdict} | мимика={mimi} | графика={grafika} | {reason}" - f" | total={metrics['total_time']:.2f}s " - f"prompt={metrics['prompt_tokens']}tok/({metrics['prompt_per_second']:.1f} tps) " - f"gen={metrics['completion_tokens']}tok/({metrics['predicted_per_second']:.1f} tps)" - ) - return {'ok': True, 'verdict': verdict, 'reason': reason, 'error': '', - 'mimi': mimi, 'grafika': grafika, 'metrics': metrics} - - except Exception as e: - err = f"{type(e).__name__}: {e}" - elapsed = time.perf_counter() - t0 - logger.error(f"[TEST MODE] Ошибка обращения к ИИ через {elapsed:.1f} сек: {err}") - return {'ok': False, 'verdict': '', 'reason': '', 'error': err, - 'error_time': elapsed} diff --git a/photo_bot/handlers.py b/photo_bot/handlers.py index f092621..8e6f403 100644 --- a/photo_bot/handlers.py +++ b/photo_bot/handlers.py @@ -14,8 +14,7 @@ from trueconf.types import FSInputFile import config.config as config from utils import states from utils.menu import MENU_TEXT -from photo_bot.photo_processor import prepare_ad_photo, get_crop_coordinates -from photo_bot.ai_validator import validate_photo_with_ai +from photo_bot.photo_processor import prepare_ad_photo, get_crop_coordinates, check_photo_api from utils.texts import ( UNKNOWN_MAIN_CMD_TEXT, EMOJI_DIGITS, @@ -324,29 +323,36 @@ async def photo_module_handler(msg: Message): try: with open(original_path, 'rb') as f: raw_photo = f.read() - ai_res = await asyncio.to_thread(validate_photo_with_ai, raw_photo) + # Dry-run: полный пайплайн проверки на САЙТЕ (CV + гримасы + ИИ), + # фото в БД/AD/Orion не попадает + check_res = await asyncio.to_thread(check_photo_api, raw_photo) except Exception as e: - logger.error(f"🔥 [TEST MODE] Ошибка чтения фото: {e}") - ai_res = {'ok': False, 'verdict': '', 'reason': '', 'error': str(e)} + logger.error(f"🔥 [TEST MODE] Ошибка проверки фото: {e}") + check_res = {"status": "error", "reason": f"Ошибка бота: {e}"} - if not ai_res['ok']: + if check_res.get("status") != "ok": # Фото остаётся в pending — можно повторить проверку (кнопка 1) - log_menu_stats(user_id, "Photo Bot (TEST)", "Ошибка обращения к ИИ") + log_menu_stats(user_id, "Photo Bot (TEST)", f"Ошибка проверки: {check_res.get('reason')}") await msg.answer( - photo_test_ai_error(ai_res.get('error', ''), ai_res.get('error_time')), + photo_test_ai_error(check_res.get('reason', 'Неизвестная ошибка')), parse_mode="html" ) return - verdict = ai_res['verdict'] - reason = ai_res['reason'] or 'Причина не указана.' - mimi = ai_res.get('mimi', '') - grafika = ai_res.get('grafika', '') - log_menu_stats(user_id, "Photo Bot (TEST)", f"ИИ-вердикт: {verdict}") + verdict = check_res.get('verdict', '—') + log_menu_stats(user_id, "Photo Bot (TEST)", f"Вердикт проверки: {verdict}") # Фото остаётся в pending — можно повторить проверку другим фото (кнопка 2) await msg.answer( - photo_test_result(verdict, reason, mimi, grafika, ai_res.get('metrics')), + photo_test_result( + verdict, + check_res.get('ai_reason') or '', + check_res.get('cv_approved'), + check_res.get('cv_reason') or '', + check_res.get('forced_moderation'), + check_res.get('timings'), + check_res.get('ai_metrics'), + ), parse_mode="html" ) return diff --git a/photo_bot/photo_processor.py b/photo_bot/photo_processor.py index 1e92ec7..ea99af4 100644 --- a/photo_bot/photo_processor.py +++ b/photo_bot/photo_processor.py @@ -187,3 +187,36 @@ def prepare_ad_photo(input_path: str, user_id: str, output_path: str = None, tar except Exception as e: logger.error(f"Ошибка в prepare_ad_photo: {e}", exc_info=True) return {"success": False, "error": str(e)} + + +def check_photo_api(image_bytes: bytes, timeout: float = 300.0) -> dict: + """ + Dry-run проверка фото через API сайта (POST /api/photo-bot/check/). + + Фото проходит полный пайплайн проверки на сайте (MediaPipe CV + + ARKit-формы + VLM ИИ), НО ни в БД, ни в AD, ни в Orion не попадает — + ответ возвращается боту для тест-под-режима. + + Returns: + {"status": "ok", "verdict", "ai_reason", "cv_approved", "cv_reason", + "raw_answer", "forced_moderation", "timings", "ai_metrics"} + или {"status": "error", "reason"} + """ + url = API_URL.replace("/upload/", "check/") + try: + response = requests.post( + url, + headers={"X-API-Key": API_KEY}, + files={"image": ("photo.jpg", image_bytes, "image/jpeg")}, + timeout=timeout, + verify=False + ) + response.raise_for_status() + logger.info(f"API /check/ response: status={response.status_code}, body[:500]: {response.text[:500]}") + return response.json() + except requests.exceptions.RequestException as e: + logger.error(f"Ошибка /check/ API: {e}") + return {"status": "error", "reason": f"Ошибка сети при обращении к сайту: {e}"} + except Exception as e: + logger.error(f"Ошибка /check/: {e}") + return {"status": "error", "reason": f"Ошибка сайта проверки: {e}"} diff --git a/utils/__pycache__/texts.cpython-313.pyc b/utils/__pycache__/texts.cpython-313.pyc index f22aab0667263d3489f699e081fefaef07a7f703..3511c605ecbb96156ef70d186a2901a1fe55278f 100644 GIT binary patch delta 7909 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 J`z;+}`WJ;HBtZZG delta 6475 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 diff --git a/utils/texts.py b/utils/texts.py index 2c094e7..c827b8a 100644 --- a/utils/texts.py +++ b/utils/texts.py @@ -283,7 +283,7 @@ def _photo_test_preview(emojis: dict) -> str: return ( f"🧪 Тестовый режим\n\n" f"Кадр получен. Фото никуда не публикуется.\n\n" - f"{emojis['1']} — отправлю фото напрямую в ИИ\n" + f"{emojis['1']} — отправлю фото на проверку по сайту\n" f"{emojis['2']} — Загрузить другое фото\n" f"{emojis['9']} — Назад\n" f"{emojis['0']} — В главное меню" @@ -291,59 +291,85 @@ def _photo_test_preview(emojis: dict) -> str: def _photo_test_ai_checking(emojis: dict) -> str: return ( - "🧪 Отправляю фото напрямую в ИИ (тестовый режим)...\n" + "🧪 Отправляю фото на проверку по сайту (тестовый режим)...\n" + "Фото проходят полный контроль: CV, гримасы, ИИ.\n" "Обычно это занимает 1-3 минуты. Пожалуйста, подождите..." ) def _photo_test_receiving(emojis: dict) -> str: return "🧪 Ищу лицо на фото (тестовый режим), пожалуйста, подождите..." -def _photo_test_result(verdict: str, reason: str, mimi: str = '', grafika: str = '', - ai_metrics: dict = None, emojis: dict = None) -> str: +def _photo_test_result(verdict: str, reason: str, cv_approved=None, cv_reason: str = '', + forced=None, timings: dict = None, ai_metrics: dict = None, + emojis: dict = None) -> str: emojis = emojis or {} icons = {"Прошел": "✅", "Не прошел": "❌", "На модерацию": "⏳"} icon = icons.get(verdict, "ℹ️") - lines = [] - if mimi: - lines.append(f"🙂 Мимика: {mimi}") - if grafika: - lines.append(f"🖼 Графика: {grafika}") - if lines: - detail = "\n".join(lines) + "\n\n" + + parts = [ + "🧪 Тестовый режим: результат проверки\n", + f"{icon} Вердикт: {verdict}\n", + ] + if reason: + parts.append(f"📝 Причина (ИИ): {reason}\n") + + # Детерминированный слой (MediaPipe CV + ARKit-формы) + if cv_approved is True: + line = "🎯 CV-проверка: ✅ пройдена" + if cv_reason: + line += f" — {cv_reason}" + parts.append(line + "\n") + elif cv_approved is False: + parts.append("🎯 CV-проверка: ❌ отклонена\n") + if cv_reason: + parts.append(f"↳ {cv_reason}\n") else: - detail = "" - metrics_block = '' + parts.append("🎯 CV-проверка: —\n") + + if forced: + parts.append("ℹ️ На сайте включён режим принудительной модерации: в реальном режиме фото ушло бы модератору, а не было опубликовано сразу.\n") + + # Тайминги этапов + if timings: + names = [ + ('cv', "CV (геометрия)"), + ('expression', "Гримасы (ARKit-формы)"), + ('ai', "ИИ (llama.cpp)"), + ('total', "Всего"), + ] + tl = ["📊 Технические показатели (по этапам)"] + for k, name in names: + v = timings.get(k) + if v is not None: + tl.append(f"⏱ {name}: {float(v):.2f} сек") + if len(tl) > 1: + parts.append("\n".join(tl) + "\n") + + # Токены и скорость (сайт передаёт их из ответа llama.cpp) if ai_metrics: - ml = ["📊 Технические показатели"] - tt = ai_metrics.get('total_time') - if tt is not None: - ml.append(f"⏱ Время ответа: {tt:.2f} сек") + ml = ["🔢 ИИ: токены и скорость"] pt = ai_metrics.get('prompt_tokens') gps = ai_metrics.get('prompt_per_second') if pt is not None: s = f"⬇️ Приём (промпт): {pt} токенов" if gps is not None: - s += f" — {gps:.1f} ток/сек" + s += f" — {float(gps):.1f} ток/сек" ml.append(s) ct = ai_metrics.get('completion_tokens') ggs = ai_metrics.get('predicted_per_second') if ct is not None: s = f"⬆️ Отдача (генерация): {ct} токенов" if ggs is not None: - s += f" — {ggs:.1f} ток/сек" + s += f" — {float(ggs):.1f} ток/сек" ml.append(s) if len(ml) > 1: - metrics_block = "\n".join(ml) + "\n\n" - return ( - f"🧪 Тестовый режим: вердикт ИИ\n\n" - f"{icon} Вердикт: {verdict}\n" - f"{detail}" - f"📝 Причина: {reason}\n\n" - f"{metrics_block}" - f"⚠️ Тестовый режим: фото не публиковалось и никуда не отправлялось.\n\n" - f"{emojis.get('2', '2')} — Загрузить другое фото\n" - f"{emojis.get('0', '0')} — В главное меню" - ) + parts.append("\n".join(ml) + "\n") + + parts.append("⚠️ Тестовый режим: фото не публиковалось и никуда не отправлялось.\n\n") + parts.append(f"{emojis.get('2', '2')} — Загрузить другое фото\n") + parts.append(f"{emojis.get('9', '9')} — Назад\n") + parts.append(f"{emojis.get('0', '0')} — В главное меню") + return "\n".join(parts) def _photo_test_ai_error(emojis: dict, error: str = '', elapsed: float = None) -> str: detail = '' @@ -355,9 +381,9 @@ def _photo_test_ai_error(emojis: dict, error: str = '', elapsed: float = None) - if elapsed is not None: detail += f"⏱ Время до ошибки: {elapsed:.1f} сек\n" return ( - f"❌ Ошибка обращения к ИИ.\n\n" + f"❌ Ошибка проверки фото.\n\n" f"{detail}\n" - f"Проверьте доступность ИИ-сервиса. Повторите попытку позже.\n\n" + f"Сервис проверки временно недоступен. Повторите попытку позже.\n\n" f"{emojis['1']} — Повторить проверку\n" f"{emojis['2']} — Загрузить другое фото\n" f"{emojis['0']} — В главное меню" @@ -448,12 +474,13 @@ PHOTO_TEST_PREVIEW_TEXT = _photo_test_preview(EMOJI_DIGITS) PHOTO_TEST_AI_CHECKING_TEXT = _photo_test_ai_checking(EMOJI_DIGITS) PHOTO_TEST_RECEIVING_TEXT = _photo_test_receiving(EMOJI_DIGITS) -def photo_test_result(verdict: str, reason: str, mimi: str = '', grafika: str = '', - ai_metrics: dict = None) -> str: - return _photo_test_result(verdict, reason, mimi, grafika, ai_metrics, EMOJI_DIGITS) +def photo_test_result(verdict: str, ai_reason: str = '', cv_approved=None, cv_reason: str = '', + forced=None, timings: dict = None, ai_metrics: dict = None) -> str: + return _photo_test_result(verdict, ai_reason, cv_approved, cv_reason, + forced, timings, ai_metrics, EMOJI_DIGITS) -def photo_test_ai_error(error: str = '', elapsed: float = None) -> str: - return _photo_test_ai_error(EMOJI_DIGITS, error, elapsed) +def photo_test_ai_error(error: str = '') -> str: + return _photo_test_ai_error(EMOJI_DIGITS, error) # =========================================================