Spaces:
Running on Zero
Running on Zero
Commit ·
23eb152
1
Parent(s): 04721fb
Log translate model/prompt, link OpenRouter key signup and leaderboard
Browse filestranslate events now record backend, model, and the actual prompt text
used (null for the local CPU backend, which ignores the prompt file) -
translate_all previously only logged a generic "OpenRouter" backend with
no model id at all. Landing page also links to where to get an
OpenRouter API key and to the Tibetan AI Leaderboard for model comparison.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- app.py +5 -3
- handlers.py +9 -4
- tests/test_handlers.py +40 -1
app.py
CHANGED
|
@@ -43,9 +43,11 @@ def build_app() -> gr.Blocks:
|
|
| 43 |
"(https://huggingface.co/billingsmoore/mlotsawa-ground-base)) — free, runs on this "
|
| 44 |
"machine, but a plain translation model, not an instruction-following AI (it ignores "
|
| 45 |
"the translation prompt below) and generally lower translation quality. For higher-quality, instruction-"
|
| 46 |
-
"following translation, add an **OpenRouter API key** in
|
| 47 |
-
"from the dropdown — this calls OpenRouter's API (usage is
|
| 48 |
-
"account) and uses the editable translation prompt.
|
|
|
|
|
|
|
| 49 |
"Usage of this space may be monitored for research purposes. You can opt out of this "
|
| 50 |
"in Settings."
|
| 51 |
)
|
|
|
|
| 43 |
"(https://huggingface.co/billingsmoore/mlotsawa-ground-base)) — free, runs on this "
|
| 44 |
"machine, but a plain translation model, not an instruction-following AI (it ignores "
|
| 45 |
"the translation prompt below) and generally lower translation quality. For higher-quality, instruction-"
|
| 46 |
+
"following translation, add an [**OpenRouter API key**](https://openrouter.ai/keys) in "
|
| 47 |
+
"Settings and pick a model from the dropdown — this calls OpenRouter's API (usage is "
|
| 48 |
+
"billed to your OpenRouter account) and uses the editable translation prompt. See the "
|
| 49 |
+
"[Tibetan AI Leaderboard](https://huggingface.co/spaces/khyentsevision/tibetan-ai-leaderboard) "
|
| 50 |
+
"for how different models perform on Tibetan translation evaluations.\n\n"
|
| 51 |
"Usage of this space may be monitored for research purposes. You can opt out of this "
|
| 52 |
"in Settings."
|
| 53 |
)
|
handlers.py
CHANGED
|
@@ -293,9 +293,11 @@ def _translate_one(state: dict, slot_idx: int, source: str, api_key: str, model:
|
|
| 293 |
segments[actual_idx]["target"] = text
|
| 294 |
state["segments"] = segments
|
| 295 |
|
| 296 |
-
|
| 297 |
_log(state, "translate", {
|
| 298 |
-
"backend":
|
|
|
|
|
|
|
| 299 |
"segment_count": 1,
|
| 300 |
"segments": [{"source": source, "target": text}],
|
| 301 |
})
|
|
@@ -308,7 +310,8 @@ def _translate_all(state, api_key, model, *slot_values):
|
|
| 308 |
targets = list(slot_values[MAX_SLOTS:])
|
| 309 |
state = _save_page_edits(state, sources, targets)
|
| 310 |
|
| 311 |
-
|
|
|
|
| 312 |
|
| 313 |
stop = threading.Event()
|
| 314 |
result = [None, None]
|
|
@@ -350,7 +353,9 @@ def _translate_all(state, api_key, model, *slot_values):
|
|
| 350 |
status += f" {len(errors)} error(s)."
|
| 351 |
|
| 352 |
_log(state, "translate", {
|
| 353 |
-
"backend":
|
|
|
|
|
|
|
| 354 |
"segment_count": count,
|
| 355 |
"segments": [{"source": seg.get("source", ""), "target": seg.get("target", "")} for seg in segments],
|
| 356 |
})
|
|
|
|
| 293 |
segments[actual_idx]["target"] = text
|
| 294 |
state["segments"] = segments
|
| 295 |
|
| 296 |
+
is_openrouter = using_openrouter(api_key)
|
| 297 |
_log(state, "translate", {
|
| 298 |
+
"backend": "openrouter" if is_openrouter else "local_cpu",
|
| 299 |
+
"model": model if is_openrouter else "billingsmoore/mlotsawa-ground-base",
|
| 300 |
+
"prompt": read_prompt() if is_openrouter else None,
|
| 301 |
"segment_count": 1,
|
| 302 |
"segments": [{"source": source, "target": text}],
|
| 303 |
})
|
|
|
|
| 310 |
targets = list(slot_values[MAX_SLOTS:])
|
| 311 |
state = _save_page_edits(state, sources, targets)
|
| 312 |
|
| 313 |
+
is_openrouter = using_openrouter(api_key)
|
| 314 |
+
backend = "OpenRouter" if is_openrouter else "local CPU model (mlotsawa-ground-base)"
|
| 315 |
|
| 316 |
stop = threading.Event()
|
| 317 |
result = [None, None]
|
|
|
|
| 353 |
status += f" {len(errors)} error(s)."
|
| 354 |
|
| 355 |
_log(state, "translate", {
|
| 356 |
+
"backend": "openrouter" if is_openrouter else "local_cpu",
|
| 357 |
+
"model": model if is_openrouter else "billingsmoore/mlotsawa-ground-base",
|
| 358 |
+
"prompt": read_prompt() if is_openrouter else None,
|
| 359 |
"segment_count": count,
|
| 360 |
"segments": [{"source": seg.get("source", ""), "target": seg.get("target", "")} for seg in segments],
|
| 361 |
})
|
tests/test_handlers.py
CHANGED
|
@@ -357,10 +357,25 @@ def test_translate_one_logs_translate_event():
|
|
| 357 |
h._translate_one(state, 0, "source text", "api-key", "model-x")
|
| 358 |
event_type, session_id, payload = mock_log.call_args[0]
|
| 359 |
assert event_type == "translate"
|
| 360 |
-
assert payload["backend"] == "openrouter
|
|
|
|
|
|
|
| 361 |
assert payload["segments"] == [{"source": "source text", "target": "translated!"}]
|
| 362 |
|
| 363 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
def test_translate_one_records_error_in_target():
|
| 365 |
state = h._make_state()
|
| 366 |
state["segments"] = _segments("source text")
|
|
@@ -407,10 +422,34 @@ def test_translate_all_reports_completion_status():
|
|
| 407 |
|
| 408 |
event_type, session_id, payload = mock_log.call_args[0]
|
| 409 |
assert event_type == "translate"
|
|
|
|
|
|
|
|
|
|
| 410 |
assert payload["segment_count"] == 2
|
| 411 |
assert payload["segments"] == [{"source": "a", "target": "A"}, {"source": "b", "target": "B"}]
|
| 412 |
|
| 413 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 |
def test_translate_all_reports_errors_in_status():
|
| 415 |
state = h._make_state()
|
| 416 |
state["segments"] = _segments("a", "b")
|
|
|
|
| 357 |
h._translate_one(state, 0, "source text", "api-key", "model-x")
|
| 358 |
event_type, session_id, payload = mock_log.call_args[0]
|
| 359 |
assert event_type == "translate"
|
| 360 |
+
assert payload["backend"] == "openrouter"
|
| 361 |
+
assert payload["model"] == "model-x"
|
| 362 |
+
assert payload["prompt"] == h.read_prompt()
|
| 363 |
assert payload["segments"] == [{"source": "source text", "target": "translated!"}]
|
| 364 |
|
| 365 |
|
| 366 |
+
def test_translate_one_logs_local_backend_without_prompt():
|
| 367 |
+
state = h._make_state()
|
| 368 |
+
state["segments"] = _segments("source text")
|
| 369 |
+
with patch.object(h, "_engine_translate_one", return_value="translated!"), \
|
| 370 |
+
patch.object(h, "using_openrouter", return_value=False), \
|
| 371 |
+
patch.object(h, "log_event") as mock_log:
|
| 372 |
+
h._translate_one(state, 0, "source text", None, "model-x")
|
| 373 |
+
event_type, session_id, payload = mock_log.call_args[0]
|
| 374 |
+
assert payload["backend"] == "local_cpu"
|
| 375 |
+
assert payload["model"] == "billingsmoore/mlotsawa-ground-base"
|
| 376 |
+
assert payload["prompt"] is None
|
| 377 |
+
|
| 378 |
+
|
| 379 |
def test_translate_one_records_error_in_target():
|
| 380 |
state = h._make_state()
|
| 381 |
state["segments"] = _segments("source text")
|
|
|
|
| 422 |
|
| 423 |
event_type, session_id, payload = mock_log.call_args[0]
|
| 424 |
assert event_type == "translate"
|
| 425 |
+
assert payload["backend"] == "openrouter"
|
| 426 |
+
assert payload["model"] == "model-x"
|
| 427 |
+
assert payload["prompt"] == h.read_prompt()
|
| 428 |
assert payload["segment_count"] == 2
|
| 429 |
assert payload["segments"] == [{"source": "a", "target": "A"}, {"source": "b", "target": "B"}]
|
| 430 |
|
| 431 |
|
| 432 |
+
def test_translate_all_logs_local_backend_without_prompt():
|
| 433 |
+
state = h._make_state()
|
| 434 |
+
state["segments"] = _segments("a")
|
| 435 |
+
slot_values = ["a"] + [""] * (h.MAX_SLOTS - 1) + [""] * h.MAX_SLOTS
|
| 436 |
+
|
| 437 |
+
def fake_translate_segments(segments, api_key, model, progress_callback=None, stop=None):
|
| 438 |
+
for seg in segments:
|
| 439 |
+
seg["target"] = seg["source"].upper()
|
| 440 |
+
return segments, []
|
| 441 |
+
|
| 442 |
+
with patch.object(h, "translate_segments", side_effect=fake_translate_segments), \
|
| 443 |
+
patch.object(h, "using_openrouter", return_value=False), \
|
| 444 |
+
patch.object(h, "log_event") as mock_log:
|
| 445 |
+
list(h._translate_all(state, None, "model-x", *slot_values))
|
| 446 |
+
|
| 447 |
+
event_type, session_id, payload = mock_log.call_args[0]
|
| 448 |
+
assert payload["backend"] == "local_cpu"
|
| 449 |
+
assert payload["model"] == "billingsmoore/mlotsawa-ground-base"
|
| 450 |
+
assert payload["prompt"] is None
|
| 451 |
+
|
| 452 |
+
|
| 453 |
def test_translate_all_reports_errors_in_status():
|
| 454 |
state = h._make_state()
|
| 455 |
state["segments"] = _segments("a", "b")
|