Merge branch 'main' into 'main'

Ace module, code 408, injecting fixes + separate option for speakers processing

See merge request DazedAnon/DazedMTLTool!26
This commit is contained in:
DazedAnon 2025-11-16 18:33:42 +00:00
commit 5a00b33f50
3 changed files with 1289 additions and 348 deletions

View file

@ -256,6 +256,13 @@ class RPGMakerTab(QWidget):
self.join408_cb.setToolTip("Join 408 codes into a single string") self.join408_cb.setToolTip("Join 408 codes into a single string")
left_column.addWidget(self.join408_cb) left_column.addWidget(self.join408_cb)
self.speakers408_cb = QCheckBox("Process Speakers in 408")
self.speakers408_cb.setToolTip("Apply speaker detection and processing to code 408 (same as code 401)")
left_column.addWidget(self.speakers408_cb)
# Only show SPEAKERS408 for ACE engine (rpgmakerace.py)
if self.engine != "ACE":
self.speakers408_cb.hide()
left_column.addSpacing(15) left_column.addSpacing(15)
# Main Codes # Main Codes
@ -379,6 +386,8 @@ class RPGMakerTab(QWidget):
self.fixtextwrap_cb.stateChanged.disconnect() self.fixtextwrap_cb.stateChanged.disconnect()
self.ignoretltext_cb.stateChanged.disconnect() self.ignoretltext_cb.stateChanged.disconnect()
self.join408_cb.stateChanged.disconnect() self.join408_cb.stateChanged.disconnect()
if self.engine == "ACE":
self.speakers408_cb.stateChanged.disconnect()
# Main Codes # Main Codes
self.code401_cb.stateChanged.disconnect() self.code401_cb.stateChanged.disconnect()
@ -415,6 +424,8 @@ class RPGMakerTab(QWidget):
self.fixtextwrap_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) self.fixtextwrap_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
self.ignoretltext_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) self.ignoretltext_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
self.join408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) self.join408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
if self.engine == "ACE":
self.speakers408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
# Main Codes # Main Codes
self.code401_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) self.code401_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
@ -473,6 +484,10 @@ class RPGMakerTab(QWidget):
"CODE111": self.code111_cb.isChecked(), "CODE111": self.code111_cb.isChecked(),
"CODE108": self.code108_cb.isChecked(), "CODE108": self.code108_cb.isChecked(),
} }
# Only include SPEAKERS408 for ACE engine
if self.engine == "ACE":
config["SPEAKERS408"] = self.speakers408_cb.isChecked()
return config return config
@ -485,6 +500,10 @@ class RPGMakerTab(QWidget):
self.fixtextwrap_cb.setChecked(config.get("FIXTEXTWRAP", True)) self.fixtextwrap_cb.setChecked(config.get("FIXTEXTWRAP", True))
self.ignoretltext_cb.setChecked(config.get("IGNORETLTEXT", False)) self.ignoretltext_cb.setChecked(config.get("IGNORETLTEXT", False))
self.join408_cb.setChecked(config.get("JOIN408", False)) self.join408_cb.setChecked(config.get("JOIN408", False))
# Only set SPEAKERS408 for ACE engine
if self.engine == "ACE":
self.speakers408_cb.setChecked(config.get("SPEAKERS408", False))
# Main Codes # Main Codes
self.code401_cb.setChecked(config.get("CODE401", True)) self.code401_cb.setChecked(config.get("CODE401", True))

File diff suppressed because it is too large Load diff

View file

@ -34,6 +34,7 @@ PBAR = None
FILENAME = None FILENAME = None
TIMETOTAL = 0 # Total Time Taken for all translations TIMETOTAL = 0 # Total Time Taken for all translations
VOCAB_LOCK = threading.Lock() VOCAB_LOCK = threading.Lock()
PREFLIGHT_COUNT_MODE = False # When True, translateAI wrapper only counts units and never calls API
# Speakers # Speakers
NAMESLIST = [] NAMESLIST = []
@ -433,10 +434,107 @@ def update_vocab_section(category: str, pairs: list[tuple[str, str]]):
def parseMap(data, filename): def parseMap(data, filename):
totalTokens = [0, 0] totalTokens = [0, 0]
totalLines = 0
events = data["events"] events = data["events"]
global LOCK global LOCK
# --- Preflight: estimate exact progress total using the same translation batching ---
class _CountingBar:
def __init__(self):
self.n = 0
def update(self, n=1):
try:
self.n += int(n) if n is not None else 1
except Exception:
self.n += 1
def write(self, *args, **kwargs):
# Silent during preflight
pass
def refresh(self):
pass
def _estimate_map_units(d, fname) -> int:
# Use a deep copy to avoid mutating real data during estimation
dcopy = copy.deepcopy(d)
bar = _CountingBar()
# Temporarily enable preflight-count mode and route progress updates to our counter
global PREFLIGHT_COUNT_MODE
saved_preflight = PREFLIGHT_COUNT_MODE
PREFLIGHT_COUNT_MODE = True
global PBAR
saved_pbar = PBAR
PBAR = bar
try:
# Count display name TL (1 unit if present)
if "Map" in fname and isinstance(dcopy.get("displayName", None), str):
try:
translateAI(
dcopy["displayName"],
"Reply with only the " + LANGUAGE + " translation of the RPG location name",
False,
)
except Exception:
pass
# Notes and pages
evts = dcopy.get("events", []) or []
for evt in evts:
if not evt:
continue
note_val = evt.get("note") or ""
if not isinstance(note_val, str):
note_val = str(note_val) if note_val is not None else ""
# <LB> name translation
if "<LB>" in note_val:
name_val = evt.get("name") or ""
if isinstance(name_val, str) and name_val:
try:
translateAI(
name_val,
"Reply with only the " + LANGUAGE + " translation of the RPG location name",
False,
)
except Exception:
pass
# <msgText:"...">
if "<msgText:" in note_val:
try:
translateNote(evt, r"<msgText:\"(.*?)\">", False)
except Exception:
pass
# <namePop:...>, <LB:...>, <dn:...> handled before page processing in real run
if "<namePop:" in note_val:
try:
translateNoteOmitSpace(evt, r"<namePop:\\s?([\\w一-龠ぁ-ゔァ-ヴーa---\\uFF61-\\uFF9F]+)")
except Exception:
pass
if "<LB:" in note_val:
try:
translateNoteOmitSpace(evt, r"<LB:(.*?)\\s?>.*")
except Exception:
pass
if "<dn:" in note_val:
try:
translateNoteOmitSpace(evt, r"<dn:\\s*(.*)>.*")
except Exception:
pass
# Other note tags handled during main loop
# We'll just invoke the normal search pass which will batch and call translateAI
for page in (evt.get("pages", []) or []):
try:
searchCodes(page, bar, [], fname)
except Exception:
# Ignore counting errors to avoid blocking
pass
return getattr(bar, "n", 0) or 0
finally:
PREFLIGHT_COUNT_MODE = saved_preflight
PBAR = saved_pbar
# Translate displayName for Map files # Translate displayName for Map files
if "Map" in filename: if "Map" in filename:
response = translateAI( response = translateAI(
@ -448,30 +546,18 @@ def parseMap(data, filename):
totalTokens[1] += response[1][1] totalTokens[1] += response[1][1]
data["displayName"] = response[0].replace('"', "") data["displayName"] = response[0].replace('"', "")
# Get total for progress bar (sum of all command list lengths across pages) # Compute accurate total using preflight (includes speakers, choices, groups, and notes)
for event in events: totalLines = _estimate_map_units(data, filename)
if event: if not isinstance(totalLines, int) or totalLines <= 0:
note_val = event.get("note") or "" # Fallback to naive count so a bar still renders
if not isinstance(note_val, str): totalLines = 0
note_val = str(note_val) if note_val is not None else "" for event in events:
if "<LB>" in note_val: if event:
# Translate event name when flagged with <LB> for page in event.get("pages", []) or []:
name_val = event.get("name") or "" try:
if isinstance(name_val, str) and name_val: totalLines += len(page.get("list", []))
response = translateAI( except Exception:
name_val, pass
"Reply with only the " + LANGUAGE + " translation of the RPG location name",
False,
)
totalTokens[0] += response[1][0]
totalTokens[1] += response[1][1]
event["name"] = response[0].replace('"', "")
if "<msgText:" in note_val:
tokensResponse = translateNote(event, r"<msgText:\"(.*?)\">", False)
totalTokens[0] += tokensResponse[0]
totalTokens[1] += tokensResponse[1]
for page in event["pages"]:
totalLines += len(page["list"])
global PBAR global PBAR
# Process each page synchronously with progress updates # Process each page synchronously with progress updates
@ -595,13 +681,52 @@ def translateNoteOmitSpace(event, regex):
def parseCommonEvents(data, filename): def parseCommonEvents(data, filename):
totalTokens = [0, 0] totalTokens = [0, 0]
totalLines = 0
global LOCK global LOCK
# Get total for progress bar # --- Preflight: estimate exact progress total using same batching ---
for page in data: class _CountingBar:
if page is not None: def __init__(self):
totalLines += len(page["list"]) self.n = 0
def update(self, n=1):
try:
self.n += int(n) if n is not None else 1
except Exception:
self.n += 1
def write(self, *args, **kwargs):
pass
def refresh(self):
pass
def _estimate_units(pages, fname) -> int:
dcopy = copy.deepcopy(pages)
bar = _CountingBar()
global PREFLIGHT_COUNT_MODE, PBAR
saved_flag = PREFLIGHT_COUNT_MODE
saved_pbar = PBAR
PREFLIGHT_COUNT_MODE = True
PBAR = bar
try:
for page in dcopy:
if page is not None:
try:
searchCodes(page, bar, [], fname)
except Exception:
pass
return getattr(bar, "n", 0) or 0
finally:
PREFLIGHT_COUNT_MODE = saved_flag
PBAR = saved_pbar
totalLines = _estimate_units(data, filename)
if not isinstance(totalLines, int) or totalLines <= 0:
# Fallback to naive command count
totalLines = 0
for page in data:
if page is not None:
try:
totalLines += len(page.get("list", []))
except Exception:
pass
global PBAR global PBAR
with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar:
@ -623,15 +748,55 @@ def parseCommonEvents(data, filename):
def parseTroops(data, filename): def parseTroops(data, filename):
totalTokens = [0, 0] totalTokens = [0, 0]
totalLines = 0
global LOCK global LOCK
# Get total for progress bar # --- Preflight total using same code paths ---
for troop in data: class _CountingBar:
if troop is not None: def __init__(self):
for page in troop["pages"]: self.n = 0
# Progress measured by number of commands in each page's list def update(self, n=1):
totalLines += len(page["list"]) try:
self.n += int(n) if n is not None else 1
except Exception:
self.n += 1
def write(self, *args, **kwargs):
pass
def refresh(self):
pass
def _estimate_units(troops, fname) -> int:
tcopy = copy.deepcopy(troops)
bar = _CountingBar()
global PREFLIGHT_COUNT_MODE, PBAR
saved_flag = PREFLIGHT_COUNT_MODE
saved_pbar = PBAR
PREFLIGHT_COUNT_MODE = True
PBAR = bar
try:
for troop in tcopy:
if troop is None:
continue
for page in (troop.get("pages", []) or []):
if page is not None:
try:
searchCodes(page, bar, [], fname)
except Exception:
pass
return getattr(bar, "n", 0) or 0
finally:
PREFLIGHT_COUNT_MODE = saved_flag
PBAR = saved_pbar
totalLines = _estimate_units(data, filename)
if not isinstance(totalLines, int) or totalLines <= 0:
totalLines = 0
for troop in data:
if troop is not None:
for page in troop.get("pages", []) or []:
try:
totalLines += len(page.get("list", []))
except Exception:
pass
global PBAR global PBAR
with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar:
@ -656,49 +821,138 @@ def parseTroops(data, filename):
def parseNames(data, filename, context): def parseNames(data, filename, context):
totalTokens = [0, 0] totalTokens = [0, 0]
# Precompute total work units for progress bar # --- Preflight: custom estimator that mirrors searchNames increments (incl. notes/messages) ---
def count_work_units(data, context): class _CountingBar:
total = 0 def __init__(self):
self.n = 0
def update(self, n=1):
try:
self.n += int(n) if n is not None else 1
except Exception:
self.n += 1
def write(self, *args, **kwargs):
pass
def refresh(self):
pass
def _estimate_names_units(entries, ctx, fname) -> int:
ecopy = copy.deepcopy(entries)
bar = _CountingBar()
global PREFLIGHT_COUNT_MODE, PBAR
saved_flag = PREFLIGHT_COUNT_MODE
saved_pbar = PBAR
PREFLIGHT_COUNT_MODE = True
PBAR = bar
try:
# Count names/descriptions/profile/nickname
name_cnt = 0
desc_cnt = 0
profile_cnt = 0
nickname_cnt = 0
msg_cnt = 0
notes_cnt = 0
note_regexes = [
(r"<note:(.*?)>", False),
(r"<PE拡張:(.*?)>", False),
(r"<hint:(.*?)>", False),
(r"<SGDescription:(.*?)>", False),
(r"<SG説明:\n?(.*?)>", False),
(r"<SG説明2:\n?(.*?)>", False),
(r"<SG説明3:\n?(.*?)>", False),
(r"<SG説明4:\n?(.*?)>", False),
(r"<SG説明:.+?Client\s?:.+?\n\n(.*?)>", True),
(r"<SGカテゴリ:(.*?)>", False),
(r"<Switch Shop Description>\n(.*)\n", False),
(r"<MapText:(.*?)>", False),
(r"WATs:(.+?)>", False),
(r"ADTs?:(.+?)>", False),
(r"<detail:(.*?)>", False),
(r"<Name:(.*?)>", False),
(r"<sub_1:([^>]+)", True),
(r"<sub_2:([^>]+)", True),
(r"<sub_3:([^>]+)", True),
(r"<infowindow:(.*?)>", True),
(r"<ExtendDesc:(.*?)>", True),
(r"<desc\d:(.*?)>", False),
(r"<拡張説明:(.+?)>", False),
(r"<STS DESC>\n(.+?)\n<", False),
(r"text:(.+)>", False),
]
for entry in ecopy:
if not entry:
continue
nm = entry.get("name") or ""
ds = entry.get("description") or ""
nn = entry.get("nickname") or ""
pf = entry.get("profile") or ""
if ctx == "Actors":
if nm: name_cnt += 1
if nn: nickname_cnt += 1
if pf: profile_cnt += 1
elif ctx in ["Armors", "Weapons", "Items"]:
if nm: name_cnt += 1
if ds: desc_cnt += 1
elif ctx == "Skills":
if nm: name_cnt += 1
if ds: desc_cnt += 1
for k in range(1,5):
if entry.get(f"message{k}"): msg_cnt += 1
elif ctx in ["Enemies", "Classes", "MapInfos"]:
if nm: name_cnt += 1
# Notes counting
note = entry.get("note") or ""
if isinstance(note, str) and note:
for regex, _ww in note_regexes:
try:
matches = re.findall(regex, note, re.DOTALL)
except Exception:
matches = []
if regex.startswith(r"<SG説明:"):
for m in matches:
s = m if isinstance(m, str) else (m[0] if m else "")
if "Client:" in s or "Client :" in s:
continue
notes_cnt += 1
else:
notes_cnt += len(matches)
# Simulate increments via wrapper (updates bar.n)
if name_cnt:
translateAI([""] * name_cnt, "", True)
if desc_cnt:
translateAI([""] * desc_cnt, "", True)
if profile_cnt:
translateAI([""] * profile_cnt, "", True)
if nickname_cnt:
translateAI([""] * nickname_cnt, "", True)
if msg_cnt:
translateAI([""] * msg_cnt, "", True)
if notes_cnt:
translateAI([""] * notes_cnt, "", True)
return getattr(bar, "n", 0) or 0
finally:
PREFLIGHT_COUNT_MODE = saved_flag
PBAR = saved_pbar
total_units = _estimate_names_units(data, context, filename)
if not isinstance(total_units, int) or total_units <= 0:
# Reasonable fallback: count visible fields/messages (no notes)
total_units = 0
for entry in data: for entry in data:
if not entry: if not entry:
continue continue
if entry.get("name"): total_units += 1
# Names and associated fields if context in ["Armors", "Weapons", "Items", "Skills"] and entry.get("description"): total_units += 1
name = entry.get("name", "")
desc = entry.get("description", "")
nickname = entry.get("nickname", "")
profile = entry.get("profile", "")
if context == "Actors": if context == "Actors":
if name: if entry.get("nickname"): total_units += 1
total += 1 if entry.get("profile"): total_units += 1
if nickname: if context == "Skills":
total += 1 for k in range(1,5):
if profile: if entry.get(f"message{k}"): total_units += 1
total += 1
elif context in ["Armors", "Weapons", "Items"]:
if name:
total += 1
if desc:
total += 1
elif context == "Skills":
if name:
total += 1
if desc:
total += 1
# Messages translated individually in searchNames
for n in range(1, 5):
msg = entry.get(f"message{n}")
if msg:
total += 1
elif context in ["Enemies", "Classes", "MapInfos"]:
if name:
total += 1
return total
total_units = count_work_units(data, context)
global PBAR global PBAR
with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar:
@ -720,22 +974,50 @@ def parseNames(data, filename, context):
def parseSS(data, filename): def parseSS(data, filename):
totalTokens = [0, 0] totalTokens = [0, 0]
# Precompute total units (ignore notes): name, description, message1..4 presence # --- Preflight using searchSS over deep copy ---
def count_work_units(states): class _CountingBar:
total = 0 def __init__(self):
for st in states: self.n = 0
def update(self, n=1):
try:
self.n += int(n) if n is not None else 1
except Exception:
self.n += 1
def write(self, *args, **kwargs):
pass
def refresh(self):
pass
def _estimate_units(states, fname) -> int:
scopy = copy.deepcopy(states)
bar = _CountingBar()
global PREFLIGHT_COUNT_MODE, PBAR
saved_flag = PREFLIGHT_COUNT_MODE
saved_pbar = PBAR
PREFLIGHT_COUNT_MODE = True
PBAR = bar
try:
for ss in scopy:
if ss is not None:
try:
searchSS(ss, bar)
except Exception:
pass
return getattr(bar, "n", 0) or 0
finally:
PREFLIGHT_COUNT_MODE = saved_flag
PBAR = saved_pbar
total_units = _estimate_units(data, filename)
if not isinstance(total_units, int) or total_units <= 0:
total_units = 0
for st in data:
if not st: if not st:
continue continue
if st.get("name"): if st.get("name"): total_units += 1
total += 1 if st.get("description"): total_units += 1
if st.get("description"): for n in range(1,5):
total += 1 if st.get(f"message{n}"): total_units += 1
for n in range(1, 5):
if st.get(f"message{n}"):
total += 1
return total
total_units = count_work_units(data)
global PBAR global PBAR
with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar:
@ -758,29 +1040,53 @@ def parseSS(data, filename):
def parseSystem(data, filename): def parseSystem(data, filename):
totalTokens = [0, 0] totalTokens = [0, 0]
# Precompute total units across system sections (exclude notes; count strings) # --- Preflight: call searchSystem on deep copy to count increments ---
def count_work_units(sys): class _CountingBar:
total = 0 def __init__(self):
# Title self.n = 0
if sys.get("gameTitle"): def update(self, n=1):
total += 1 try:
# Terms (excluding 'messages' object) self.n += int(n) if n is not None else 1
terms = sys.get("terms", {}) except Exception:
for term_key, term_list in terms.items(): self.n += 1
if term_key == "messages": def write(self, *args, **kwargs):
continue pass
if isinstance(term_list, list): def refresh(self):
total += sum(1 for x in term_list if x is not None) pass
# Armor, Skill, Equip types
total += len(sys.get("armorTypes", []) or [])
total += len(sys.get("skillTypes", []) or [])
total += len(sys.get("equipTypes", []) or [])
# Messages
messages = terms.get("messages", {}) or {}
total += len(messages)
return total
total_units = count_work_units(data) def _estimate_units(sysobj, fname) -> int:
scopy = copy.deepcopy(sysobj)
bar = _CountingBar()
global PREFLIGHT_COUNT_MODE, PBAR
saved_flag = PREFLIGHT_COUNT_MODE
saved_pbar = PBAR
PREFLIGHT_COUNT_MODE = True
PBAR = bar
try:
try:
searchSystem(scopy, bar)
except Exception:
pass
return getattr(bar, "n", 0) or 0
finally:
PREFLIGHT_COUNT_MODE = saved_flag
PBAR = saved_pbar
total_units = _estimate_units(data, filename)
if not isinstance(total_units, int) or total_units <= 0:
# Fallback: rough count of strings
total_units = 0
if data.get("gameTitle"): total_units += 1
terms = data.get("terms", {}) or {}
for k,v in terms.items():
if k == "messages":
continue
if isinstance(v, list):
total_units += sum(1 for x in v if x is not None)
total_units += len(data.get("armorTypes", []) or [])
total_units += len(data.get("skillTypes", []) or [])
total_units += len(data.get("equipTypes", []) or [])
total_units += len((terms.get("messages", {}) or {}))
global PBAR global PBAR
with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar:
@ -800,12 +1106,50 @@ def parseSystem(data, filename):
def parseScenario(data, filename): def parseScenario(data, filename):
totalTokens = [0, 0] totalTokens = [0, 0]
totalLines = 0
global LOCK global LOCK
# Get total for progress bar # --- Preflight: run searchCodes on each page list ---
for page in data.items(): class _CountingBar:
totalLines += len(page[1]) def __init__(self):
self.n = 0
def update(self, n=1):
try:
self.n += int(n) if n is not None else 1
except Exception:
self.n += 1
def write(self, *args, **kwargs):
pass
def refresh(self):
pass
def _estimate_units(scenario, fname) -> int:
scopy = copy.deepcopy(scenario)
bar = _CountingBar()
global PREFLIGHT_COUNT_MODE, PBAR
saved_flag = PREFLIGHT_COUNT_MODE
saved_pbar = PBAR
PREFLIGHT_COUNT_MODE = True
PBAR = bar
try:
for key, lst in scopy.items():
if lst is not None:
try:
searchCodes(lst, bar, [], fname)
except Exception:
pass
return getattr(bar, "n", 0) or 0
finally:
PREFLIGHT_COUNT_MODE = saved_flag
PBAR = saved_pbar
totalLines = _estimate_units(data, filename)
if not isinstance(totalLines, int) or totalLines <= 0:
totalLines = 0
for _, lst in data.items():
try:
totalLines += len(lst or [])
except Exception:
pass
global PBAR global PBAR
with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar:
@ -3192,6 +3536,21 @@ def translateAI(text, history, fullPromptFlag):
# Return original text unmodified with zero tokens # Return original text unmodified with zero tokens
return [text, [0, 0]] return [text, [0, 0]]
# Preflight count mode: don't hit API; just simulate progress units
if 'PREFLIGHT_COUNT_MODE' in globals() and PREFLIGHT_COUNT_MODE:
try:
n = len(text) if isinstance(text, list) else 1
except Exception:
n = 1
if PBAR is not None:
try:
with LOCK:
PBAR.update(n)
except Exception:
pass
# Return original payload and zero tokens so totals aren't affected
return [text, [0, 0]]
return sharedtranslateAI( return sharedtranslateAI(
text=text, text=text,
history=history, history=history,