124 lines
4.6 KiB
Python
124 lines
4.6 KiB
Python
"""
|
||
nlp.py — GigaChat NLP-анализ текста песни → {mood, scenes}
|
||
"""
|
||
|
||
import json
|
||
import os
|
||
import requests
|
||
from dotenv import load_dotenv
|
||
|
||
load_dotenv(os.path.expanduser("~/.openclaw/.env"))
|
||
|
||
|
||
def get_gigachat_token() -> str:
|
||
"""Получить access_token GigaChat через GigaChat API."""
|
||
base_url = os.environ.get("GIGACHAT_BASE_URL", "https://gigachat.devices.sberbank.ru")
|
||
# По ТЗ: через прокси 185.130.212.192:8443
|
||
creds = os.environ.get("GIGACHAT_CREDS", "")
|
||
|
||
token_url = os.environ.get("GIGACHAT_TOKEN_URL", f"{base_url}/api/v2/oauth")
|
||
headers = {
|
||
"Authorization": f"Basic {creds}",
|
||
"RqUID": "00000000-0000-0000-0000-000000000000",
|
||
"Content-Type": "application/x-www-form-urlencoded",
|
||
}
|
||
resp = requests.post(token_url, headers=headers, data={"scope": "GIGACHAT_API_PERS"},
|
||
verify=False, timeout=15)
|
||
resp.raise_for_status()
|
||
return resp.json()["access_token"]
|
||
|
||
|
||
def analyze(text: str) -> dict:
|
||
"""
|
||
Отправить текст в GigaChat, получить {mood, scenes}.
|
||
При ошибке возвращает fallback.
|
||
"""
|
||
try:
|
||
base_url = os.environ.get("GIGACHAT_BASE_URL", "https://gigachat.devices.sberbank.ru")
|
||
token = get_gigachat_token()
|
||
except Exception as e:
|
||
print(f"[nlp] GigaChat недоступен: {e}. Используем fallback.")
|
||
return _fallback(text)
|
||
|
||
prompt = (
|
||
"Определи настроение и 3-5 ключевых визуальных сцен для этой песни. "
|
||
"Ответь ТОЛЬКО JSON без обёрток:\n"
|
||
'{"mood": "строка", "scenes": ["сцена1", "сцена2", ...]}'
|
||
"\n\nТекст песни:\n" + text[:3000]
|
||
)
|
||
|
||
url = f"{base_url}/api/v1/chat/completions"
|
||
headers = {
|
||
"Authorization": f"Bearer {token}",
|
||
"Content-Type": "application/json",
|
||
}
|
||
body = {
|
||
"model": "GigaChat",
|
||
"messages": [{"role": "user", "content": prompt}],
|
||
"temperature": 0.3,
|
||
}
|
||
|
||
try:
|
||
resp = requests.post(url, headers=headers, json=body,
|
||
verify=False, timeout=30)
|
||
resp.raise_for_status()
|
||
data = resp.json()
|
||
content = data["choices"][0]["message"]["content"].strip()
|
||
|
||
# Парсим JSON из ответа — иногда модель возвращает markdown-обёртку
|
||
content = content.strip().strip("```json").strip("```").strip()
|
||
result = json.loads(content)
|
||
print(f"[nlp] GigaChat ответ: mood={result.get('mood')}, scenes={result.get('scenes')}")
|
||
return result
|
||
except Exception as e:
|
||
print(f"[nlp] Ошибка GigaChat API: {e}. Используем fallback.")
|
||
return _fallback(text)
|
||
|
||
|
||
def _fallback(text: str) -> dict:
|
||
"""Простой fallback без API."""
|
||
text_lower = text.lower()
|
||
|
||
mood_map = {
|
||
"love": "romantic", "любов": "romantic", "heart": "romantic",
|
||
"сердц": "romantic", "kiss": "romantic", "night": "moody",
|
||
"ноч": "moody", "dark": "moody", "темн": "moody",
|
||
"rain": "moody", "дожд": "moody", "sun": "happy",
|
||
"солнц": "happy", "свет": "happy", "light": "happy",
|
||
"party": "energetic", "танц": "energetic", "dance": "energetic",
|
||
"drive": "energetic", "драйв": "energetic",
|
||
"sad": "sad", "груст": "sad", "cry": "sad", "плач": "sad",
|
||
}
|
||
|
||
mood = "neutral"
|
||
for key, val in mood_map.items():
|
||
if key in text_lower:
|
||
mood = val
|
||
break
|
||
|
||
scene_map = {
|
||
"love": "romantic couple", "любов": "romantic sunset",
|
||
"night": "city night lights", "ноч": "starry sky",
|
||
"sun": "golden hour landscape", "солнц": "sunrise nature",
|
||
"rain": "rain window", "дожд": "rainy city",
|
||
"party": "party lights", "танц": "dance floor",
|
||
"sad": "solitary person", "груст": "lonely road",
|
||
"sea": "ocean waves", "мор": "ocean sunset",
|
||
"mountain": "mountain peaks", "гор": "mountain landscape",
|
||
"fire": "campfire", "огон": "firelight",
|
||
"snow": "snowy landscape", "снег": "winter forest",
|
||
"лес": "forest path", "forest": "forest path",
|
||
"road": "highway drive", "дорог": "open road",
|
||
}
|
||
|
||
scenes = ["abstract gradient"]
|
||
for key, val in scene_map.items():
|
||
if key in text_lower:
|
||
scenes.append(val)
|
||
|
||
scenes = scenes[:5]
|
||
if len(scenes) < 1:
|
||
scenes = ["abstract gradient"]
|
||
|
||
return {"mood": mood, "scenes": scenes}
|