File size: 10,119 Bytes
9963647
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# -*- coding: utf-8 -*-
import requests
import json
from config import GEMINI_API_BASE, GEMINI_MODEL

# Список базовых локальных стоп-слов или подозрительных спам-маркеров
SPAM_TRIGGER_WORDS = [
    "купити зараз", "купи дешево", "безкоштовні гроші", "free money", "click here",
    "клікни сюди", "заробіток без вкладень", "швидкий прибуток", "найкраща ціна",
    "гарантований дохід", "розіграш айфонів", "iphone giveaway"
]

def local_safety_check(text):
    """
    Проверяет текст на наличие базовых спам-маркеров (регистронезависимо).
    """
    text_lower = text.lower()
    for word in SPAM_TRIGGER_WORDS:
        if word in text_lower:
            return False, f"Обнаружено локальное стоп-слово: '{word}'"
    return True, "Локальный тест безопасности пройден."

def gemini_compliance_audit(text):
    """
    Использует gemini-web2api для комплексного аудита текста на триггер-слова, 
    токсичность или спам-маркеры Meta Safety Compliance.
    Если текст не проходит аудит, возвращает перефразированную безопасную версию.
    """
    prompt = f"""
Проаналізуй наступний текст для публікації в мережі Threads на відповідність правилам безпеки (Meta Safety Compliance): відсутність спаму, агресивного маркетингу, заборонених слів чи клікбейту, який може призвести до тіньового бану (Shadowban).

Текст:
\"\"\"{text}\"\"\"

Якщо текст повністю безпечний і відповідає правилам, поверни його без змін у форматі JSON:
{{"status": "safe", "text": "оригінальний текст"}}

Якщо текст містить небезпечні слова, спам-триггери чи підозрілі конструкції, перефразуй його безпечною та природною українською мовою та поверни у форматі JSON:
{{"status": "rewritten", "text": "новий безпечний текст"}}

Повертай ТІЛЬКИ чистий JSON-об'єкт, без маркдауну та зайвого тексту.
"""
    headers = {"Content-Type": "application/json"}
    payload = {
        "model": GEMINI_MODEL,
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.2
    }

    try:
        response = requests.post(f"{GEMINI_API_BASE}/chat/completions", headers=headers, json=payload, timeout=30)
        if response.status_code == 200:
            result_json = response.json()
            content = result_json["choices"][0]["message"]["content"].strip()
            
            import re
            match = re.search(r'\{.*\}', content, re.DOTALL)
            if match:
                content = match.group(0)
            
            parsed = json.loads(content)
            return parsed.get("text", text)
        else:
            print(f"[Safety Warning] Ошибка Gemini API ({response.status_code}). Возвращен исходный текст.")
            return text
    except Exception as e:
        print(f"[Safety Exception] Ошибка при аудите контента: {e}. Возвращен исходный текст.")
        return text

def filter_and_correct_content(text):
    """
    Полный цикл проверки безопасности контента:
    1. Локальный аудит по стоп-словам.
    2. Семантический аудит через Gemini с автокоррекцией.
    """
    # 1. Локальный быстрый чек
    is_local_safe, reason = local_safety_check(text)
    if not is_local_safe:
        print(f"[Safety Audit] {reason}. Запуск автоматической коррекции через Gemini...")
    
    # 2. Семантический чек и автокоррекция
    corrected_text = gemini_compliance_audit(text)
    return corrected_text

def evaluate_image_with_vlm(image_path, text_context=""):
    """
    Использует мультимодальную модель Llama-3.2-11b-Vision через Nvidia NIM
    для оценки качества сгенерированного изображения. Возвращает структурированный вердикт.
    """
    import os
    import base64
    import json
    from config import NVIDIA_NIM_API_KEY
    
    if not os.path.exists(image_path):
        print(f"[VLM Safety] Файл изображения {image_path} не найден для оценки.")
        return {"is_perfect": True, "issue_detected": None, "bg_prompt_improvement": "", "criticism": "Файл не найден"}
        
    try:
        with open(image_path, "rb") as image_file:
            encoded_image = base64.b64encode(image_file.read()).decode('utf-8')
            
        headers = {
            "Authorization": f"Bearer {NVIDIA_NIM_API_KEY}",
            "Content-Type": "application/json"
        }
        
        prompt_text = (
            "Перед тобой сгенерированный пост-карточка для соцсети Threads (кириллица, украинский язык).\n"
            "Проанализируй изображение и верни строго JSON-объект с результатами анализа читаемости и дизайна.\n"
            "Будь ОБЪЕКТИВЕН и РЕАЛИСТИЧЕН: белый или очень светлый текст на темной или полупрозрачной темно-серой подложке панели является ИДЕАЛЬНО ЧИТАЕМЫМ. Не придирайся к мелочам. Ставь 'is_perfect': true во всех случаях, кроме критических дефектов (текст физически нечитаем, буквы накладываются друг на друга в кашу, или картинка сломана).\n"
            "Формат JSON:\n"
            "{\n"
            "  \"is_perfect\": true или false,\n"
            "  \"issue_detected\": \"описание проблемы на русском языке (коротко) или null\",\n"
            "  \"bg_prompt_improvement\": \"дополнительные ключевые слова на английском языке для улучшения промпта фона (например, 'darker gradients, vibrant lighting, smooth empty space'), чтобы решить проблему. НЕ ПРОСИ делать фон сплошным или полностью черным, фон должен оставаться высокодетализированным, кинематографичным и ярким!\",\n"
            "  \"criticism\": \"вердикт на русском языке (1-2 предложения)\"\n"
            "}\n"
            f"Контекст поста: '{text_context}'.\n"
            "Возвращай ТОЛЬКО чистый JSON-объект без маркдаун-разметки ```json."
        )
        
        payload = {
            "model": "meta/llama-3.2-11b-vision-instruct",
            "messages": [
                {
                    "role": "user",
                    "content": [
                        {"type": "text", "text": prompt_text},
                        {
                            "type": "image_url",
                            "image_url": {
                                "url": f"data:image/png;base64,{encoded_image}"
                            }
                        }
                    ]
                }
            ],
            "max_tokens": 350,
            "temperature": 0.1
        }
        
        print(f"[VLM Safety] Аудит изображения {os.path.basename(image_path)} через Llama-3.2-Vision...")
        response = requests.post("https://integrate.api.nvidia.com/v1/chat/completions", headers=headers, json=payload, timeout=40)
        
        if response.status_code == 200:
            res_data = response.json()
            content = res_data['choices'][0]['message']['content'].strip()
            
            # Очищаем markdown-теги json
            if content.startswith("```"):
                content = content.split("```")[1]
                if content.startswith("json"):
                    content = content[4:].strip()
                content = content.strip("`").strip()
                
            parsed = json.loads(content)
            print(f"\n=== [VLM ВЕРДИКТ ДЛЯ {os.path.basename(image_path)}] ===")
            print(f"Идеально: {parsed.get('is_perfect')}")
            print(f"Проблема: {parsed.get('issue_detected')}")
            print(f"Рекомендация: {parsed.get('bg_prompt_improvement')}")
            print(f"Критика: {parsed.get('criticism')}")
            print("==================================================\n")
            return parsed
        else:
            print(f"[VLM Safety Warning] Ошибка Nvidia NIM VLM API ({response.status_code}): {response.text}")
            return {"is_perfect": True, "issue_detected": None, "bg_prompt_improvement": "", "criticism": "Ошибка API"}
    except Exception as e:
        print(f"[VLM Safety Exception] Ошибка при парсинге вердикта VLM: {e}")
        return {"is_perfect": True, "issue_detected": None, "bg_prompt_improvement": "", "criticism": "Исключение при парсинге"}