File size: 5,239 Bytes
3885f96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import torch
from PIL import Image
from transformers import pipeline
from huggingface_hub import hf_hub_download
from llama_cpp import Llama
import io

# --- ГЛОБАЛЬНАЯ ЗАГРУЗКА МОДЕЛЕЙ (ВЫПОЛНЯЕТСЯ ОДИН РАЗ ПРИ СТАРТЕ ПРИЛОЖЕНИЯ) ---

print("Загрузка ML-моделей... Это может занять время.")
MODELS_LOADED = False

try:
    # Загрузка Модели 1 (VQA)
    # VQA-модель довольно легкая, ее можно оставить на CPU
    vqa_pipeline = pipeline(
        "visual-question-answering",
        model="Salesforce/blip-vqa-base",
        device="cpu"
    )

    # Загрузка Модели 2 (Gemma GGUF)
    model_name_gguf = "unsloth/gemma-3-4b-it-GGUF"
    model_file = "gemma-3-4b-it-Q3_K_M.gguf"
    model_path = hf_hub_download(repo_id=model_name_gguf, filename=model_file)
    
    # Инициализируем для работы на CPU
    gemma_gguf_model = Llama(
        model_path=model_path,
        n_ctx=2048,
        n_gpu_layers=0,
        verbose=False
    )

    MODELS_LOADED = True
    print("Все ML-модели успешно загружены!")

except Exception as e:
    print(f"КРИТИЧЕСКАЯ ОШИБКА при загрузке моделей: {e}")

# Список вопросов, которые мы будем задавать VQA-модели
QUESTIONS_TO_ASK = {
    "Материал стен": "What is the primary material of the exterior walls?",
    "Количество этажей": "How many floors does the house have?",
    "Цвет крыши": "What color is the roof?",
    "Архитектурный стиль": "What is the architectural style of the house?",
    "Гараж": "Is there a garage?",
    "Озеленение": "What does the landscaping look like?",
}

PROMPT_INSTRUCTIONS = {
    "brief": "Ты — копирайтер, который пишет короткие, яркие и продающие объявления для недвижимости (4-5 предложений).",
    "professional": "Ты — риелтор, который составляет детальное и структурированное объявление для листинга на сайте недвижимости. Опиши преимущества, используя профессиональный и деловой стиль.",
    "social": "Ты — SMM-менеджер, который пишет яркий и вовлекающий пост для социальных сетей. Используй эмодзи, добавь несколько релевантных хэштегов и обращайся к аудитории неформально."
}

# --- ГЛАВНАЯ ФУНКЦИЯ ПАЙПЛАЙНА ---

def generate_ad_from_image(image_bytes: bytes, style: str) -> dict:
    """
    Принимает изображение и стиль, анализирует и генерирует объявление.
    """
    if not MODELS_LOADED:
        raise RuntimeError("Модели не были загружены.")

    try:
        image = Image.open(io.BytesIO(image_bytes))
    except Exception as e:
        raise ValueError(f"Не удалось прочитать изображение: {e}")

    # 1. Сбор характеристик с помощью VQA
    print("Анализ изображения...")
    house_characteristics = {}
    for key, question in QUESTIONS_TO_ASK.items():
        answer = vqa_pipeline(image, question=question, top_k=1)
        characteristic = answer[0]['answer']
        house_characteristics[key] = characteristic

    # 2. Создание промпта для Gemma
    print(f"Создание промпта в стиле '{style}'...")
    prompt_details = ""
    for key, value in house_characteristics.items():
        prompt_details += f"- {key}: {value}\n"

    # Выбираем нужную инструкцию из словаря. Если стиль неизвестен, используем "brief".
    instruction = PROMPT_INSTRUCTIONS.get(style, PROMPT_INSTRUCTIONS["brief"])

    prompt = f"""<start_header_id>user<end_header_id>
{instruction}

Вот характеристики дома:
{prompt_details}
Напиши объявление.<end_header_id>
<start_header_id>model<end_header_id>
"""

    # 3. Генерация текста объявления
    print("Генерация объявления...")
    response = gemma_gguf_model(
        prompt,
        max_tokens=200,
        temperature=1.0,
        top_k=64,
        top_p=0.95,
        min_p=0.0,
        stop=["<end_header_id>"],
    )
    ad_text = response['choices'][0]['text'].strip()
    
    # 4. Постобработка текста
    if ad_text and not ad_text.endswith(('.', '!', '?')):
        last_sentence_end = max(ad_text.rfind('.'), ad_text.rfind('!'), ad_text.rfind('?'))
        if last_sentence_end != -1:
            ad_text = ad_text[:last_sentence_end + 1]

    print("Генерация завершена.")
    return {
        "characteristics": house_characteristics,
        "ad_text": ad_text
    }