AndreyUniverse / app.py
root39058's picture
Update app.py
3c89192 verified
Raw
History Blame Contribute Delete
32.8 kB
import gradio as gr
import torch
import torch.nn as nn
import numpy as np
import spaces
import random
import os
import requests
from PIL import Image, ImageDraw
import io
import tempfile
import base64
import pretty_midi
from huggingface_hub import hf_hub_download
import subprocess
import wave
import struct
import time
# ============================================================
# 0. ЗАГРУЗКА ФАЙЛОВ ИЗ БАКЕТА
# ============================================================
def download_file(url, local_path):
if os.path.exists(local_path):
print(f"[INFO] Файл уже есть: {local_path}")
return local_path
print(f"[INFO] Скачиваю {url}")
response = requests.get(url, stream=True)
response.raise_for_status()
with open(local_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print(f"[INFO] Скачано: {local_path}")
return local_path
VAE_URL = "https://huggingface.co/buckets/root39058/shhh/resolve/andrey_dreams.pt?download=true"
VIDEO_URL = "https://huggingface.co/buckets/root39058/shhh/resolve/andrey_videos.pt?download=true"
MUSIC_URL = "https://huggingface.co/buckets/root39058/shhh/resolve/andrey_music.pt?download=true"
VAE_PATH = download_file(VAE_URL, "andrey_dreams.pt")
VIDEO_PATH = download_file(VIDEO_URL, "andrey_videos.pt")
MUSIC_PATH = download_file(MUSIC_URL, "andrey_music.pt")
CHAT_MODEL = hf_hub_download(
repo_id="root39058/AndreyBot",
filename="pytorch_model.bin",
local_dir="./andrey_model"
)
WORLD_MODEL = hf_hub_download(repo_id="root39058/AndreyWorld", filename="andrey_world_model.pt")
print("[INFO] Все модели загружены")
# ============================================================
# 1. УСТРОЙСТВО
# ============================================================
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"[INFO] Устройство: {device}")
# ============================================================
# 2. ТЕКСТОВАЯ МОДЕЛЬ ДЛЯ ОПИСАНИЙ
# ============================================================
class TextModel(nn.Module):
def __init__(self, vocab_size=50, embed_dim=64, hidden_dim=128):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, vocab_size)
def forward(self, x):
x = self.embedding(x)
x, _ = self.lstm(x)
x = self.fc(x)
return x
def load_text_model():
checkpoint = torch.load(VAE_PATH, map_location='cpu')
vocab = checkpoint['vocab']
word_to_idx = checkpoint['word_to_idx']
idx_to_word = checkpoint['idx_to_word']
model = TextModel(vocab_size=len(vocab), embed_dim=64, hidden_dim=128)
model.load_state_dict(checkpoint['text_state'])
model.eval()
return model, vocab, word_to_idx, idx_to_word
text_model, text_vocab, text_w2i, text_i2w = load_text_model()
text_model = text_model.to(device)
print("[INFO] Текстовая модель загружена")
def generate_description(prompt, max_len=15):
text_model.eval()
words = prompt.lower().split()
ids = [text_w2i.get(w, 1) for w in words]
if len(ids) < 5:
ids = ids + [0] * (5 - len(ids))
inp = torch.tensor([ids[:5]], dtype=torch.long).to(device)
generated = []
current = inp
with torch.no_grad():
for _ in range(max_len):
logits = text_model(current)
probs = torch.softmax(logits[0, -1, :] / 0.8, dim=-1)
probs[0] = 0
probs[1] = 0
probs = probs / probs.sum()
idx = torch.multinomial(probs, 1).item()
if idx == text_w2i.get('', 2):
break
generated.append(idx)
current = torch.cat([current, torch.tensor([[idx]]).to(device)], dim=1)
if len(generated) >= 12:
break
words = [text_i2w.get(i, '?') for i in generated]
return ' '.join(words)
# ============================================================
# 3. ЧАТ-МОДЕЛЬ
# ============================================================
class AndreyBot(nn.Module):
def __init__(self, vocab_size=155, hidden_size=512, num_layers=3, embedding_dim=128):
super().__init__()
self.vocab_size = vocab_size
self.hidden_size = hidden_size
self.num_layers = num_layers
self.embedding_dim = embedding_dim
self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=0)
self.lstm = nn.LSTM(embedding_dim, hidden_size, batch_first=True, num_layers=num_layers, dropout=0.2)
self.fc = nn.Linear(hidden_size, vocab_size)
self.dropout = nn.Dropout(0.2)
def forward(self, x):
if x.dim() == 1:
x = x.unsqueeze(0)
emb = self.embedding(x)
out, _ = self.lstm(emb)
out = self.dropout(out)
out = out[:, -1, :]
return self.fc(out)
def load_chat_model():
checkpoint = torch.load(CHAT_MODEL, map_location='cpu')
vocab_size = checkpoint.get('vocab_size', 155)
hidden_size = checkpoint.get('hidden_size', 512)
num_layers = checkpoint.get('num_layers', 3)
embedding_dim = checkpoint.get('embedding_dim', 128)
model = AndreyBot(
vocab_size=vocab_size,
hidden_size=hidden_size,
num_layers=num_layers,
embedding_dim=embedding_dim
)
if 'model_state' in checkpoint:
model.load_state_dict(checkpoint['model_state'])
elif 'model_state_dict' in checkpoint:
model.load_state_dict(checkpoint['model_state_dict'])
else:
model.load_state_dict(checkpoint)
model.eval()
word_to_idx = checkpoint.get('word_to_idx', {})
idx_to_word = {int(k): v for k, v in checkpoint.get('idx_to_word', {}).items()}
return model, word_to_idx, idx_to_word
chat_model, word_to_idx, idx_to_word = load_chat_model()
chat_model = chat_model.to(device)
print(f"[INFO] Чат-модель загружена: {len(word_to_idx)} слов, 512 нейронов")
PAD = 0
UNK = 1
def tokenize(text):
words = text.lower().split()
return [word_to_idx.get(w, UNK) for w in words if w in word_to_idx]
def detokenize(tokens):
words = []
for t in tokens:
if t not in [PAD, UNK]:
words.append(idx_to_word.get(t, '?'))
return ' '.join(words)
@spaces.GPU(duration=20)
def chat_response(message, history):
"""Генератор: выдаёт ответ по одному токену с задержкой"""
chat_model.eval()
tokens = tokenize(message)
if not tokens:
yield "..."
return
current = tokens[0]
response_tokens = []
partial_text = ""
with torch.no_grad():
for step in range(15):
logits = chat_model(torch.tensor([[current]]).to(device))
probs = torch.softmax(logits[0] / 0.85, dim=-1)
top_k = min(8, len(word_to_idx))
top_probs, top_idx = torch.topk(probs, top_k)
probs = top_probs / top_probs.sum()
current = top_idx[torch.multinomial(probs, 1)].item()
if current in [PAD, UNK]:
continue
response_tokens.append(current)
word = idx_to_word.get(current, '')
if word:
partial_text += word + " "
time.sleep(0.08)
yield partial_text.strip()
if len(response_tokens) >= 10:
break
if not partial_text.strip():
yield "..."
# ============================================================
# 4. МОДЕЛЬ МИРОВ
# ============================================================
class AndreyWorldMLP(nn.Module):
def __init__(self, vocab_size=38, hidden_size=128):
super().__init__()
self.vocab_size = vocab_size
self.fc1 = nn.Linear(vocab_size, hidden_size)
self.fc2 = nn.Linear(hidden_size, hidden_size)
self.fc3 = nn.Linear(hidden_size, hidden_size // 2)
self.fc4 = nn.Linear(hidden_size // 2, hidden_size // 4)
self.fc5 = nn.Linear(hidden_size // 4, vocab_size)
self.dropout = nn.Dropout(0.2)
self.relu = nn.ReLU()
def forward(self, x):
if x.dim() == 0:
x = x.unsqueeze(0)
if x.dim() == 1:
x = x.unsqueeze(0)
x = torch.nn.functional.one_hot(x, num_classes=self.vocab_size).float()
x = self.relu(self.fc1(x))
x = self.dropout(x)
x = self.relu(self.fc2(x))
x = self.dropout(x)
x = self.relu(self.fc3(x))
x = self.dropout(x)
x = self.relu(self.fc4(x))
x = self.fc5(x)
return x
def load_world_model():
checkpoint = torch.load(WORLD_MODEL, map_location='cpu')
vocab_size = checkpoint['vocab_size']
word_to_idx = checkpoint['word_to_idx']
idx_to_word = {int(k): v for k, v in checkpoint['idx_to_word'].items()}
model_config = checkpoint.get('model_config', {})
hidden_size = model_config.get('hidden_size', 128)
model = AndreyWorldMLP(vocab_size=vocab_size, hidden_size=hidden_size)
model.load_state_dict(checkpoint['model_state'])
model.eval()
return model, word_to_idx, idx_to_word
world_model, world_w2i, world_i2w = load_world_model()
world_model = world_model.to(device)
print("[INFO] Модель миров загружена")
def tokenize_world(text):
words = text.lower().split()
return [world_w2i.get(w, 1) for w in words if w in world_w2i]
def detokenize_world(tokens):
words = []
for t in tokens:
if t not in [0, 1]:
words.append(world_i2w.get(t, '?'))
return ' '.join(words)
def generate_world(prompt, temperature=0.85):
world_model.eval()
tokens = tokenize_world(prompt)
if not tokens:
return "..."
current = tokens[0]
response_tokens = []
with torch.no_grad():
for _ in range(20):
logits = world_model(torch.tensor([[current]]).to(device))
probs = torch.softmax(logits[0] / temperature, dim=-1)
top_k = min(8, len(world_w2i))
top_probs, top_idx = torch.topk(probs, top_k)
probs = top_probs / top_probs.sum()
current = top_idx[torch.multinomial(probs, 1)].item()
if current in [0, 1]:
continue
response_tokens.append(current)
if len(response_tokens) >= 15:
break
if not response_tokens:
return "..."
return detokenize_world(response_tokens)
def draw_world_image(text):
img = Image.new('RGB', (512, 256), color=(10, 10, 30))
draw = ImageDraw.Draw(img)
draw.rectangle([(0, 180), (512, 256)], fill=(40, 60, 30))
for y in range(180):
r = 10 + y // 5
g = 10 + y // 8
b = 30 + y // 4
draw.line([(0, y), (512, y)], fill=(r, g, b))
for i in range(4):
x = i * 130 + 20
h = random.randint(60, 120)
draw.polygon([(x, 180), (x + 60, 180 - h), (x + 120, 180)], fill=(80, 70, 60))
draw.ellipse([(400, 30), (470, 100)], fill=(255, 200, 80))
for i in range(8):
x = random.randint(20, 490)
draw.rectangle([(x, 180 - random.randint(20, 40)), (x + 6, 180)], fill=(50, 30, 20))
draw.ellipse([(x - 12, 180 - random.randint(30, 50)), (x + 18, 180 - random.randint(10, 30))], fill=(30, 120, 30))
draw.text((20, 20), text[:50], fill=(255, 255, 255))
return img
# ============================================================
# 5. ПОЛНАЯ VAE
# ============================================================
class FullVAE(nn.Module):
def __init__(self, latent_dim=64, img_size=128):
super().__init__()
self.latent_dim = latent_dim
self.img_size = img_size
self.encoder = nn.Sequential(
nn.Conv2d(3, 32, 4, 2, 1),
nn.BatchNorm2d(32),
nn.LeakyReLU(0.2),
nn.Conv2d(32, 64, 4, 2, 1),
nn.BatchNorm2d(64),
nn.LeakyReLU(0.2),
nn.Conv2d(64, 128, 4, 2, 1),
nn.BatchNorm2d(128),
nn.LeakyReLU(0.2),
nn.Conv2d(128, 256, 4, 2, 1),
nn.BatchNorm2d(256),
nn.LeakyReLU(0.2),
nn.Flatten(),
)
self.fc_mu = nn.Linear(256 * 8 * 8, latent_dim)
self.fc_logvar = nn.Linear(256 * 8 * 8, latent_dim)
self.decoder_input = nn.Linear(latent_dim, 256 * 8 * 8)
self.decoder = nn.Sequential(
nn.ConvTranspose2d(256, 128, 4, 2, 1),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.ConvTranspose2d(128, 64, 4, 2, 1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.ConvTranspose2d(64, 32, 4, 2, 1),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.ConvTranspose2d(32, 3, 4, 2, 1),
nn.Sigmoid(),
)
def decode(self, z):
h = self.decoder_input(z)
h = h.view(-1, 256, 8, 8)
return self.decoder(h)
def load_vae():
checkpoint = torch.load(VAE_PATH, map_location='cpu')
model = FullVAE(latent_dim=64, img_size=128)
model.load_state_dict(checkpoint['vae_state'])
model.eval()
return model
vae_model = load_vae().to(device)
print("[INFO] VAE загружена")
@spaces.GPU(duration=15)
def generate_dream():
with torch.no_grad():
z = torch.randn(1, 64).to(device)
img = vae_model.decode(z)
img = img.squeeze().cpu().numpy()
img = np.transpose(img, (1, 2, 0))
img = np.clip(img, 0, 1) * 255
pil_img = Image.fromarray(img.astype(np.uint8))
pil_img = pil_img.resize((512, 512), Image.Resampling.LANCZOS)
dream_types = ['космос', 'лес', 'океан', 'город', 'подводный мир', 'пустыня', 'горы', 'ночь']
dream_type = random.choice(dream_types)
description = generate_description(f"сон про {dream_type}")
return pil_img, description
# ============================================================
# 6. ВИДЕО (GIF)
# ============================================================
def generate_video_frame(z):
with torch.no_grad():
img = vae_model.decode(z)
img = img.squeeze().cpu().numpy()
img = np.transpose(img, (1, 2, 0))
img = np.clip(img, 0, 1) * 255
pil_img = Image.fromarray(img.astype(np.uint8))
return pil_img.resize((512, 512), Image.Resampling.LANCZOS)
def slerp(z1, z2, t):
return (1 - t) * z1 + t * z2
@spaces.GPU(duration=45)
def generate_gif(duration=5, fps=10):
total_frames = duration * fps
z1 = torch.randn(1, 64).to(device)
z2 = torch.randn(1, 64).to(device)
frames = []
frames_per_segment = 20
for i in range(total_frames):
t = (i % frames_per_segment) / frames_per_segment
if i % frames_per_segment == 0:
z1 = z2
z2 = torch.randn(1, 64).to(device)
z = slerp(z1, z2, t)
frames.append(generate_video_frame(z))
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.gif')
temp_path = temp_file.name
temp_file.close()
frames[0].save(temp_path, save_all=True, append_images=frames[1:],
duration=1000//fps, loop=0, optimize=True)
dream_types = ['космос', 'лес', 'океан', 'город', 'подводный мир', 'пустыня', 'горы', 'ночь']
dream_type = random.choice(dream_types)
video_description = generate_description(f"видео сон про {dream_type}")
return temp_path, video_description
# ============================================================
# 7. УЛУЧШЕННАЯ МУЗЫКАЛЬНАЯ МОДЕЛЬ С НОВЫМИ ФУНКЦИЯМИ
# ============================================================
class MusicGenerator(nn.Module):
def __init__(self, vocab_size=128, embed_dim=64, hidden_dim=128, num_layers=2):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
self.lstm = nn.LSTM(embed_dim, hidden_dim, num_layers, batch_first=True, dropout=0.2)
self.fc = nn.Linear(hidden_dim, vocab_size)
def forward(self, x, hidden=None):
x = self.embedding(x)
x, hidden = self.lstm(x, hidden)
x = self.fc(x)
return x, hidden
class MusicTokenizer:
def __init__(self):
self.notes = list(range(36, 85))
self.durations = [0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, 4.0]
self.vocab = ['', '', '', '']
for note in self.notes:
for dur in self.durations:
self.vocab.append(f'{note}_{dur}')
self.word_to_idx = {w: i for i, w in enumerate(self.vocab)}
self.idx_to_word = {i: w for w, i in self.word_to_idx.items()}
self.vocab_size = len(self.vocab)
self.max_len = 50
def tokenize(self, notes, durations):
tokens = []
for n, d in zip(notes, durations):
if n in self.notes and d in self.durations:
tokens.append(f'{n}_{d}')
return [self.word_to_idx.get(t, 1) for t in tokens]
def detokenize(self, ids):
notes = []
durations = []
for i in ids:
if i in [0, 1, 2, 3]:
continue
token = self.idx_to_word.get(i, '')
if '_' in token:
try:
n, d = token.split('_')
notes.append(int(n))
durations.append(float(d))
except:
continue
return notes, durations
def load_music_model():
checkpoint = torch.load(MUSIC_PATH, map_location='cpu')
tokenizer = MusicTokenizer()
model = MusicGenerator(
vocab_size=tokenizer.vocab_size,
embed_dim=64,
hidden_dim=128,
num_layers=2
)
if 'model_state' in checkpoint:
try:
model.load_state_dict(checkpoint['model_state'])
except:
print("[WARNING] Не удалось загрузить веса модели, используем случайные")
elif 'model_state_dict' in checkpoint:
try:
model.load_state_dict(checkpoint['model_state_dict'])
except:
print("[WARNING] Не удалось загрузить веса модели, используем случайные")
model.eval()
return model, tokenizer
music_model, music_tokenizer = load_music_model()
music_model = music_model.to(device)
print("[INFO] Музыкальная модель загружена")
# Словарь стилей музыки
MUSIC_STYLES = {
'Классика': {'tempo': 100, 'program': 0, 'note_range': (48, 72)},
'Джаз': {'tempo': 130, 'program': 6, 'note_range': (40, 80)},
'Электроника': {'tempo': 140, 'program': 88, 'note_range': (36, 84)},
'Рок': {'tempo': 160, 'program': 29, 'note_range': (40, 76)},
'Акустика': {'tempo': 110, 'program': 25, 'note_range': (45, 75)},
'Фортепиано': {'tempo': 120, 'program': 0, 'note_range': (36, 84)},
}
def generate_melody_with_style(length=30, temperature=0.9, style='Классика'):
"""Генерация мелодии в определённом стиле"""
music_model.eval()
style_config = MUSIC_STYLES.get(style, MUSIC_STYLES['Классика'])
note_min, note_max = style_config['note_range']
start_token = music_tokenizer.word_to_idx['']
inp = torch.tensor([[start_token]], dtype=torch.long).to(device)
generated = []
hidden = None
last_notes = []
with torch.no_grad():
for step in range(length):
logits, hidden = music_model(inp, hidden)
probs = torch.softmax(logits[0, -1, :] / temperature, dim=-1)
probs[0] = 0 # PAD
probs[1] = 0 # UNK
probs[2] = 0 # EOS
probs[3] = 0 # START
# Ограничиваем диапазон нот для стиля
for i in range(len(probs)):
token = music_tokenizer.idx_to_word.get(i, '')
if '_' in token:
try:
note = int(token.split('_')[0])
if note < note_min or note > note_max:
probs[i] = 0
except:
pass
if probs.sum() == 0:
break
probs = probs / probs.sum()
top_k = min(20, len(music_tokenizer.vocab))
top_probs, top_idx = torch.topk(probs, top_k)
noise = torch.randn_like(top_probs) * 0.05
top_probs = top_probs + noise
top_probs = torch.clamp(top_probs, min=0)
top_probs = top_probs / top_probs.sum()
idx = top_idx[torch.multinomial(top_probs, 1)].item()
token = music_tokenizer.idx_to_word.get(idx, '')
if '_' in token:
note = token.split('_')[0]
if len(last_notes) >= 3 and all(n == note for n in last_notes[-3:]):
temp_idx = top_idx[torch.multinomial(top_probs / 1.5, 1)].item()
if temp_idx != idx:
idx = temp_idx
token = music_tokenizer.idx_to_word.get(idx, '')
if '_' in token:
note = token.split('_')[0]
last_notes.append(note)
if len(last_notes) > 5:
last_notes.pop(0)
generated.append(idx)
inp = torch.cat([inp, torch.tensor([[idx]]).to(device)], dim=1)
if len(generated) >= length:
break
if not generated:
return generate_random_melody(length, note_min, note_max)
notes, durations = music_tokenizer.detokenize(generated)
if len(notes) < 5:
extra_notes, extra_durs = generate_random_melody(length - len(notes), note_min, note_max)
notes.extend(extra_notes)
durations.extend(extra_durs)
return notes, durations
def generate_random_melody(length=20, note_min=48, note_max=72):
"""Генерация случайной мелодии"""
notes = []
durations = []
for _ in range(length):
note = random.randint(note_min, note_max)
dur = random.choice([0.25, 0.5, 0.75, 1.0, 1.5, 2.0])
notes.append(note)
durations.append(dur)
return notes, durations
def create_chord_progression(notes, durations, chord_style='simple'):
"""Добавляет аккорды к мелодии"""
if len(notes) < 4:
return notes, durations
chord_notes = []
chord_durations = []
chords = {
'C': [0, 4, 7],
'D': [2, 6, 9],
'E': [4, 8, 11],
'F': [5, 9, 0],
'G': [7, 11, 2],
'A': [9, 1, 4],
'B': [11, 3, 6]
}
progression = ['C', 'G', 'Am', 'F']
chord_index = 0
for i, (note, dur) in enumerate(zip(notes, durations)):
chord_notes.append(note)
chord_durations.append(dur)
if i % 4 == 0 and i > 0:
chord_index = (chord_index + 1) % len(progression)
chord_name = progression[chord_index]
if chord_name in chords:
base_note = chords[chord_name][0] + 48 # C4
for j, interval in enumerate(chords[chord_name]):
chord_note = base_note + interval
if 36 <= chord_note <= 84:
chord_notes.append(chord_note)
chord_durations.append(dur * 0.5)
return chord_notes, chord_durations
def notes_to_midi_bytes(notes, durations, tempo=120, program=0, add_chords=True):
"""Создание MIDI с возможностью добавления аккордов"""
midi = pretty_midi.PrettyMIDI(initial_tempo=tempo)
instrument = pretty_midi.Instrument(program=program)
time = 0.0
for note, dur in zip(notes, durations):
if note < 21 or note > 108:
continue
note_obj = pretty_midi.Note(
velocity=random.randint(70, 100),
pitch=int(note),
start=time,
end=time + dur
)
instrument.notes.append(note_obj)
time += dur
midi.instruments.append(instrument)
if add_chords and len(notes) > 8:
chord_instrument = pretty_midi.Instrument(program=program + 1)
chord_notes, chord_durations = create_chord_progression(notes, durations)
time = 0.0
for i, (note, dur) in enumerate(zip(chord_notes, chord_durations)):
if i % 2 == 0:
continue
if note < 21 or note > 108:
continue
note_obj = pretty_midi.Note(
velocity=random.randint(50, 70),
pitch=int(note),
start=time,
end=time + dur * 0.7
)
chord_instrument.notes.append(note_obj)
time += dur
midi.instruments.append(chord_instrument)
buffer = io.BytesIO()
midi.write(buffer)
buffer.seek(0)
return buffer.getvalue()
# ============================================================
# 8. GRADIO ИНТЕРФЕЙС
# ============================================================
with gr.Blocks(title="🌌 Андрей — Целостный ИИ") as demo:
gr.Markdown("""
# 🌌 Андрей — Целостный ИИ
**Чат • Миры • Сны • Видео • Музыка — всё в одном месте**
🤖 **Новый Андрей:** 22 МБ, 512 нейронов, 3 слоя, 155 слов
🎵 **Музыка:** 6 стилей + аккорды
""")
with gr.Tabs():
# ===== ВКЛАДКА 1: ЧАТ =====
with gr.TabItem("💬 Чат"):
gr.Markdown("### Поговори с Андреем (512 нейронов)")
chatbot = gr.ChatInterface(
fn=chat_response,
title="Андрей",
description="Тихий друг с 512 нейронами — текст появляется по токенам"
)
# ===== ВКЛАДКА 2: МИРЫ =====
with gr.TabItem("🌍 Миры"):
gr.Markdown("### Андрей создаёт миры")
with gr.Row():
with gr.Column(scale=2):
world_img = gr.Image(label="Мир", type="pil")
world_desc = gr.Textbox(label="📖 Описание мира", lines=3)
with gr.Column(scale=1):
world_prompt = gr.Textbox(label="Что за мир?", placeholder="Например: лесной мир")
world_btn = gr.Button("🌀 Создать мир", variant="primary")
@spaces.GPU(duration=10)
def create_world(prompt):
if not prompt.strip():
prompt = "тихий лесной мир"
text = generate_world(prompt)
img = draw_world_image(text)
return img, text
world_btn.click(
fn=create_world,
inputs=[world_prompt],
outputs=[world_img, world_desc]
)
# ===== ВКЛАДКА 3: СНЫ =====
with gr.TabItem("🌙 Сны"):
gr.Markdown("### Андрей видит сны")
with gr.Row():
with gr.Column(scale=2):
dream_img = gr.Image(label="Сон", type="pil")
dream_desc = gr.Textbox(label="📖 Описание сна", lines=3)
with gr.Column(scale=1):
dream_btn = gr.Button("🌀 Новый сон + описание", variant="primary")
dream_btn.click(
fn=generate_dream,
outputs=[dream_img, dream_desc]
)
# ===== ВКЛАДКА 4: ВИДЕО =====
with gr.TabItem("🎬 Видео"):
gr.Markdown("### GIF-видео сна (5 секунд)")
with gr.Row():
with gr.Column(scale=2):
video_output = gr.Image(label="GIF-сон", type="filepath")
video_desc = gr.Textbox(label="📖 Описание видео", lines=3)
with gr.Column(scale=1):
video_btn = gr.Button("🌀 Сгенерировать GIF", variant="primary")
video_btn.click(
fn=generate_gif,
outputs=[video_output, video_desc]
)
# ===== ВКЛАДКА 5: МУЗЫКА (ОБНОВЛЁННАЯ) =====
with gr.TabItem("🎵 Музыка"):
gr.Markdown("### Андрей сочиняет мелодии с аккордами и стилями")
with gr.Row():
with gr.Column(scale=2):
music_output = gr.File(label="🎵 MIDI-файл", file_types=[".mid"])
music_info = gr.Textbox(label="📖 Описание", lines=3)
music_style_display = gr.Textbox(label="🎼 Стиль", lines=1)
with gr.Column(scale=1):
music_style = gr.Dropdown(
choices=list(MUSIC_STYLES.keys()),
value='Классика',
label="🎹 Стиль музыки"
)
music_length = gr.Slider(
minimum=10,
maximum=80,
value=40,
step=1,
label="Длина мелодии (нот)"
)
temperature_slider = gr.Slider(
minimum=0.5,
maximum=1.5,
value=0.9,
step=0.05,
label="🌡️ Температура (разнообразие)"
)
add_chords = gr.Checkbox(
label="🎶 Добавить аккорды",
value=True
)
music_btn = gr.Button("🌀 Создать мелодию", variant="primary", size="lg")
@spaces.GPU(duration=15)
def generate_music_with_style(length, temperature, style, add_chords_flag):
notes, durations = generate_melody_with_style(
length=length,
temperature=temperature,
style=style
)
if not notes:
return None, "❌ Не удалось сгенерировать мелодию", ""
style_config = MUSIC_STYLES.get(style, MUSIC_STYLES['Классика'])
midi_bytes = notes_to_midi_bytes(
notes,
durations,
tempo=style_config['tempo'],
program=style_config['program'],
add_chords=add_chords_flag
)
temp = tempfile.NamedTemporaryFile(delete=False, suffix='.mid')
temp.write(midi_bytes)
temp.close()
desc = generate_description(f"{style} мелодия из {len(notes)} нот")
chord_info = " + аккорды" if add_chords_flag and len(notes) > 8 else ""
return temp.name, f"🎵 {len(notes)} нот • {desc}{chord_info}", f"{style}{style_config['tempo']} BPM"
music_btn.click(
fn=generate_music_with_style,
inputs=[music_length, temperature_slider, music_style, add_chords],
outputs=[music_output, music_info, music_style_display]
)
gr.Markdown("""
### 🎼 Примеры стилей:
\- **Классика** — медленные, плавные мелодии
\- **Джаз** — свинговые, с характерными гармониями
\- **Электроника** — быстрые, повторяющиеся паттерны
\- **Рок** — энергичные, с сильным ритмом
\- **Акустика** — тёплые, натуральные звуки
\- **Фортепиано** — классическое звучание
""")
if __name__ == "__main__":
demo.launch(share=True, theme=gr.themes.Soft())