any2human / app /config.py
idnameraj's picture
Re-enable primary T5 paraphrase for stronger out1→out2 divergence.
24a79a8
Raw
History Blame Contribute Delete
9.02 kB
"""Application config."""
from __future__ import annotations
import os
from pathlib import Path
from dotenv import load_dotenv
ROOT_DIR = Path(__file__).resolve().parent.parent
load_dotenv(ROOT_DIR / ".env")
APP_DIR = Path(__file__).resolve().parent
APP_TITLE = os.environ.get("APP_TITLE", "ZuZu Writer")
HOST = os.environ.get("HOST", "0.0.0.0")
PORT = int(os.environ.get("PORT", "7860") or "7860")
SPACY_MODEL = os.environ.get("SPACY_MODEL", "en_core_web_sm")
MAX_CHARS = max(
100_000,
int(os.environ.get("MAX_CHARS", "1000000") or "1000000"),
)
# Optional Supabase authentication and UI quotas.
SUPABASE_URL = (os.environ.get("SUPABASE_URL") or "").rstrip("/")
SUPABASE_ANON_KEY = os.environ.get("SUPABASE_ANON_KEY") or ""
SUPABASE_SERVICE_ROLE_KEY = os.environ.get("SUPABASE_SERVICE_ROLE_KEY") or ""
SUPABASE_JWT_SECRET = os.environ.get("SUPABASE_JWT_SECRET") or ""
AUTH_ENABLED = bool(SUPABASE_URL and SUPABASE_ANON_KEY and SUPABASE_SERVICE_ROLE_KEY)
GUEST_DAILY_REWRITES = max(
1, min(int(os.environ.get("GUEST_DAILY_REWRITES", "1000") or "1000"), 1000)
)
GUEST_MAX_WORDS = max(
20, min(int(os.environ.get("GUEST_MAX_WORDS", "100") or "100"), 5000)
)
GUEST_DAILY_WORD_CAP = max(
GUEST_MAX_WORDS,
min(
int(
os.environ.get("GUEST_DAILY_WORD_CAP", str(GUEST_MAX_WORDS))
or str(GUEST_MAX_WORDS)
),
5000,
),
)
SESSION_IDLE_MINUTES = int(
os.environ.get("SESSION_IDLE_MINUTES", "30") or "30"
)
# Self-hosted LanguageTool (ZuZu Grammar)
# Example compose service: http://languagetool:8010 | local: http://127.0.0.1:8010
LANGUAGE_TOOL_URL = (os.environ.get("LANGUAGE_TOOL_URL") or "").rstrip("/")
LANGUAGE_TOOL_LANGUAGE = os.environ.get("LANGUAGE_TOOL_LANGUAGE") or "en-US"
LANGUAGE_TOOL_TIMEOUT = float(os.environ.get("LANGUAGE_TOOL_TIMEOUT", "90") or "90")
LANGUAGE_TOOL_CHUNK_CHARS = max(
500,
min(int(os.environ.get("LANGUAGE_TOOL_CHUNK_CHARS", "1800") or "1800"), 8000),
)
# Standalone grammar endpoint cap. Rewrite grammar runs sentence-by-sentence.
GRAMMAR_MAX_CHARS = max(
1000,
min(int(os.environ.get("GRAMMAR_MAX_CHARS", "50000") or "50000"), MAX_CHARS),
)
_lt_flag = (os.environ.get("LANGUAGE_TOOL_ENABLED") or "true").strip().lower()
LANGUAGE_TOOL_ENABLED = _lt_flag not in {"0", "false", "no", "off"} and bool(LANGUAGE_TOOL_URL)
# Optional lightweight semantic safety model. It is never used for generation.
MINILM_MODEL = (
os.environ.get("MINILM_MODEL") or "sentence-transformers/all-MiniLM-L6-v2"
).strip()
# Grammar cleanup is applied only after a candidate rewrite. Skipped sentences stay exact.
_gfo = (os.environ.get("GRAMMAR_FIX_OUTPUT") or "true").strip().lower()
GRAMMAR_FIX_OUTPUT = _gfo not in {"0", "false", "no", "off"}
ENGINE_BATCH_PARAS = max(
1, min(int(os.environ.get("ENGINE_BATCH_PARAS", "20") or "20"), 100)
)
ENGINE_MIN_CONFIDENCE = max(
0.0, min(float(os.environ.get("ENGINE_MIN_CONFIDENCE", "0.55") or "0.55"), 1.0)
)
ENGINE_SAFETY_MIN = max(
0.0, min(float(os.environ.get("ENGINE_SAFETY_MIN", "0.80") or "0.80"), 1.0)
)
# MiniLM safety gate (optional). When off, phrase/lexical use classical
# collocation brakes instead of embedding similarity.
_ems = (os.environ.get("ENGINE_USE_MINILM_SAFETY") or "true").strip().lower()
ENGINE_USE_MINILM_SAFETY = _ems in {"1", "true", "yes", "on"}
# When MiniLM is off: aggressive classical wording (option 2) maximizes
# surface change per pass while keeping hard VO/sense blockers. Set false
# to restore tight anti-drift mode (options 1/3 later).
_eca = (os.environ.get("ENGINE_CLASSICAL_AGGRESSIVE") or "true").strip().lower()
ENGINE_CLASSICAL_AGGRESSIVE = _eca in {"1", "true", "yes", "on"}
# Rotate between equally-ranked structural options so repeating a request
# returns a different valid rewrite instead of the identical one.
_esv = (os.environ.get("ENGINE_STRUCTURAL_VARIATION") or "true").strip().lower()
ENGINE_STRUCTURAL_VARIATION = _esv in {"1", "true", "yes", "on"}
# Context-aware vocabulary refinement after structural/paraphrase rewrites.
# Synonym count is chosen dynamically from sentence length (and polish).
_elr = (os.environ.get("ENGINE_LEXICAL_REFINEMENT") or "true").strip().lower()
ENGINE_LEXICAL_REFINEMENT = _elr in {"1", "true", "yes", "on"}
# 0 = fully dynamic (recommended). Positive values only hard-cap that budget.
ENGINE_LEXICAL_MAX_CHANGES = max(
0,
min(int(os.environ.get("ENGINE_LEXICAL_MAX_CHANGES", "0") or "0"), 15),
)
ENGINE_LEXICAL_MIN_WSD = max(
0.0,
min(float(os.environ.get("ENGINE_LEXICAL_MIN_WSD", "0.14") or "0.14"), 1.0),
)
ENGINE_LEXICAL_MIN_ZIPF = max(
0.0,
min(float(os.environ.get("ENGINE_LEXICAL_MIN_ZIPF", "4.0") or "4.0"), 8.0),
)
ENGINE_LEXICAL_MAX_FREQUENCY_GAP = max(
0.0,
min(
float(
os.environ.get("ENGINE_LEXICAL_MAX_FREQUENCY_GAP", "0.60")
or "0.60"
),
8.0,
),
)
# Prefer everyday synonyms: allow a larger jump toward more-common words,
# but only a tiny step toward rarer/advanced wording.
ENGINE_LEXICAL_MAX_SIMPLER_GAP = max(
0.0,
min(
float(
os.environ.get("ENGINE_LEXICAL_MAX_SIMPLER_GAP", "2.5") or "2.5"
),
8.0,
),
)
ENGINE_LEXICAL_MAX_HARDER_GAP = max(
0.0,
min(
float(
os.environ.get("ENGINE_LEXICAL_MAX_HARDER_GAP", "0.15") or "0.15"
),
2.0,
),
)
_els = (os.environ.get("ENGINE_LEXICAL_PREFER_SIMPLER") or "true").strip().lower()
ENGINE_LEXICAL_PREFER_SIMPLER = _els in {"1", "true", "yes", "on"}
ENGINE_WORDNET_LEXICON = (
os.environ.get("ENGINE_WORDNET_LEXICON") or "oewn:2025"
).strip()
# Require every rewriteable sentence to change wording when a safe option exists.
_erw = (os.environ.get("ENGINE_REQUIRE_WORDING_CHANGE") or "true").strip().lower()
ENGINE_REQUIRE_WORDING_CHANGE = _erw in {"1", "true", "yes", "on"}
# Fixed cleft fallback is off by default; prefer local paraphrase instead.
_efr = (os.environ.get("ENGINE_FORCE_REWRITE") or "false").strip().lower()
ENGINE_FORCE_REWRITE = _efr in {"1", "true", "yes", "on"}
# Local CPU T5 paraphraser (primary rewrite path when the model is available).
_ep = (os.environ.get("ENGINE_PARAPHRASE") or "true").strip().lower()
ENGINE_PARAPHRASE = _ep in {"1", "true", "yes", "on"}
_epp = (os.environ.get("ENGINE_PARAPHRASE_PRIMARY") or "true").strip().lower()
ENGINE_PARAPHRASE_PRIMARY = _epp in {"1", "true", "yes", "on"}
ENGINE_PARAPHRASE_MODEL = (
os.environ.get("ENGINE_PARAPHRASE_MODEL")
or "Vamsi/T5_Paraphrase_Paws"
).strip()
ENGINE_PARAPHRASE_MIN_SIM = max(
0.0,
min(
float(os.environ.get("ENGINE_PARAPHRASE_MIN_SIM", "0.72") or "0.72"),
1.0,
),
)
ENGINE_PARAPHRASE_NUM_RETURN = max(
1,
min(int(os.environ.get("ENGINE_PARAPHRASE_NUM_RETURN", "6") or "6"), 8),
)
ENGINE_PARAPHRASE_MAX_NEW_TOKENS = max(
16,
min(
int(os.environ.get("ENGINE_PARAPHRASE_MAX_NEW_TOKENS", "72") or "72"),
128,
),
)
# Reject paraphrases whose surface form is still nearly identical to the source.
ENGINE_PARAPHRASE_MAX_SURFACE = max(
0.5,
min(
float(os.environ.get("ENGINE_PARAPHRASE_MAX_SURFACE", "0.70") or "0.70"),
0.99,
),
)
# When primary paraphrase is on, require at least this much surface distance.
ENGINE_PARAPHRASE_MIN_DIVERGENCE = max(
0.0,
min(
float(
os.environ.get("ENGINE_PARAPHRASE_MIN_DIVERGENCE", "0.30") or "0.30"
),
0.6,
),
)
# Phrase-level rewrite: verb–object / modifier–noun spans via WordNet + optional T5.
_ephr = (os.environ.get("ENGINE_PHRASE_REWRITE") or "true").strip().lower()
ENGINE_PHRASE_REWRITE = _ephr in {"1", "true", "yes", "on"}
# 0 = dynamic (1 normally, 2 with polish). Positive values hard-cap changes.
ENGINE_PHRASE_MAX_CHANGES = max(
0,
min(int(os.environ.get("ENGINE_PHRASE_MAX_CHANGES", "0") or "0"), 4),
)
ENGINE_PHRASE_MIN_SIM = max(
0.0,
min(float(os.environ.get("ENGINE_PHRASE_MIN_SIM", "0.74") or "0.74"), 1.0),
)
# T5 span paraphrase is optional; WordNet phrase swaps are the default wording path.
_ephr_t5 = (os.environ.get("ENGINE_PHRASE_USE_T5") or "false").strip().lower()
ENGINE_PHRASE_USE_T5 = _ephr_t5 in {"1", "true", "yes", "on"}
# Keep rewritten length near the source (trims only when clearly bloated).
_epl = (os.environ.get("ENGINE_PRESERVE_LENGTH") or "true").strip().lower()
ENGINE_PRESERVE_LENGTH = _epl in {"1", "true", "yes", "on"}
# Split long clauses during structural fallback for more surface change.
_esl = (os.environ.get("ENGINE_SPLIT_LONG") or "true").strip().lower()
ENGINE_SPLIT_LONG = _esl in {"1", "true", "yes", "on"}
ENGINE_SPLIT_MIN_WORDS = max(
10,
min(int(os.environ.get("ENGINE_SPLIT_MIN_WORDS", "16") or "16"), 40),
)