Model selection (ai.py): - get_optimal_model() now scores Gemini 2.5 > 2.0 > 1.5 when ranking candidates - get_default_models() fallbacks updated to gemini-2.0-pro-exp (logic) and gemini-2.0-flash (writer/artist) - AI selection prompt rewritten: includes Gemini 2.x pricing context, guidance to avoid 'thinking' models for writer/artist roles, and instructions to prefer 2.x over 1.5 - Added image_model_name and image_model_source globals for UI visibility - init_models() now reads MODEL_IMAGE_HINT; tries imagen-3.0-generate-001 then imagen-3.0-fast-generate-001 on both Gemini API and Vertex AI paths Cover generation (marketing.py): - Fixed display bug: "Attempt X/5" now correctly reads "Attempt X/3" - Added imagen-3.0-fast-generate-001 as intermediate fallback before legacy Imagen 2 - Quality threshold: images with score < 5 are only kept if nothing better exists - Smarter prompt refinement on retry: deformity, blur, and watermark critique keywords each append targeted corrections to the art prompt - Fixed missing sys import (sys.platform check for macOS was silently broken) Config / Docker: - config.py: added MODEL_IMAGE_HINT env var, bumped version to 1.2.0 - docker-compose.yml: added MODEL_IMAGE environment variable - Dockerfile: added libpng-dev and libfreetype6-dev for better font/PNG rendering; added HEALTHCHECK so Portainer detects unhealthy containers System status UI: - system_status.html: added Image row showing active Imagen model and provider (Gemini API / Vertex AI) - Added cache expiry countdown with colour-coded badges Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
68 lines
2.9 KiB
Python
68 lines
2.9 KiB
Python
import os
|
|
from dotenv import load_dotenv
|
|
|
|
# Ensure .env is loaded from the script's directory (VS Code fix)
|
|
load_dotenv(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"))
|
|
|
|
def get_clean_env(key, default=None):
|
|
val = os.getenv(key, default)
|
|
return val.strip() if val else None
|
|
|
|
API_KEY = get_clean_env("GEMINI_API_KEY")
|
|
GCP_PROJECT = get_clean_env("GCP_PROJECT")
|
|
GCP_LOCATION = get_clean_env("GCP_LOCATION", "us-central1")
|
|
MODEL_LOGIC_HINT = get_clean_env("MODEL_LOGIC", "AUTO")
|
|
MODEL_WRITER_HINT = get_clean_env("MODEL_WRITER", "AUTO")
|
|
MODEL_ARTIST_HINT = get_clean_env("MODEL_ARTIST", "AUTO")
|
|
MODEL_IMAGE_HINT = get_clean_env("MODEL_IMAGE", "AUTO")
|
|
DEFAULT_BLUEPRINT = "book_def.json"
|
|
|
|
# --- SECURITY & ADMIN ---
|
|
FLASK_SECRET = get_clean_env("FLASK_SECRET_KEY", "dev-secret-key-change-this")
|
|
ADMIN_USER = get_clean_env("ADMIN_USERNAME")
|
|
ADMIN_PASSWORD = get_clean_env("ADMIN_PASSWORD")
|
|
|
|
if FLASK_SECRET == "dev-secret-key-change-this":
|
|
print("⚠️ WARNING: Using default FLASK_SECRET_KEY. This is insecure for production.")
|
|
|
|
if not API_KEY: raise ValueError("❌ CRITICAL ERROR: GEMINI_API_KEY not found.")
|
|
|
|
# --- DATA DIRECTORIES ---
|
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
DATA_DIR = os.path.join(BASE_DIR, "data")
|
|
PROJECTS_DIR = os.path.join(DATA_DIR, "projects")
|
|
PERSONAS_DIR = os.path.join(DATA_DIR, "personas")
|
|
PERSONAS_FILE = os.path.join(PERSONAS_DIR, "personas.json")
|
|
FONTS_DIR = os.path.join(DATA_DIR, "fonts")
|
|
|
|
# --- ENSURE DIRECTORIES EXIST ---
|
|
# Critical: Create data folders immediately to prevent DB initialization errors
|
|
for d in [DATA_DIR, PROJECTS_DIR, PERSONAS_DIR, FONTS_DIR]:
|
|
if not os.path.exists(d): os.makedirs(d, exist_ok=True)
|
|
|
|
# --- AUTHENTICATION ---
|
|
GOOGLE_CREDS = os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
|
|
if GOOGLE_CREDS:
|
|
# Resolve to absolute path relative to this config file if not absolute
|
|
if not os.path.isabs(GOOGLE_CREDS):
|
|
base = os.path.dirname(os.path.abspath(__file__))
|
|
GOOGLE_CREDS = os.path.join(base, GOOGLE_CREDS)
|
|
|
|
if os.path.exists(GOOGLE_CREDS):
|
|
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = GOOGLE_CREDS
|
|
else:
|
|
print(f"⚠️ Warning: GOOGLE_APPLICATION_CREDENTIALS file not found at: {GOOGLE_CREDS}")
|
|
|
|
# --- DEFINITIONS ---
|
|
LENGTH_DEFINITIONS = {
|
|
"01": {"label": "Chapter Book", "words": "5,000 - 10,000", "chapters": 10, "depth": 1},
|
|
"1": {"label": "Flash Fiction", "words": "500 - 1,500", "chapters": 1, "depth": 1},
|
|
"2": {"label": "Short Story", "words": "5,000 - 10,000", "chapters": 5, "depth": 1},
|
|
"2b": {"label": "Young Adult", "words": "50,000 - 70,000", "chapters": 25, "depth": 3},
|
|
"3": {"label": "Novella", "words": "20,000 - 40,000", "chapters": 15, "depth": 2},
|
|
"4": {"label": "Novel", "words": "60,000 - 80,000", "chapters": 30, "depth": 3},
|
|
"5": {"label": "Epic", "words": "100,000+", "chapters": 50, "depth": 4}
|
|
}
|
|
|
|
# --- SYSTEM ---
|
|
VERSION = "1.2.0" |