EOL: Final release
This commit is contained in:
@@ -2,16 +2,16 @@ import os
|
||||
import json
|
||||
import re
|
||||
import requests
|
||||
from typing import List, Dict
|
||||
|
||||
class LLMAcousticBridge:
|
||||
def __init__(self, model_name="dolphin-llama3:8b"):
|
||||
def __init__(self, model_name: str = "dolphin-llama3:8b"):
|
||||
self.model_name = model_name
|
||||
base_url = os.getenv("OLLAMA_API_URL", "http://emom_ollama:11434")
|
||||
self.api_url = f"{base_url}/api/generate"
|
||||
|
||||
def get_acoustic_profile(self, valence, arousal, semantics):
|
||||
def get_acoustic_profile(self, valence: float, arousal: float, semantics: List[str]) -> Dict[str, float]:
|
||||
context_str = ", ".join(semantics) if semantics else "abstract scene"
|
||||
|
||||
prompt = f"""
|
||||
Analyze the visual context and emotions to determine the ideal background music properties.
|
||||
Emotions: Valence {valence:.1f}/9.0 (Positivity), Arousal {arousal:.1f}/9.0 (Energy).
|
||||
@@ -34,32 +34,42 @@ class LLMAcousticBridge:
|
||||
"model": self.model_name,
|
||||
"prompt": prompt,
|
||||
"stream": False,
|
||||
"format": "json" # Принудительный JSON-режим Ollama
|
||||
"format": "json",
|
||||
"options": {
|
||||
"temperature": 0.7,
|
||||
"top_p": 0.9
|
||||
}
|
||||
}
|
||||
|
||||
print(f"Запрос акустического профиля к Ollama...")
|
||||
response = requests.post(self.api_url, json=payload, timeout=120)
|
||||
response.raise_for_status()
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
response_text = data.get("response", "")
|
||||
|
||||
profile = {}
|
||||
try:
|
||||
# 1. Попытка прямой десериализации
|
||||
profile = json.loads(response_text)
|
||||
return profile
|
||||
except json.JSONDecodeError:
|
||||
# 2. Аварийное извлечение JSON из текста с помощью регулярного выражения
|
||||
match = re.search(r'\{.*\}', response_text, re.DOTALL)
|
||||
if match:
|
||||
return json.loads(match.group(0))
|
||||
|
||||
print(f"Ошибка парсинга LLM ответа: {response_text}")
|
||||
return {}
|
||||
profile = json.loads(match.group(0))
|
||||
else:
|
||||
print(f"Ollama вернула ошибку HTTP: {response.status_code}")
|
||||
print(f"[ERROR] Ошибка парсинга LLM ответа: {response_text}")
|
||||
return {}
|
||||
|
||||
# 3. Жесткая валидация ключей (чтобы matcher.py не упал на демо)
|
||||
required_keys = ['energy', 'flux', 'centroid', 'pitch', 'hnr', 'zcr']
|
||||
if profile and all(k in profile for k in required_keys):
|
||||
return profile
|
||||
else:
|
||||
print(f"[WARN] LLM вернула неполный JSON, активирован fallback: {profile}")
|
||||
return {}
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"[ERROR] Ошибка соединения с Ollama: {str(e)}")
|
||||
return {}
|
||||
except Exception as e:
|
||||
print(f"Ошибка соединения с Ollama: {str(e)}")
|
||||
print(f"[ERROR] Внутренняя ошибка семантического моста: {str(e)}")
|
||||
return {}
|
||||
@@ -78,6 +78,6 @@ class MusicMatcher:
|
||||
self.norm_db['acoustic_distance'] = acoustic_penalty / len(self.acoustic_features)
|
||||
|
||||
# Вычисление интегральной метрики соответствия (мультимодальный скоринг)
|
||||
self.norm_db['final_score'] = self.norm_db['emo_distance'] + (self.norm_db['acoustic_distance'] * 4.0)
|
||||
self.norm_db['final_score'] = self.norm_db['emo_distance'] + (self.norm_db['acoustic_distance'] * 2.0)
|
||||
|
||||
return self.norm_db.sort_values(by='final_score').head(top_k)
|
||||
@@ -264,9 +264,9 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (thesis)",
|
||||
"display_name": "Python (my-python-project)",
|
||||
"language": "python",
|
||||
"name": "thesis"
|
||||
"name": "my-python-project"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
||||
Reference in New Issue
Block a user