From 7fa515de58dd6c90e6ee300fee9cfc4e44e0be52 Mon Sep 17 00:00:00 2001 From: dddennnisss Date: Sat, 22 Aug 2026 10:59:29 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=BE=D1=82=D0=BE=20(=D1=82=D0=B5=D1=81?= =?UTF-8?q?=D1=82-=D1=80=D0=B5=D0=B6=D0=B8=D0=BC):=20=D0=B4=D0=B5=D1=82?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BE=D1=82=D1=87=D1=91?= =?UTF-8?q?=D1=82=20=D0=98=D0=98=20=E2=80=94=20=D0=B2=D1=80=D0=B5=D0=BC?= =?UTF-8?q?=D1=8F,=20=D1=82=D0=BE=D0=BA=D0=B5=D0=BD=D1=8B=20(=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D1=91=D0=BC/=D0=BE=D1=82=D0=B4=D0=B0=D1=87=D0=B0),=20?= =?UTF-8?q?=D1=81=D0=BA=D0=BE=D1=80=D0=BE=D1=81=D1=82=D1=8C=20=D1=82=D0=BE?= =?UTF-8?q?=D0=BA/=D1=81=D0=B5=D0=BA,=20=D0=B4=D0=B5=D1=82=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__pycache__/ai_validator.cpython-313.pyc | Bin 0 -> 9726 bytes .../__pycache__/handlers.cpython-313.pyc | Bin 26672 -> 26855 bytes photo_bot/ai_validator.py | 35 +++++++++++-- photo_bot/handlers.py | 10 +++- utils/__pycache__/texts.cpython-313.pyc | Bin 48377 -> 50156 bytes utils/texts.py | 46 +++++++++++++++--- 6 files changed, 78 insertions(+), 13 deletions(-) create mode 100644 photo_bot/__pycache__/ai_validator.cpython-313.pyc diff --git a/photo_bot/__pycache__/ai_validator.cpython-313.pyc b/photo_bot/__pycache__/ai_validator.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1312d144c00da9455356bec0e581147ee205da8 GIT binary patch literal 9726 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 YoBoWz`bOt{jz;ni(b(^4C}k!815yAt%>V!Z delta 2002 zcmZ8hSxg&O6rJmNw!zqVVHq1^jKS=IU~GdiI56xXg*Hr`5TP^$l%V7zQAwl9*x23M zd^PG*fBMx$RX3?kWJO(6sYH$1AhJLp2`vdrf10IDL!uHE1AXtgR8>csbI-ZwzWeUX z8_jPMaC#h)A89lyj$DS<-#qeb-taIsQZiIVlZDzTYPkZ%#&v89gx*NUxAZ17e(TFQ3gF?JkS)jGKA#NSj`}I;0 zc2o&a%z7QEDuWWHw-MdXmr7+=@Ys~w{XE-mx6_FCdQ94K$>Em$(<5D~z!8rLoU}vq zTh9oX;#2(mzbRCbn@r&X{8P<6sKn9Quas4OSu!Vi@N+%^s_{u*2Y915d@}$wB$bXw z>q<La9UQ#J0w2)9!eslRv8Nk^c47QWrkn*bEZ>rqKpZ%ayQWCvz28;%-JC*pt(G1%oQ@G9(0mFt<^^WV8%2(& z1tSD4Xu(9Cg@}b&3t48W$WYm6!ONT-Ei$^zZ4N>i=r-grkq;pYx?SL-;*K3c+2~Fl zoB2+0PT0%cRYNEj-PJUd5%GJCK9tY@p%zU<+|Sd8JpBDik(NC$!mtWGFo`G}@p6k6 z7}lW0EJU2e95&9Py=nV!3b&L3!#cE-iYPZ4h`&^92wVB3+8V+iS3%f;9(zg%U(SWF z3oYAekL5zvW4Xvo#0r9N6E1bwFP6dWtHX$*)_A!UDgcfN-K>L#d-&kdypI zCQm9itg3LCa-)bY-f+>i8{`-WdR1aQ;x|!wq>kToL8MBt>6TOOmD7)9vyKGBpCdPw zMQwwP^k4Hl{_tQ2T#YUstOppy6$7n`(=;E$k%0`*M&BK9K;|F dict: """ Прямая проверка фото ИИ (тестовый мод). - Возвращает {'ok': bool, 'verdict': str, 'reason': str, 'error': str} + Возвращает {'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) @@ -88,6 +93,18 @@ def validate_photo_with_ai(file_bytes: bytes) -> dict: 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() @@ -114,10 +131,18 @@ def validate_photo_with_ai(file_bytes: bytes) -> dict: if not reason: reason = clean_ans.replace('\n', ' ').strip() - logger.info(f"[TEST MODE] ИИ-вердикт: {verdict} | мимика={mimi} | графика={grafika} | {reason}") + 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} + 'mimi': mimi, 'grafika': grafika, 'metrics': metrics} except Exception as e: - logger.error(f"[TEST MODE] Ошибка обращения к ИИ: {e}") - return {'ok': False, 'verdict': '', 'reason': '', 'error': str(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 e02371a..a44ddb4 100644 --- a/photo_bot/handlers.py +++ b/photo_bot/handlers.py @@ -323,7 +323,10 @@ async def photo_module_handler(msg: Message): if not ai_res['ok']: # Фото остаётся в pending — можно повторить проверку (кнопка 1) log_menu_stats(user_id, "Photo Bot (TEST)", "Ошибка обращения к ИИ") - await msg.answer(photo_test_ai_error(), parse_mode="html") + await msg.answer( + photo_test_ai_error(ai_res.get('error', ''), ai_res.get('error_time')), + parse_mode="html" + ) return verdict = ai_res['verdict'] @@ -333,7 +336,10 @@ async def photo_module_handler(msg: Message): log_menu_stats(user_id, "Photo Bot (TEST)", f"ИИ-вердикт: {verdict}") # Фото остаётся в pending — можно повторить проверку другим фото (кнопка 2) - await msg.answer(photo_test_result(verdict, reason, mimi, grafika), parse_mode="html") + await msg.answer( + photo_test_result(verdict, reason, mimi, grafika, ai_res.get('metrics')), + parse_mode="html" + ) return elif cmd == "2": diff --git a/utils/__pycache__/texts.cpython-313.pyc b/utils/__pycache__/texts.cpython-313.pyc index 82b9084b3ec89d6ec0bc9ae3a848b576c99aeccd..25ac25c3e59e469e1cece366dbd262a0b6501581 100644 GIT binary patch delta 7153 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 r@1KqF`HeGX-cSKeI%9F=;wN str: def _photo_test_receiving(emojis: dict) -> str: return "🧪 Ищу лицо на фото (тестовый режим), пожалуйста, подождите..." -def _photo_test_result(verdict: str, reason: str, mimi: str = '', grafika: str = '', emojis: dict = None) -> str: +def _photo_test_result(verdict: str, reason: str, mimi: str = '', grafika: str = '', + ai_metrics: dict = None, emojis: dict = None) -> str: emojis = emojis or {} icons = {"Прошел": "✅", "Не прошел": "❌", "На модерацию": "⏳"} icon = icons.get(verdict, "ℹ️") @@ -322,19 +323,51 @@ def _photo_test_result(verdict: str, reason: str, mimi: str = '', grafika: str = detail = "\n".join(lines) + "\n\n" else: detail = "" + metrics_block = '' + if ai_metrics: + ml = ["📊 Технические показатели"] + tt = ai_metrics.get('total_time') + if tt is not None: + ml.append(f"⏱ Время ответа: {tt:.2f} сек") + 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} ток/сек" + 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} ток/сек" + 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')} — В главное меню" ) -def _photo_test_ai_error(emojis: dict) -> str: +def _photo_test_ai_error(emojis: dict, error: str = '', elapsed: float = None) -> str: + detail = '' + err = (error or '').strip() + if len(err) > 400: + err = err[:400] + '…' + if err: + detail += f"📋 Детали: {err}\n" + if elapsed is not None: + detail += f"⏱ Время до ошибки: {elapsed:.1f} сек\n" return ( f"❌ Ошибка обращения к ИИ.\n\n" + f"{detail}\n" f"Проверьте доступность ИИ-сервиса. Повторите попытку позже.\n\n" f"{emojis['1']} — Повторить проверку\n" f"{emojis['2']} — Загрузить другое фото\n" @@ -428,11 +461,12 @@ 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 = '') -> str: - return _photo_test_result(verdict, reason, mimi, grafika, 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_ai_error() -> str: - return _photo_test_ai_error(EMOJI_DIGITS) +def photo_test_ai_error(error: str = '', elapsed: float = None) -> str: + return _photo_test_ai_error(EMOJI_DIGITS, error, elapsed) # =========================================================