Translation Architecture Code Review

Status note (documentation review): This document was found empty during the 2024 documentation audit and has been (re)written from a direct review of the current translation code. It replaces whatever content may have previously existed under this filename.

Overview

Thalabus supports two independent, parallel translation code paths, both reachable from TranslatorService (C#, ThalabusCore/Services/TranslatorService.cs):

  1. External translation API — a dedicated translation model/endpoint (e.g. DeepL-style service), used when TranslationConfiguration.UseTranslationModel is true.
  2. LLM-based translation — routes through the Python server's LLM translation service, used when UseTranslationModel is false (the default).

If the external API path throws an AiException, TranslatorService automatically falls back to the LLM-based path and shows a one-time global warning message ("Fast translator is not available, falling back to slow translator").

Configuration

TranslationConfiguration (ThalabusInterfaces/Data/Llm/LlmSettings.cs, ~line 583) has three JSON-backed properties: - use_translation_model → UseTranslationModel (bool, default false) - model → Model (string, default Constants.Default) - endpoint → Endpoint (string, default empty)

This is read from LlmSettings.TranslationConfiguration via ContainerService.Singleton.LlmSettings.

C# Entry Points

ITranslatorService (ThalabusInterfaces/Services/ITranslatorService.cs) is implemented by TranslatorService, exposing: - TranslateTextAsync(string inputText, string targetLanguageCode, bool disableCache = false, List<string>? lexiconHints = null, string? ontologyId = null) → returns the translated string. - An overload returning a full TranslationResult (ThalabusInterfaces/Data/Translation/TranslationResult.cs) including TranslatedText and SourceLanguage, used when the caller needs the detected source language (the external API path never populates SourceLanguage).

Both entry points: 1. Return the input unchanged for null/whitespace input. 2. Resolve ontology lexicon hints via ResolveLexiconHintsAsync(ontologyId, lexiconHints). 3. Route to TranslateWithExternalApiAsync or TranslateWithLlmAsync/TranslateWithLlmResponseAsync depending on UseTranslationModel. 4. Apply NormalizeSwissGermanOrthography(translatedText, targetLanguageCode), which replaces ß with ss only when targetLanguageCode is de-CH (Swiss German orthography does not use the ß character). 5. On error, log via Log.Exception/Log.Error and return an "Error translating text: ..." string rather than throwing, so callers always get a text result back.

Concurrency is capped globally via a static SemaphoreSlim (_globalTranslationSemaphore, capacity Constants.MaxConcurrentTranslationRequests) shared across all translation calls.

Python Server Side

TranslationService (PythonServer/src/services/translation_service.py) implements TranslationInterface and is exposed over HTTP via PythonServer/src/api/v1/translation.py (POST /translation/translate).

Translation Cache

TranslationService caches translations in Neo4j using nodes labeled TranslationCache: - Cache key is computed via _compute_cache_key(text, target_language): f"{target_language}:{sha256(text)}:{len(text)}", falling back to a truncated-text hash on error. - _cache_read(key, target_language) matches (t:TranslationCache {key, target_language}) and returns translated_text/source_language (normalized via _normalize_swiss_orthography / _normalize_source_language). - _cache_write(key, target_language, translated_text, source_language) uses MERGE (t:TranslationCache {key}) SET ... to upsert the cache node. - TranslationCache nodes have a range index on key (PythonServer/src/core/base/neo4j_indexes.py, translation_cache_key) — not on id (there is no id property on this node type). - The cache can be manually erased from the UI: ThalabusWeb/Pages/SettingsPages/SettingsCache.razor → "Erase Translation Cache" button → Neo4jUtilities.DeleteAllTranslationCacheEntries() (Neo4jORM/DataAccess/Neo4jUtilities.cs), which deletes all TranslationCache nodes in batches via apoc.periodic.commit.

AgentTranslator

PythonServer/src/core/agents/AgentTranslator.py is a separate, LLM-driven agent (not the same code path as TranslationService) used to translate lists of UI/system messages into English, French, German, and Italian simultaneously (e.g. for multilingual hints and system messages). It uses MODEL_LOWINTELLIGENCE as its default model and understands two special inline markers in source text: - HINT=[[...]] — content inside is itself translated to English. - PRESERVE_ENGLISH=[[...]] — content inside is preserved verbatim in English in all four output languages, and the marker/brackets are stripped from the output.

This agent is distinct from the per-request TranslationService/TranslateTextAsync path described above and is not backed by the TranslationCache Neo4j cache.

Vatican Skillset (Document Scan-and-Translate)

PythonServer/src/core/extensions/skillsets/Vatican/ScanAndTranslate*.py implement a separate, document-oriented OCR-then-translate pipeline used by a specific skillset, distinct from the general-purpose TranslationService API. This pipeline is not covered in detail here; consult the skillset's own code and the active/skills documentation topic for skillset conventions.