b2aad43a44
Extensive per-track meta feeds DeepSeek planning. Caravan of the Night kept with electric guitar marked disliked. Sahara Saz remains gold standard. Gateway index.html auto-plays on tinqs.com. Co-authored-by: Cursor <cursoragent@cursor.com>
157 lines
4.9 KiB
Python
157 lines
4.9 KiB
Python
"""Lyria 3 capabilities — synced from Google Gemini API docs + live model list."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
|
||
from google import genai
|
||
from google.genai import errors as genai_errors
|
||
|
||
DOCS_URL = "https://ai.google.dev/gemini-api/docs/music-generation"
|
||
|
||
VOCAL_MODES = [
|
||
{
|
||
"id": "instrumental",
|
||
"label": "Instrumental only",
|
||
"hint": "Lyria tip: include 'Instrumental only, no vocals' in every prompt.",
|
||
},
|
||
{
|
||
"id": "mix",
|
||
"label": "Mix (textures + chants)",
|
||
"hint": "Wordless vocals, Sahel chants, whispers — no full lead lyrics unless chat asks.",
|
||
},
|
||
{
|
||
"id": "vocals",
|
||
"label": "Full vocals",
|
||
"hint": "Lead vocals and lyrics; language follows prompt or Lyria language setting.",
|
||
},
|
||
]
|
||
|
||
LANGUAGES = [
|
||
{"id": "auto", "label": "Auto (match prompt / taste)"},
|
||
{"id": "en", "label": "English"},
|
||
{"id": "hi", "label": "Hindi"},
|
||
{"id": "tr", "label": "Turkish"},
|
||
{"id": "fr", "label": "French"},
|
||
{"id": "ar", "label": "Arabic"},
|
||
{"id": "es", "label": "Spanish"},
|
||
{"id": "pt", "label": "Portuguese"},
|
||
{"id": "de", "label": "German"},
|
||
]
|
||
|
||
SINGER_PROFILES = [
|
||
{"id": "", "label": "Default (model picks)"},
|
||
{"id": "male_baritone", "label": "Male baritone — warm, desert blues"},
|
||
{"id": "male_tenor", "label": "Male tenor — bright, energetic"},
|
||
{"id": "female_alto", "label": "Female alto — smoky, soulful"},
|
||
{"id": "female_soprano", "label": "Female soprano — airy, soaring"},
|
||
{"id": "weathered_rocker", "label": "Weathered rocker — raspy 90s grit"},
|
||
]
|
||
|
||
OUTPUT_FORMATS = [
|
||
{"id": "mp3", "label": "MP3 (default)"},
|
||
{"id": "wav", "label": "WAV (Pro only, larger files)"},
|
||
]
|
||
|
||
STATIC_MODELS = [
|
||
{
|
||
"id": "lyria-3-pro-preview",
|
||
"label": "Lyria 3 Pro",
|
||
"duration": "1–2 minutes",
|
||
"cost_key": "lyria_pro_usd",
|
||
"supports_wav": True,
|
||
"supports_timestamps": True,
|
||
"supports_image_input": True,
|
||
},
|
||
{
|
||
"id": "lyria-3-clip-preview",
|
||
"label": "Lyria 3 Clip",
|
||
"duration": "30 seconds",
|
||
"cost_key": "lyria_clip_usd",
|
||
"supports_wav": False,
|
||
"supports_timestamps": False,
|
||
"supports_image_input": True,
|
||
},
|
||
]
|
||
|
||
|
||
@dataclass
|
||
class LyriaApiStatus:
|
||
ok: bool
|
||
message: str
|
||
models_available: list[str]
|
||
key_configured: bool
|
||
|
||
|
||
def static_capabilities() -> dict:
|
||
return {
|
||
"docs_url": DOCS_URL,
|
||
"models": STATIC_MODELS,
|
||
"vocal_modes": VOCAL_MODES,
|
||
"languages": LANGUAGES,
|
||
"singer_profiles": SINGER_PROFILES,
|
||
"output_formats": OUTPUT_FORMATS,
|
||
"features": [
|
||
"Text-to-music via generateContent",
|
||
"Vocals + timed lyrics (Pro)",
|
||
"Instrumental-only prompts",
|
||
"Custom lyrics with [Verse]/[Chorus] tags",
|
||
"Timestamp structure [0:00 - 0:30]",
|
||
"Prompt language steers lyric language",
|
||
"Image-to-music (multimodal)",
|
||
"44.1 kHz stereo MP3 or WAV (Pro)",
|
||
],
|
||
"prompt_tips": [
|
||
"Iterate with Clip (30s) before burning a Pro generation.",
|
||
"Be specific: BPM, key, instruments, mood, structure.",
|
||
"For instrumentals, always say 'Instrumental only, no vocals'.",
|
||
"For vocals, describe singer gender, timbre, and range.",
|
||
"Avoid 'vocal sample' / 'griot sample' — use 'Sahel blues chants'.",
|
||
],
|
||
}
|
||
|
||
|
||
def probe_lyria_api(api_key: str | None) -> dict:
|
||
if not api_key:
|
||
return LyriaApiStatus(
|
||
ok=False,
|
||
message="GEMINI_API_KEY not set",
|
||
models_available=[],
|
||
key_configured=False,
|
||
).__dict__
|
||
|
||
try:
|
||
client = genai.Client(api_key=api_key)
|
||
found: list[str] = []
|
||
for model in client.models.list():
|
||
name = (getattr(model, "name", "") or "").replace("models/", "")
|
||
if "lyria" in name.lower():
|
||
found.append(name)
|
||
if not found:
|
||
return LyriaApiStatus(
|
||
ok=False,
|
||
message="API key works but no Lyria models visible on this project",
|
||
models_available=[],
|
||
key_configured=True,
|
||
).__dict__
|
||
return LyriaApiStatus(
|
||
ok=True,
|
||
message=f"Lyria ready — {len(found)} model(s) available",
|
||
models_available=sorted(found),
|
||
key_configured=True,
|
||
).__dict__
|
||
except genai_errors.ClientError as exc:
|
||
return LyriaApiStatus(
|
||
ok=False,
|
||
message=f"Gemini API error: {exc}",
|
||
models_available=[],
|
||
key_configured=True,
|
||
).__dict__
|
||
except Exception as exc:
|
||
return LyriaApiStatus(
|
||
ok=False,
|
||
message=f"Probe failed: {exc}",
|
||
models_available=[],
|
||
key_configured=True,
|
||
).__dict__
|