mtapi / app /config.py
Cnass's picture
Upload 3 files
072a1f2 verified
Raw History Blame Contribute Delete
4.37 kB
import os
def _int(name: str, default: int) -> int:
return int(os.environ.get(name, default))
def _float(name: str, default: float) -> float:
return float(os.environ.get(name, default))
# Back to 7B now that this runs on a T4: 1.8B only existed to make CPU
# inference bearable, and it costs real accuracy. Q4_K_M (4.6 GB) plus the KV
# cache below leaves most of the 16 GB free, so Q6_K or Q8_0 also fit if the
# extra quality is worth the bandwidth - see the VRAM budget in the README.
MODEL_REPO = os.environ.get("MODEL_REPO", "tencent/Hy-MT2-7B-GGUF")
MODEL_FILE = os.environ.get("MODEL_FILE", "Hy-MT2-7B-Q4_K_M.gguf")
MODEL_DIR = os.environ.get("MODEL_DIR", "/app/models")
# Optional JSON file of term -> translation applied to every request (only the
# terms that occur in a given string are attached). This is what keeps
# character names and game terms identical across a whole 50k-string run.
GLOSSARY_FILE = os.environ.get("GLOSSARY_FILE", "")
# Style applied when a request does not send its own. Measurably the strongest
# lever for register consistency: without it the model drifts between the
# formal and informal second person from one line to the next (Turkish
# siz/sen), which reads as two different characters. Example value:
# "casual spoken Turkish, informal second person singular (sen)"
DEFAULT_STYLE = os.environ.get("DEFAULT_STYLE", "")
# Strings packed into a single generation by /translate/document. Bigger
# groups amortise the instruction prompt further and give the model more
# surrounding dialogue, but raise the cost of a group failing validation and
# being retried one string at a time.
GROUP_SIZE = _int("GROUP_SIZE", 10)
# "rosetta" or "hy-mt2" - the two families use different instruction shapes,
# and sending one model the other's prompt produces garbage. Inferred from the
# repo name so switching MODEL_REPO alone stays safe; set PROMPT_FORMAT
# explicitly to override.
PROMPT_FORMAT = os.environ.get("PROMPT_FORMAT", "").strip().lower()
if not PROMPT_FORMAT:
PROMPT_FORMAT = "rosetta" if "rosetta" in MODEL_REPO.lower() else "hy-mt2"
# Where uploaded RPG Maker projects live. Prefers the Space's persistent
# volume when one is mounted: a job that survives a restart is the difference
# between resuming a half-finished game and re-translating it on GPU time.
_DEFAULT_PROJECT_DIR = (
"/data/projects"
if os.path.isdir("/data") and os.access("/data", os.W_OK)
else "/app/projects"
)
PROJECT_DIR = os.environ.get("PROJECT_DIR", _DEFAULT_PROJECT_DIR)
# Uploaded projects are deleted this many hours after their last update, so a
# few full games do not quietly fill the disk.
PROJECT_RETENTION_HOURS = _int("PROJECT_RETENTION_HOURS", 24)
# Hard cap on an uploaded zip. A www/data folder is a few MB even for a long
# game; anything far past that is a whole packaged game, not the data folder.
MAX_UPLOAD_MB = _int("MAX_UPLOAD_MB", 200)
LLAMA_SERVER_HOST = os.environ.get("LLAMA_SERVER_HOST", "127.0.0.1")
LLAMA_SERVER_PORT = _int("LLAMA_SERVER_PORT", 8080)
LLAMA_SERVER_URL = f"http://{LLAMA_SERVER_HOST}:{LLAMA_SERVER_PORT}"
# Sampling defaults per model family: Hy-MT2's model card prescribes
# 0.7/0.6/20/1.05, while Rosetta is a Gemma 3 fine-tune whose card only
# specifies temperature 0.7 - the rest stay at Gemma 3's own defaults.
if PROMPT_FORMAT == "rosetta":
_TEMPERATURE, _TOP_P, _TOP_K, _REPEAT_PENALTY = 0.7, 0.95, 64, 1.0
else:
_TEMPERATURE, _TOP_P, _TOP_K, _REPEAT_PENALTY = 0.7, 0.6, 20, 1.05
TEMPERATURE = _float("TEMPERATURE", _TEMPERATURE)
TOP_P = _float("TOP_P", _TOP_P)
TOP_K = _int("TOP_K", _TOP_K)
REPEAT_PENALTY = _float("REPEAT_PENALTY", _REPEAT_PENALTY)
MAX_TOKENS = _int("MAX_TOKENS", 512)
# --parallel value the llama-server was started with; also used to bound
# how many translation requests this gateway keeps in flight at once so
# requests queue instead of overwhelming the server's slots. Higher on GPU
# than the 8 that suited CPU: continuous batching actually scales there,
# where on CPU the cores were saturated long before the slots were.
PARALLEL_SLOTS = _int("PARALLEL_SLOTS", 16)
MAX_BATCH_SIZE = _int("MAX_BATCH_SIZE", 200)
REQUEST_TIMEOUT_SECONDS = _float("REQUEST_TIMEOUT_SECONDS", 120.0)
# Optional shared-secret auth for the public gateway. Empty = disabled.
API_KEY = os.environ.get("API_KEY", "")
PORT = _int("PORT", 7860)