File size: 4,369 Bytes
375386d
 
 
 
 
 
 
 
 
 
 
0f5268a
 
 
 
 
 
375386d
 
4112c61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
072a1f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375386d
 
 
 
4112c61
 
 
 
 
 
 
 
 
 
 
 
375386d
 
 
 
0f5268a
 
 
 
375386d
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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)