@danhachuel/thunderbolt 0.2.91 → 0.2.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/main.py +19 -4
- package/hermes_ui/languages.py +18 -6
- package/package.json +1 -1
- package/scripts/cli.mjs +49 -1
package/app/main.py
CHANGED
|
@@ -216,9 +216,20 @@ st.markdown("""
|
|
|
216
216
|
|
|
217
217
|
|
|
218
218
|
def current_ui_language() -> str:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
"""Return the session language without hitting disk on every widget render."""
|
|
220
|
+
cached = st.session_state.get("ui_language")
|
|
221
|
+
if cached:
|
|
222
|
+
return language_code(cached)
|
|
223
|
+
requested = ""
|
|
224
|
+
try:
|
|
225
|
+
requested = str(st.query_params.get("lang") or "").strip()
|
|
226
|
+
except Exception:
|
|
227
|
+
requested = ""
|
|
228
|
+
if requested:
|
|
229
|
+
normalized = language_code(requested)
|
|
230
|
+
else:
|
|
231
|
+
settings = read_json("settings.json", {})
|
|
232
|
+
normalized = language_code(settings.get("ui_language") or "pt")
|
|
222
233
|
st.session_state["ui_language"] = normalized
|
|
223
234
|
return normalized
|
|
224
235
|
|
|
@@ -235,7 +246,7 @@ _STREAMLIT_I18N_INSTALLED = False
|
|
|
235
246
|
def _translate_streamlit_arguments(method_name: str, args: tuple[Any, ...], kwargs: dict[str, Any]) -> tuple[tuple[Any, ...], dict[str, Any]]:
|
|
236
247
|
if not args and "label" not in kwargs:
|
|
237
248
|
return args, kwargs
|
|
238
|
-
selected_language = current_ui_language()
|
|
249
|
+
selected_language = str(st.session_state.get("ui_language") or current_ui_language())
|
|
239
250
|
translated_args = list(args)
|
|
240
251
|
if translated_args and isinstance(translated_args[0], str):
|
|
241
252
|
translated_args[0] = ui_text(translated_args[0], selected_language)
|
|
@@ -339,6 +350,10 @@ def render_ui_language_picker(language: str) -> None:
|
|
|
339
350
|
)
|
|
340
351
|
if selected != current:
|
|
341
352
|
save_ui_language(selected)
|
|
353
|
+
try:
|
|
354
|
+
st.query_params["lang"] = selected
|
|
355
|
+
except Exception:
|
|
356
|
+
pass
|
|
342
357
|
st.rerun()
|
|
343
358
|
|
|
344
359
|
|
package/hermes_ui/languages.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import base64
|
|
6
|
+
from functools import lru_cache
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
8
9
|
|
|
@@ -589,6 +590,22 @@ for _row in _CONTENT_TOKEN_TRANSLATION_ROWS:
|
|
|
589
590
|
UI_TOKEN_TRANSLATIONS[_code][_source] = _translated
|
|
590
591
|
|
|
591
592
|
|
|
593
|
+
@lru_cache(maxsize=16)
|
|
594
|
+
def _combined_translations(language: str) -> dict[str, str]:
|
|
595
|
+
"""Build the immutable-language translation index once per process."""
|
|
596
|
+
return {
|
|
597
|
+
**UI_TRANSLATIONS.get(language, {}),
|
|
598
|
+
**UI_CONTENT_TRANSLATIONS.get(language, {}),
|
|
599
|
+
**UI_TOKEN_TRANSLATIONS.get(language, {}),
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
@lru_cache(maxsize=16)
|
|
604
|
+
def _sorted_translations(language: str) -> tuple[tuple[str, str], ...]:
|
|
605
|
+
"""Cache longest-first token replacement order for each language."""
|
|
606
|
+
return tuple(sorted(_combined_translations(language).items(), key=lambda item: len(item[0]), reverse=True))
|
|
607
|
+
|
|
608
|
+
|
|
592
609
|
def translate_ui_content(value: Any, language: Any = "pt") -> Any:
|
|
593
610
|
"""Translate visible Streamlit content while preserving non-text values and user data markup."""
|
|
594
611
|
if not isinstance(value, str):
|
|
@@ -601,12 +618,7 @@ def translate_ui_content(value: Any, language: Any = "pt") -> Any:
|
|
|
601
618
|
if exact is not None:
|
|
602
619
|
return exact
|
|
603
620
|
translated = value
|
|
604
|
-
|
|
605
|
-
**UI_TRANSLATIONS.get(code, {}),
|
|
606
|
-
**UI_CONTENT_TRANSLATIONS.get(code, {}),
|
|
607
|
-
**UI_TOKEN_TRANSLATIONS.get(code, {}),
|
|
608
|
-
}
|
|
609
|
-
for source, target in sorted(combined_translations.items(), key=lambda item: len(item[0]), reverse=True):
|
|
621
|
+
for source, target in _sorted_translations(code):
|
|
610
622
|
if source != target and source in translated:
|
|
611
623
|
translated = translated.replace(source, target)
|
|
612
624
|
return translated
|
package/package.json
CHANGED
package/scripts/cli.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
+
import http from "node:http";
|
|
4
|
+
import net from "node:net";
|
|
3
5
|
import { existsSync, mkdirSync, readFileSync, copyFileSync, readdirSync } from "node:fs";
|
|
4
6
|
import { homedir, platform } from "node:os";
|
|
5
7
|
import { join, resolve } from "node:path";
|
|
@@ -150,6 +152,51 @@ if (args[0] === "pipeline-worker" || args.includes("--pipeline-worker")) {
|
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
const port = process.env.THUNDERBOLT_PORT || process.env.HERMES_PORT || "3030";
|
|
155
|
+
const publicPort = Number.parseInt(String(port), 10);
|
|
156
|
+
const backendPort = Number.isFinite(publicPort) ? publicPort + 1 : 3031;
|
|
157
|
+
const supportedLanguages = new Set(["en", "zh", "de", "vi", "tr", "pt", "ru", "es", "id", "it"]);
|
|
158
|
+
|
|
159
|
+
const proxy = http.createServer((request, response) => {
|
|
160
|
+
const requestUrl = new URL(request.url || "/", `http://localhost:${publicPort}`);
|
|
161
|
+
const pathParts = requestUrl.pathname.split("/").filter(Boolean);
|
|
162
|
+
const languagePrefix = pathParts.length === 1 && supportedLanguages.has(pathParts[0]) ? pathParts[0] : "";
|
|
163
|
+
if (languagePrefix) {
|
|
164
|
+
requestUrl.pathname = "/";
|
|
165
|
+
requestUrl.searchParams.set("lang", languagePrefix);
|
|
166
|
+
response.writeHead(302, { Location: `${requestUrl.pathname}?${requestUrl.searchParams.toString()}` });
|
|
167
|
+
response.end();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const upstream = http.request({
|
|
171
|
+
hostname: "127.0.0.1",
|
|
172
|
+
port: backendPort,
|
|
173
|
+
method: request.method,
|
|
174
|
+
path: `${requestUrl.pathname}${requestUrl.search}`,
|
|
175
|
+
headers: { ...request.headers, host: `127.0.0.1:${backendPort}` },
|
|
176
|
+
}, (upstreamResponse) => {
|
|
177
|
+
response.writeHead(upstreamResponse.statusCode || 502, upstreamResponse.headers);
|
|
178
|
+
upstreamResponse.pipe(response);
|
|
179
|
+
});
|
|
180
|
+
upstream.on("error", (error) => {
|
|
181
|
+
response.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
|
|
182
|
+
response.end(`Thunderbolt backend indisponível: ${error.message}`);
|
|
183
|
+
});
|
|
184
|
+
request.pipe(upstream);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
proxy.on("upgrade", (request, clientSocket, head) => {
|
|
188
|
+
const upstreamSocket = net.connect(backendPort, "127.0.0.1", () => {
|
|
189
|
+
const headers = Object.entries(request.headers)
|
|
190
|
+
.map(([name, value]) => `${name}: ${Array.isArray(value) ? value.join(", ") : value}`)
|
|
191
|
+
.join("\\r\\n");
|
|
192
|
+
upstreamSocket.write(`GET ${request.url} HTTP/1.1\\r\\n${headers}\\r\\n\\r\\n`);
|
|
193
|
+
if (head.length) upstreamSocket.write(head);
|
|
194
|
+
clientSocket.pipe(upstreamSocket).pipe(clientSocket);
|
|
195
|
+
});
|
|
196
|
+
upstreamSocket.on("error", () => clientSocket.destroy());
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
proxy.listen(publicPort, "127.0.0.1");
|
|
153
200
|
const worker = spawn(python, ["-m", "hermes_ui.automation_worker"], {
|
|
154
201
|
cwd: root,
|
|
155
202
|
stdio: "inherit",
|
|
@@ -162,7 +209,7 @@ const pipelineWorker = spawn(python, ["-m", "hermes_ui.pipeline_worker"], {
|
|
|
162
209
|
env: runtimeEnv,
|
|
163
210
|
windowsHide: false,
|
|
164
211
|
});
|
|
165
|
-
const child = spawn(python, ["-m", "streamlit", "run", main, "--server.port",
|
|
212
|
+
const child = spawn(python, ["-m", "streamlit", "run", main, "--server.port", String(backendPort), "--server.address", "127.0.0.1"], {
|
|
166
213
|
cwd: root,
|
|
167
214
|
stdio: "inherit",
|
|
168
215
|
env: runtimeEnv,
|
|
@@ -170,6 +217,7 @@ const child = spawn(python, ["-m", "streamlit", "run", main, "--server.port", po
|
|
|
170
217
|
});
|
|
171
218
|
|
|
172
219
|
const stopWorker = () => {
|
|
220
|
+
proxy.close();
|
|
173
221
|
if (!worker.killed) worker.kill();
|
|
174
222
|
if (!pipelineWorker.killed) pipelineWorker.kill();
|
|
175
223
|
};
|