For the models available for Argos Translate, I ob...

Criado em: 16 de fevereiro de 2025

Respondido usando O3 Mini High por Chat01

Pergunta

For the models available for Argos Translate, I obtained this word table from the WEB that was converted to txt:
"""

From
To
From code
To code
Albanian
English
sq
en
Arabic
English
ar
en
Azerbaijani
English
az
en
Basque
English
eu
en
Bengali
English
bn
en
Bulgarian
English
bg
en
Catalan
English
ca
en
Chinese (traditional)
English
zt
en
Chinese
English
zh
en
Czech
English
cs
en
Danish
English
da
en
Dutch
English
nl
en
English
Albanian
en
sq
English
Arabic
en
ar
English
Azerbaijani
en
az
English
Basque
en
eu
English
Bengali
en
bn
English
Bulgarian
en
bg
English
Catalan
en
ca
English
Chinese
en
zh
English
Chinese (traditional)
en
zt
English
Czech
en
cs
English
Danish
en
da
English
Dutch
en
nl
English
Esperanto
en
eo
English
Estonian
en
et
English
Finnish
en
fi
English
French
en
fr
English
Galician
en
gl
English
German
en
de
English
Greek
en
el
English
Hebrew
en
he
English
Hindi
en
hi
English
Hungarian
en
hu
English
Indonesian
en
id
English
Irish
en
ga
English
Italian
en
it
English
Japanese
en
ja
English
Korean
en
ko
English
Latvian
en
lv
English
Lithuanian
en
lt
English
Malay
en
ms
English
Norwegian
en
nb
English
Persian
en
fa
English
Polish
en
pl
English
Portuguese
en
pt
English
Romanian
en
ro
English
Russian
en
ru
English
Slovak
en
sk
English
Slovenian
en
sl
English
Spanish
en
es
English
Swedish
en
sv
English
Tagalog
en
tl
English
Thai
en
th
English
Turkish
en
tr
English
Ukranian
en
uk
English
Urdu
en
ur
Esperanto
English
eo
en
Estonian
English
et
en
Finnish
English
fi
en
French
English
fr
en
Galician
English
gl
en
German
English
de
en
Greek
English
el
en
Hebrew
English
he
en
Hindi
English
hi
en
Hungarian
English
hu
en
Indonesian
English
id
en
Irish
English
ga
en
Italian
English
it
en
Japanese
English
ja
en
Korean
English
ko
en
Latvian
English
lv
en
Lithuanian
English
lt
en
Malay
English
ms
en
Norwegian
English
nb
en
Persian
English
fa
en
Polish
English
pl
en
Portuguese
English
pt
en
Portuguese
Spanish
pt
es
Romanian
English
ro
en
Russian
English
ru
en
Slovak
English
sk
en
Slovenian
English
sl
en
Spanish
English
es
en
Spanish
Portuguese
es
pt
Swedish
English
sv
en
Tagalog
English
tl
en
Thai
English
th
en
Turkish
English
tr
en
Ukrainian
English
uk
en
Urdu
English
ur
en

"""

Note that the formatting is a bit strange for a table converted to txt by a Text Editor. Note that the first four txt lines below ("From", "To", "From code" and "To code") are from the table header. Lines 5th to 8th make up the first line of the table, 9th to 12th make up the second line of the table, and so on.

Each position of the four txt elements/lines corresponds to the columns in each line of the table, they are the respective values ​​of origin, target, source code and target code, four at a time.

Did you understand? I don't know if I was able to explain it well.


I tried to implement this in Delphi, I made a draft below, see:

text
ModelList: array[0..91, 0..3] of string = ( // Block 1 (lines 0 to 11) ('Albanian', 'English', 'sq', 'en'), ('Arabic', 'English', 'ar', 'en'), ('Azerbaijani', 'English', 'az', 'en'), ('Basque', 'English', 'eu', 'en'), ('Bengali', 'English', 'bn', 'en'), ('Bulgarian', 'English', 'bg', 'en'), ('Catalan', 'English', 'ca', 'en'), ('Chinese (traditional)', 'English', 'zt', 'en'), ('Chinese', 'English', 'zh', 'en'), ('Czech', 'English', 'cs', 'en'), ('Danish', 'English', 'da', 'en'), ('Dutch', 'English', 'nl', 'en'), // Block 2 (lines 12 to 56): translations where English is the source language ('English', 'Albanian', 'en', 'sq'), ('English', 'Arabic', 'en', 'ar'), ('English', 'Azerbaijani', 'en', 'az'), ('English', 'Basque', 'en', 'eu'), ('English', 'Bengali', 'en', 'bn'), ('English', 'Bulgarian', 'en', 'bg'), ('English', 'Catalan', 'en', 'ca'), ('English', 'Chinese', 'en', 'zh'), ('English', 'Chinese (traditional)', 'en', 'zt'), ('English', 'Czech', 'en', 'cs'), ('English', 'Danish', 'en', 'da'), ('English', 'Dutch', 'en', 'nl'), ('English', 'Esperanto', 'en', 'eo'), ('English', 'Estonian', 'en', 'et'), ('English', 'Finnish', 'en', 'fi'), ('English', 'French', 'en', 'fr'), ('English', 'Galician', 'en', 'gl'), ('English', 'German', 'en', 'de'), ('English', 'Greek', 'en', 'el'), ('English', 'Hebrew', 'en', 'he'), ('English', 'Hindi', 'en', 'hi'), ('English', 'Hungarian', 'en', 'hu'), ('English', 'Indonesian', 'en', 'id'), ('English', 'Irish', 'en', 'ga'), ('English', 'Italian', 'en', 'it'), ('English', 'Japanese', 'en', 'ja'), ('English', 'Korean', 'en', 'ko'), ('English', 'Latvian', 'en', 'lv'), ('English', 'Lithuanian', 'en', 'lt'), ('English', 'Malay', 'en', 'ms'), ('English', 'Norwegian', 'en', 'nb'), ('English', 'Persian', 'en', 'fa'), ('English', 'Polish', 'en', 'pl'), ('English', 'Portuguese', 'en', 'pt'), ('English', 'Romanian', 'en', 'ro'), ('English', 'Russian', 'en', 'ru'), ('English', 'Slovak', 'en', 'sk'), ('English', 'Slovenian', 'en', 'sl'), ('English', 'Spanish', 'en', 'es'), ('English', 'Swedish', 'en', 'sv'), ('English', 'Tagalog', 'en', 'tl'), ('English', 'Thai', 'en', 'th'), ('English', 'Turkish', 'en', 'tr'), ('English', 'Ukranian', 'en', 'uk'), ('English', 'Urdu', 'en', 'ur'), // Block 3 (lines 57 to 91): translations from other languages ​​to English ('Esperanto', 'English', 'eo', 'en'), ('Estonian', 'English', 'et', 'en'), ('Finnish', 'English', 'fi', 'en'), ('French', 'English', 'fr', 'en'), ('Galician', 'English', 'gl', 'en'), ('German', 'English', 'de', 'en'), ('Greek', 'English', 'el', 'en'), ('Hebrew', 'English', 'he', 'en'), ('Hindi', 'English', 'hi', 'en'), ('Hungarian', 'English', 'hu', 'en'), ('Indonesian', 'English', 'id', 'en'), ('Irish', 'English', 'ga', 'en'), ('Italian', 'English', 'it', 'en'), ('Japanese', 'English', 'ja', 'en'), ('Korean', 'English', 'ko', 'en'), ('Latvian', 'English', 'lv', 'en'), ('Lithuanian', 'English', 'lt', 'en'), ('Malay', 'English', 'ms', 'en'), ('Norwegian', 'English', 'nb', 'en'), ('Persian', 'English', 'fa', 'en'), ('Polish', 'English', 'pl', 'en'), ('Portuguese', 'English', 'pt', 'en'), ('Portuguese', 'Spanish', 'pt', 'es'), ('Romanian', 'English', 'ro', 'en'), ('Russian', 'English', 'ru', 'en'), ('Slovak', 'English', 'sk', 'en'), ('Slovenian', 'English', 'sl', 'en'), ('Spanish', 'English', 'es', 'en'), ('Spanish', 'Portuguese', 'es', 'pt'), ('Swedish', 'English', 'sv', 'en'), ('Tagalog', 'English', 'tl', 'en'), ('Thai', 'English', 'th', 'en'), ('Turkish', 'English', 'tr', 'en'), ('Ukrainian', 'English', 'uk', 'en'), ('Urdu', 'English', 'ur', 'en') );

I will explain the strategy that I need to implement now. If you notice that not all languages ​​are available to be translated from one to another, then the strategy to be adopted would need to be Language Pivoting, with English as the intermediate language for the pivot, so that we can convert between all the other languages.

Did you understand? I don't know if I explained it well.

I will try to explain in more detail:

Develop a way in Delphi code of automatically translate between languages even if a direct translation model isn't installed. It does this by "pivoting" through an intermediate language (e.g., English). If you have es -> en and en -> fr models, it can translate es -> fr by chaining these two translations.

If the translation requires pivoting, then the translation procedure should be executed twice. If there is a direct translation model, then the translation should be just one call to the translation procedure.

Our main focus is to look at the list of models (column 0 means the source language, column 1 means the target language, column 2 means the source language code, column 3 means the target language code). Then the code should look for the combination that represents the correct model for the chosen translation. If there is no direct combination, then the pivoting and double translation strategy with an intermediate language between the source and target languages ​​comes into play.

See, I tried to implement this by writing the modifications myself to pP4DArgosTranslate.dpr:

text
program pP4DArgosTranslate; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Diagnostics, Windows, PythonEngine, VarPyth, System.Classes, System.IOUtils, // For TDirectory, TPath System.Net.HttpClient, // For IHTTPResponse System.Net.URLClient, // For URL Client System.Net.HttpClientComponent, // For TNetHTTPClient System.Zip; // For TZipFile var PythonEngine: TPythonEngine; PythonModule: TPythonModule; // Use TPythonModule for better structure PythonHome: string; PyFuncTranslate: PPyObject; // Keep reference to the translation function const // Initialization script (initialization.py) EmbeddedPythonScriptInit: string = '# initialization.py' + sLineBreak + 'import ctranslate2' + sLineBreak + 'import sentencepiece as spm' + sLineBreak + 'from sacremoses.normalize import MosesPunctNormalizer' + sLineBreak + 'import os' + sLineBreak + 'import logging' + sLineBreak + '' + sLineBreak + 'logging.basicConfig(level=logging.WARNING)' + sLineBreak + 'logger = logging.getLogger(__name__)' + sLineBreak + '' + sLineBreak + '# --- Initialization Logic ---' + sLineBreak + 'def init_translator(source_lang, target_lang):' + sLineBreak + ' global normalizer, sp_processor, translator, MODEL_DIRECTORY, SP_MODEL_FILE' + sLineBreak + '' + sLineBreak + ' MODEL_NAME = f"translate-{source_lang}_{target_lang}-1_0"' + sLineBreak + ' EXTRACT_FOLDER = "argos_models"' + sLineBreak + ' ' + sLineBreak + ' # --- Dynamically find the model directory (same as before) ---' + sLineBreak + ' extracted_folder_path = os.path.join(EXTRACT_FOLDER, f"{MODEL_NAME}_extracted")' + sLineBreak + ' subfolders = [f for f in os.listdir(extracted_folder_path) if os.path.isdir(os.path.join(extracted_folder_path, f))]' + sLineBreak + ' if not subfolders:' + sLineBreak + ' raise FileNotFoundError(f"No subfolders found in {extracted_folder_path}.")' + sLineBreak + ' if len(subfolders) > 1:' + sLineBreak + ' logger.warning(f"Multiple subfolders found in {extracted_folder_path}. Using the first one: {subfolders[0]}.")' + sLineBreak + ' model_subfolder_name = subfolders[0]' + sLineBreak + ' MODEL_DIRECTORY = os.path.join(extracted_folder_path, model_subfolder_name, "model")' + sLineBreak + ' SP_MODEL_FILE = os.path.join(extracted_folder_path, model_subfolder_name, "sentencepiece.model")' + sLineBreak + ' # --- End dynamic model directory finding ---' + sLineBreak + '' + sLineBreak + ' normalizer = MosesPunctNormalizer(lang=source_lang)' + sLineBreak + ' sp_processor = spm.SentencePieceProcessor(model_file=SP_MODEL_FILE)' + sLineBreak + ' translator = ctranslate2.Translator(MODEL_DIRECTORY, device="cpu")' + sLineBreak + ' logger.info(f"Argos Translate components initialized for {{source_lang}}->{{target_lang}}.")' + sLineBreak; // Optimized translation script (translation_optimized.py) - Now RETURNS a LIST EmbeddedPythonScriptTranslateOptimized: string = '# translation_optimized.py' + sLineBreak + 'import logging' + sLineBreak + '' + sLineBreak + 'logger = logging.getLogger(__name__)' + sLineBreak + '' + sLineBreak + 'def perform_translation_optimized(texts_to_translate):' + sLineBreak + // Accepts a LIST of texts ' """Translates a LIST of texts using pre-initialized Argos Translate components."""' + sLineBreak + ' global normalizer, sp_processor, translator' + sLineBreak + ' translated_texts = []' + sLineBreak + // Initialize a list to store results ' for text_to_translate in texts_to_translate:' + sLineBreak + // Iterate through the list ' try:' + sLineBreak + ' normalized_text = normalizer.normalize(text_to_translate)' + sLineBreak + ' tokens = sp_processor.encode(normalized_text, out_type=str)' + sLineBreak + ' translation_result = translator.translate_batch([tokens], beam_size=1)' + sLineBreak + ' translated_tokens = translation_result[0].hypotheses[0]' + sLineBreak + ' translated_text = sp_processor.decode(translated_tokens)' + sLineBreak + ' translated_texts.append(translated_text)' + sLineBreak + // Add to result list ' except Exception as e:' + sLineBreak + ' translated_texts.append(f"Translation Error: {{str(e)}}")' + sLineBreak + // Add error to result list ' return translated_texts' + sLineBreak; // Return the ENTIRE list of translated texts // --- Function to Check and Create Model Directory --- function CheckAndCreateModelDirectory(SourceLang, TargetLang: string): Boolean; var AppPath: string; ExtractFolder: string; begin // 1. Get the application's directory. AppPath := ExtractFilePath(ParamStr(0)); // 2. Check/create the "argos_models" directory. ExtractFolder := TPath.Combine(AppPath, 'argos_models'); if not TDirectory.Exists(ExtractFolder) then begin try TDirectory.CreateDirectory(ExtractFolder); Result := False; // Indicate that the main folder did not exist. except on E: Exception do begin Writeln('Error creating argos_models directory: ', E.Message); Result := False; // Indicate failure Exit; end; end; end else Result := False; // Initially assume model is not installed end; // --- Function to Download a File --- function DownloadFile(const AURL, AFilename: string): Boolean; var HttpClient: TNetHTTPClient; Response: IHTTPResponse; FileStream: TFileStream; begin Result := False; HttpClient := TNetHTTPClient.Create(nil); try // Set the User-Agent header to mimic a browser. THIS IS CRUCIAL. HttpClient.UserAgent := 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'; try Response := HttpClient.Get(AURL); if Response.StatusCode = 200 then begin FileStream := TFileStream.Create(AFilename, fmCreate); // Use TFileStream try FileStream.CopyFrom(Response.ContentStream, 0); // Use CopyFrom Result := True; finally FileStream.Free; end; end else begin Writeln('HTTP Error: ', Response.StatusCode, ' - ', Response.StatusText); Exit; // Add exit here end; except on E: Exception do begin Writeln('Error downloading file: ', E.Message); Exit; // Add exit here also end; end; finally HttpClient.Free; end; end; // --- Function to Extract a Zip File --- procedure ExtractZipFile(const AZipFile, ADestinationDir: string); var ZipFile: TZipFile; ExtractPath: string; begin ZipFile := TZipFile.Create; try ZipFile.Open(AZipFile, zmRead); try // Create the _extracted folder ExtractPath := TPath.Combine(ADestinationDir, TPath.GetFileNameWithoutExtension(AZipFile) + '_extracted'); if not TDirectory.Exists(ExtractPath) then TDirectory.CreateDirectory(ExtractPath); // Extract to the _extracted folder ZipFile.ExtractAll(ExtractPath); finally ZipFile.Close; end; except on E: Exception do begin Writeln('Error extracting zip file: ', E.Message); // Consider deleting the partially extracted folder here in case of an error end; end; end; // --- CUDA Setup (Ideally, do this only ONCE per application run) --- procedure SetupCUDAEnvironment; var OldPath: string; begin SetEnvironmentVariable('CUDA_PATH', 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4'); OldPath := GetEnvironmentVariable('PATH'); SetEnvironmentVariable('PATH', PChar(OldPath + ';C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4\bin' + ';C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4\libnvvp')); // These loads might not be necessary, depending on how ctranslate2 is set up. // They *are* necessary if you want to force loading of specific DLLs. LoadLibrary (PChar('C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\torch\lib\cudnn_graph64_9.dll') ); LoadLibrary (PChar('C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\ctranslate2\ctranslate2.dll') ); LoadLibrary (PChar('C:\Windows\system32\DriverStore\FileRepository\nvdmui.inf_amd64_fdc98cdf10f69918\nvcuda64.dll') ); end; procedure InitializeCUDAContext(PythonEngine: TPythonEngine); begin PythonEngine.ExecString(AnsiString('import torch;' + sLineBreak + 'torch.cuda.init();' + sLineBreak + 'print("CUDA Device:", torch.cuda.get_device_name(0))' + sLineBreak)); PythonEngine.CheckError; // Always check for errors after Python calls end; // Dummy function, just to demonstrate the pattern. Not actually used. function DoNothing(Self, Args: PPyObject): PPyObject; cdecl; begin Result := GetPythonEngine.ReturnNone; end; procedure CreatePyEngine(SourceLang, TargetLang: string); var PyArgs: PPyObject; PyInitFunc: PPyObject; PyMainModule: PPyObject; // Hold a reference to __main__ ModelName: string; // Store Model Name DownloadURL: string; // Store the download URL DownloadPath: string; // Store the download Path begin PythonEngine := TPythonEngine.Create(nil); PythonEngine.Name := 'PythonEngine'; PythonEngine.DllName := 'C:\Users\user\AppData\Local\Programs\Python\Python39\python39.dll'; // Your path here PythonHome := 'C:\Users\user\AppData\Local\Programs\Python\Python39'; // Your path here PythonEngine.SetPythonHome(PWideChar(PythonHome)); PythonEngine.LoadDll; // Create the module *before* running any scripts PythonModule := TPythonModule.Create(nil); PythonModule.Engine := PythonEngine; PythonModule.ModuleName := 'delphi_module'; // Good practice // Add a dummy method (optional, but good for consistency) // PythonModule.AddMethod('do_nothing', @DoNothing, 'A dummy method.'); // --- 1. Execute Initialization Script --- PythonEngine.ExecString(PAnsiChar(AnsiString(EmbeddedPythonScriptInit))); PythonEngine.CheckError; // Get the __main__ module PyMainModule := PythonEngine.GetMainModule; if not Assigned(PyMainModule) then raise Exception.Create('Could not get __main__ module.'); PythonEngine.CheckError; // Get the init_translator function from __main__ PyInitFunc := PythonEngine.PyObject_GetAttrString(PyMainModule, 'init_translator'); if not Assigned(PyInitFunc) then begin PythonEngine.Py_XDECREF(PyMainModule); // Decref __main__ raise Exception.Create('init_translator function not found'); end; PythonEngine.CheckError; // Prepare arguments for init_translator PyArgs := PythonEngine.PyTuple_New(2); PythonEngine.CheckError; try PythonEngine.PyTuple_SetItem(PyArgs, 0, PythonEngine.PyUnicode_FromString(PAnsiChar(UTF8String(SourceLang)))); PythonEngine.CheckError; PythonEngine.PyTuple_SetItem(PyArgs, 1, PythonEngine.PyUnicode_FromString(PAnsiChar(UTF8String(TargetLang)))); PythonEngine.CheckError; // Call init_translator. It returns None, so we don't need to manage the result. PythonEngine.PyObject_CallObject(PyInitFunc, PyArgs); PythonEngine.CheckError; finally PythonEngine.Py_XDECREF(PyArgs); // Clean up the arguments tuple PythonEngine.Py_XDECREF(PyInitFunc); // Clean up reference to the init function end; // --- 2. Load Translation Script (for later use) --- PythonEngine.ExecString (PAnsiChar(AnsiString(EmbeddedPythonScriptTranslateOptimized))); PythonEngine.CheckError; // Get a reference to the translation function. GOOD: More descriptive name. PyFuncTranslate := PythonEngine.PyObject_GetAttrString(PyMainModule, 'perform_translation_optimized'); // from __main__ PythonEngine.Py_XDECREF(PyMainModule); // Done with __main__ module now if not Assigned(PyFuncTranslate) then begin raise Exception.Create ('Could not find perform_translation_optimized function in __main__.'); end; PythonEngine.CheckError; // No need to initialize PythonModule here, it's done above. end; procedure DestroyEngine; begin if Assigned(PyFuncTranslate) then PythonEngine.Py_XDECREF(PyFuncTranslate); // Clean up the function reference. ESSENTIAL. PythonModule.Free; // Free the module *before* the engine PythonEngine.Free; end; function CallOptimizedTranslate(const TextToTranslate: string): string; var PyArgsTuple: PPyObject; PyTextList: PPyObject; PyResultList: PPyObject; PySingleTranslation: PPyObject; begin // 1. Create a Python list containing the single text to translate PyTextList := PythonEngine.PyList_New(1); PythonEngine.CheckError; try PythonEngine.PyList_SetItem(PyTextList, 0, PythonEngine.PyUnicode_FromString (PAnsiChar(UTF8String(TextToTranslate)))); PythonEngine.CheckError; // 2. Prepare arguments - now pass the list PyArgsTuple := PythonEngine.PyTuple_New(1); PythonEngine.CheckError; try PythonEngine.PyTuple_SetItem(PyArgsTuple, 0, PyTextList); // Pass the Python list as argument. STEALS a reference. PythonEngine.CheckError; // 3. Call Python function. GOOD: More descriptive name. PyResultList := PythonEngine.PyObject_CallObject(PyFuncTranslate, PyArgsTuple); PythonEngine.CheckError; // Check for errors *immediately* after the call finally PythonEngine.Py_XDECREF(PyArgsTuple); // Clean up the arguments tuple. ESSENTIAL. // DO NOT DECREF PyTextList here! It was STOLEN by PyTuple_SetItem end; if not Assigned(PyResultList) then begin // Handle the case where the Python function returned None (an error occurred) Result := 'Error: No result returned from Python.'; Exit; // Exit the function end; try // Add try finally for PyResultList cleanup // 4. Expect a Python LIST back (even if it contains only one translation) if not PythonEngine.PyList_Check(PyResultList) then begin Result := 'Error: Unexpected result from Python - not a list.'; Exit; // Exit the function end; if PythonEngine.PyList_Size(PyResultList) = 0 then begin Result := 'Error: Empty result list from Python.'; Exit; // Exit the function end; // 5. Get the string out. PySingleTranslation := PythonEngine.PyList_GetItem(PyResultList, 0); // Get the first item. NO INCREF NEEDED (borrowed reference) if not Assigned(PySingleTranslation) then begin Result := 'Error: could not extract translated item.'; Exit; end; // No need to Py_INCREF PySingleTranslation - it's a BORROWED reference Result := UTF8ToString(PythonEngine.PyUnicode_AsUTF8(PySingleTranslation) ); // Extract string. No need to DECREF the result of PyUnicode_AsUTF8 PythonEngine.CheckError; finally PythonEngine.Py_XDECREF(PyResultList); // Clean up the result list. ESSENTIAL. end; finally // No need to clean PyTextList, reference was stolen by PyTuple_SetItem end; end; // --- Function to find the translation path (direct or pivoted) --- function FindTranslationPath(const SourceLang, TargetLang: string; const ModelList: TArray < TArray < string >> ): TStringList; var i, j: Integer; begin Result := TStringList.Create; try // Check for direct translation for i := Low(ModelList) to High(ModelList) do begin if (Length(ModelList[i]) > 3) and (ModelList[i][2] = SourceLang) and (ModelList[i][3] = TargetLang) then begin Result.Add(Format('(%s,%s)', [SourceLang, TargetLang])); Exit; // Direct path found end; end; // Check for English pivot for i := Low(ModelList) to High(ModelList) do begin if (Length(ModelList[i]) > 3) and (ModelList[i][2] = SourceLang) and (ModelList[i][3] = 'en') then begin for j := Low(ModelList) to High(ModelList) do begin if (Length(ModelList[j]) > 3) and (ModelList[j][2] = 'en') and (ModelList[j][3] = TargetLang) then begin Result.Add(Format('(%s,%s)', [SourceLang, 'en'])); Result.Add(Format('(%s,%s)', ['en', TargetLang])); Exit; // Indirect path found end; end; end; end; except Result.Free; raise; end; end; const ModelList: TArray < TArray < string >> = [ // Block 1 (lines 0 to 11) ['Albanian', 'English', 'sq', 'en'], ['Arabic', 'English', 'ar', 'en'], ['Azerbaijani', 'English', 'az', 'en'], ['Basque', 'English', 'eu', 'en'], ['Bengali', 'English', 'bn', 'en'], ['Bulgarian', 'English', 'bg', 'en'], ['Catalan', 'English', 'ca', 'en'], ['Chinese (traditional)', 'English', 'zt', 'en'], ['Chinese', 'English', 'zh', 'en'], ['Czech', 'English', 'cs', 'en'], ['Danish', 'English', 'da', 'en'], ['Dutch', 'English', 'nl', 'en'], // Block 2 (lines 12 to 56): translations where English is the source language ['English', 'Albanian', 'en', 'sq'], ['English', 'Arabic', 'en', 'ar'], ['English', 'Azerbaijani', 'en', 'az'], ['English', 'Basque', 'en', 'eu'], ['English', 'Bengali', 'en', 'bn'], ['English', 'Bulgarian', 'en', 'bg'], ['English', 'Catalan', 'en', 'ca'], ['English', 'Chinese', 'en', 'zh'], ['English', 'Chinese (traditional)', 'en', 'zt'], ['English', 'Czech', 'en', 'cs'], ['English', 'Danish', 'en', 'da'], ['English', 'Dutch', 'en', 'nl'], ['English', 'Esperanto', 'en', 'eo'], ['English', 'Estonian', 'en', 'et'], ['English', 'Finnish', 'en', 'fi'], ['English', 'French', 'en', 'fr'], ['English', 'Galician', 'en', 'gl'], ['English', 'German', 'en', 'de'], ['English', 'Greek', 'en', 'el'], ['English', 'Hebrew', 'en', 'he'], ['English', 'Hindi', 'en', 'hi'], ['English', 'Hungarian', 'en', 'hu'], ['English', 'Indonesian', 'en', 'id'], ['English', 'Irish', 'en', 'ga'], ['English', 'Italian', 'en', 'it'], ['English', 'Japanese', 'en', 'ja'], ['English', 'Korean', 'en', 'ko'], ['English', 'Latvian', 'en', 'lv'], ['English', 'Lithuanian', 'en', 'lt'], ['English', 'Malay', 'en', 'ms'], ['English', 'Norwegian', 'en', 'nb'], ['English', 'Persian', 'en', 'fa'], ['English', 'Polish', 'en', 'pl'], ['English', 'Portuguese', 'en', 'pt'], ['English', 'Romanian', 'en', 'ro'], ['English', 'Russian', 'en', 'ru'], ['English', 'Slovak', 'en', 'sk'], ['English', 'Slovenian', 'en', 'sl'], ['English', 'Spanish', 'en', 'es'], ['English', 'Swedish', 'en', 'sv'], ['English', 'Tagalog', 'en', 'tl'], ['English', 'Thai', 'en', 'th'], ['English', 'Turkish', 'en', 'tr'], ['English', 'Ukranian', 'en', 'uk'], ['English', 'Urdu', 'en', 'ur'], // Block 3 (lines 57 to 91): translations from other languages ​​to English ['Esperanto', 'English', 'eo', 'en'], ['Estonian', 'English', 'et', 'en'], ['Finnish', 'English', 'fi', 'en'], ['French', 'English', 'fr', 'en'], ['Galician', 'English', 'gl', 'en'], ['German', 'English', 'de', 'en'], ['Greek', 'English', 'el', 'en'], ['Hebrew', 'English', 'he', 'en'], ['Hindi', 'English', 'hi', 'en'], ['Hungarian', 'English', 'hu', 'en'], ['Indonesian', 'English', 'id', 'en'], ['Irish', 'English', 'ga', 'en'], ['Italian', 'English', 'it', 'en'], ['Japanese', 'English', 'ja', 'en'], ['Korean', 'English', 'ko', 'en'], ['Latvian', 'English', 'lv', 'en'], ['Lithuanian', 'English', 'lt', 'en'], ['Malay', 'English', 'ms', 'en'], ['Norwegian', 'English', 'nb', 'en'], ['Persian', 'English', 'fa', 'en'], ['Polish', 'English', 'pl', 'en'], ['Portuguese', 'English', 'pt', 'en'], ['Portuguese', 'Spanish', 'pt', 'es'], ['Romanian', 'English', 'ro', 'en'], ['Russian', 'English', 'ru', 'en'], ['Slovak', 'English', 'sk', 'en'], ['Slovenian', 'English', 'sl', 'en'], ['Spanish', 'English', 'es', 'en'], ['Spanish', 'Portuguese', 'es', 'pt'], ['Swedish', 'English', 'sv', 'en'], ['Tagalog', 'English', 'tl', 'en'], ['Thai', 'English', 'th', 'en'], ['Turkish', 'English', 'tr', 'en'], ['Ukrainian', 'English', 'uk', 'en'], ['Urdu', 'English', 'ur', 'en']]; var TranslatedText: string; TotalStopwatch: TStopwatch; CreateEngineTime, TranslationTime, DestroyEngineTime: Int64; SourceLanguage, TargetLanguage: string; OriginalText: string; UserInputText: string; UserSourceLang, UserSourceLang1, UserSourceLang2: string; UserTargetLang, UserTargetLang1, UserTargetLang2: string; ModelInstalled: Boolean; ModelName: string; // Store Model Name DownloadURL: string; // Store the download URL DownloadPath: string; // Store the download Path AppPath: string; ExtractFolder: string; ChosenModels: TStringList; PathParts: TArray<string>; begin try MaskFPUExceptions(True); // Do this very early! TotalStopwatch := TStopwatch.StartNew; // Ideally, CUDA setup should be done only ONCE for the entire application. // If you create multiple PythonEngine instances, you only need to do this once. SetupCUDAEnvironment; CreateEngineTime := 0; var EngineTimer := TStopwatch.StartNew; Writeln('=== Interactive Translation ==='); Writeln('Enter source language code (e.g., ''en'', ''pt'', ''es''):'); Readln(UserSourceLang); Writeln('Enter target language code (e.g., ''en'', ''pt'', ''es''):'); Readln(UserTargetLang); ChosenModels := TStringList.Create; ChosenModels := FindTranslationPath(UserSourceLang, UserTargetLang, ModelList); case ChosenModels.Count of 0: // Direct translation begin raise Exception.Create('No translation path found for ' + UserSourceLang + ' -> ' + UserTargetLang); end; 2: begin // Extract source and target from the path string "(source,target)" PathParts := ChosenModels[0].Split([',', '(', ')']); SourceLanguage := Trim(PathParts[1]); TargetLanguage := Trim(PathParts[2]); UserSourceLang1 := SourceLanguage; UserTargetLang1 := TargetLanguage; // Construct the model name and paths AppPath := ExtractFilePath(ParamStr(0)); ModelName := Format('translate-%s_%s-1_0', [UserSourceLang1, UserTargetLang1]); ExtractFolder := TPath.Combine(AppPath, 'argos_models'); DownloadPath := TPath.Combine(ExtractFolder, ModelName + '.argosmodel'); // --- Check if the model directory exists BEFORE creating the Python engine --- // Only check if argos_models main folder exists ModelInstalled := CheckAndCreateModelDirectory(UserSourceLang1, UserTargetLang1); if not ModelInstalled then begin Writeln('Model directory does not exist.'); // --- Download and Install Model (Delphi Implementation) --- DownloadURL := Format('https://argos-net.com/v1/%s.argosmodel', [ModelName]); // Corrected URL if DownloadFile(DownloadURL, DownloadPath) then begin Writeln('Model downloaded successfully.'); ExtractZipFile(DownloadPath, ExtractFolder); // Extract to argos_models Writeln('Model installed successfully.'); // Now check that the model exists, after downloading and extracting. ModelInstalled := TDirectory.Exists(TPath.Combine(ExtractFolder, ModelName + '_extracted')); if not ModelInstalled then begin Writeln('Error: Model installation check failed. Exiting.'); Writeln('Press Enter to exit...'); Readln; Exit; end else begin // --- Delete the .argosmodel file --- try TFile.Delete(DownloadPath); Writeln('Model file deleted successfully.'); except on E: Exception do Writeln('Warning: Could not delete model file: ', E.Message); end; end; end else begin Writeln('Error: Model download failed. Exiting.'); Writeln('Press Enter to exit...'); Readln; Exit; // Exit if download failed. end; end; // Extract source and target from the path string "(source,target)" PathParts := ChosenModels[1].Split([',', '(', ')']); SourceLanguage := Trim(PathParts[1]); TargetLanguage := Trim(PathParts[2]); UserSourceLang2 := SourceLanguage; UserTargetLang2 := TargetLanguage; // Construct the model name and paths AppPath := ExtractFilePath(ParamStr(0)); ModelName := Format('translate-%s_%s-1_0', [UserSourceLang2, UserTargetLang2]); ExtractFolder := TPath.Combine(AppPath, 'argos_models'); DownloadPath := TPath.Combine(ExtractFolder, ModelName + '.argosmodel'); // --- Check if the model directory exists BEFORE creating the Python engine --- // Only check if argos_models main folder exists ModelInstalled := CheckAndCreateModelDirectory(UserSourceLang2, UserTargetLang2); if not ModelInstalled then begin Writeln('Model directory does not exist.'); // --- Download and Install Model (Delphi Implementation) --- DownloadURL := Format('https://argos-net.com/v1/%s.argosmodel', [ModelName]); // Corrected URL if DownloadFile(DownloadURL, DownloadPath) then begin Writeln('Model downloaded successfully.'); ExtractZipFile(DownloadPath, ExtractFolder); // Extract to argos_models Writeln('Model installed successfully.'); // Now check that the model exists, after downloading and extracting. ModelInstalled := TDirectory.Exists(TPath.Combine(ExtractFolder, ModelName + '_extracted')); if not ModelInstalled then begin Writeln('Error: Model installation check failed. Exiting.'); Writeln('Press Enter to exit...'); Readln; Exit; end else begin // --- Delete the .argosmodel file --- try TFile.Delete(DownloadPath); Writeln('Model file deleted successfully.'); except on E: Exception do Writeln('Warning: Could not delete model file: ', E.Message); end; end; end else begin Writeln('Error: Model download failed. Exiting.'); Writeln('Press Enter to exit...'); Readln; Exit; // Exit if download failed. end; end; end else begin // Construct the model name and paths AppPath := ExtractFilePath(ParamStr(0)); ModelName := Format('translate-%s_%s-1_0', [UserSourceLang, UserTargetLang]); ExtractFolder := TPath.Combine(AppPath, 'argos_models'); DownloadPath := TPath.Combine(ExtractFolder, ModelName + '.argosmodel'); // --- Check if the model directory exists BEFORE creating the Python engine --- // Only check if argos_models main folder exists ModelInstalled := CheckAndCreateModelDirectory(UserSourceLang, UserTargetLang); if not ModelInstalled then begin Writeln('Model directory does not exist.'); // --- Download and Install Model (Delphi Implementation) --- DownloadURL := Format('https://argos-net.com/v1/%s.argosmodel', [ModelName]); // Corrected URL if DownloadFile(DownloadURL, DownloadPath) then begin Writeln('Model downloaded successfully.'); ExtractZipFile(DownloadPath, ExtractFolder); // Extract to argos_models Writeln('Model installed successfully.'); // Now check that the model exists, after downloading and extracting. ModelInstalled := TDirectory.Exists(TPath.Combine(ExtractFolder, ModelName + '_extracted')); if not ModelInstalled then begin Writeln('Error: Model installation check failed. Exiting.'); Writeln('Press Enter to exit...'); Readln; Exit; end else begin // --- Delete the .argosmodel file --- try TFile.Delete(DownloadPath); Writeln('Model file deleted successfully.'); except on E: Exception do Writeln('Warning: Could not delete model file: ', E.Message); end; end; end else begin Writeln('Error: Model download failed. Exiting.'); Writeln('Press Enter to exit...'); Readln; Exit; // Exit if download failed. end; end; end; end; case ChosenModels.Count of 0: // Direct translation begin raise Exception.Create('No translation path found for ' + UserSourceLang + ' -> ' + UserTargetLang); end; 2: begin CreatePyEngine(UserSourceLang1, UserTargetLang1); EngineTimer.Stop; CreateEngineTime := EngineTimer.ElapsedMilliseconds; try SourceLanguage := UserSourceLang1; TargetLanguage := UserTargetLang1; InitializeCUDAContext(PythonEngine); Writeln('Please enter text to translate (' + SourceLanguage + ' to ' + TargetLanguage + '):'); Readln(UserInputText); OriginalText := UserInputText; var TranslationTimer: TStopwatch := TStopwatch.StartNew; TranslatedText := CallOptimizedTranslate(OriginalText); TranslationTimer.Stop; TranslationTime := TranslationTimer.ElapsedMilliseconds; Writeln('=== Translation Result (Optimized) ==='); Writeln('Original (' + SourceLanguage + '): ', OriginalText); Writeln('Translated Text (' + TargetLanguage + '): ', TranslatedText); Writeln(''); Writeln('=== Performance Metrics (ms) ==='); Writeln('Create Python Engine (including init): ', CreateEngineTime); Writeln('Translation Time (Delphi Call): ', TranslationTime); finally // Moved DestroyEngine here, so it's called *after* timing DestroyEngineTime := 0; var DestroyEngineTimer := TStopwatch.StartNew; DestroyEngine; // GOOD: Clean up in a finally block DestroyEngineTimer.Stop; DestroyEngineTime := DestroyEngineTimer.ElapsedMilliseconds; end; CreatePyEngine(UserSourceLang2, UserTargetLang2); EngineTimer.Stop; CreateEngineTime := EngineTimer.ElapsedMilliseconds; try SourceLanguage := UserSourceLang2; TargetLanguage := UserTargetLang2; OriginalText := TranslatedText; var TranslationTimer: TStopwatch := TStopwatch.StartNew; TranslatedText := CallOptimizedTranslate(OriginalText); TranslationTimer.Stop; TranslationTime := TranslationTimer.ElapsedMilliseconds; Writeln('=== Translation Result (Optimized) ==='); Writeln('Original (' + SourceLanguage + '): ', OriginalText); Writeln('Translated Text (' + TargetLanguage + '): ', TranslatedText); Writeln(''); Writeln('=== Performance Metrics (ms) ==='); Writeln('Create Python Engine (including init): ', CreateEngineTime); Writeln('Translation Time (Delphi Call): ', TranslationTime); finally // Moved DestroyEngine here, so it's called *after* timing DestroyEngineTime := 0; var DestroyEngineTimer := TStopwatch.StartNew; DestroyEngine; // GOOD: Clean up in a finally block DestroyEngineTimer.Stop; DestroyEngineTime := DestroyEngineTimer.ElapsedMilliseconds; end; end else begin CreatePyEngine(UserSourceLang, UserTargetLang); EngineTimer.Stop; CreateEngineTime := EngineTimer.ElapsedMilliseconds; try SourceLanguage := UserSourceLang; TargetLanguage := UserTargetLang; InitializeCUDAContext(PythonEngine); Writeln('Please enter text to translate (' + SourceLanguage + ' to ' + TargetLanguage + '):'); Readln(UserInputText); OriginalText := UserInputText; var TranslationTimer: TStopwatch := TStopwatch.StartNew; TranslatedText := CallOptimizedTranslate(OriginalText); TranslationTimer.Stop; TranslationTime := TranslationTimer.ElapsedMilliseconds; Writeln('=== Translation Result (Optimized) ==='); Writeln('Original (' + SourceLanguage + '): ', OriginalText); Writeln('Translated Text (' + TargetLanguage + '): ', TranslatedText); Writeln(''); Writeln('=== Performance Metrics (ms) ==='); Writeln('Create Python Engine (including init): ', CreateEngineTime); Writeln('Translation Time (Delphi Call): ', TranslationTime); finally // Moved DestroyEngine here, so it's called *after* timing DestroyEngineTime := 0; var DestroyEngineTimer := TStopwatch.StartNew; DestroyEngine; // GOOD: Clean up in a finally block DestroyEngineTimer.Stop; DestroyEngineTime := DestroyEngineTimer.ElapsedMilliseconds; end; end; end; TotalStopwatch.Stop; // Stop the total timer Writeln('Destroy Python Engine: ', DestroyEngineTime); Writeln('Total Delphi Execution Time: ', TotalStopwatch.ElapsedMilliseconds); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; Writeln('Press Enter to exit...'); Readln; end.

But I'm getting the following error:

text
=== Interactive Translation === Enter source language code (e.g., 'en', 'pt', 'es'): es Enter target language code (e.g., 'en', 'pt', 'es'): fr Model directory does not exist. Model downloaded successfully. Model installed successfully. Model file deleted successfully. Model directory does not exist. Model downloaded successfully. Model installed successfully. Model file deleted successfully. CUDA Device: NVIDIA GeForce GTX 1060 with Max-Q Design Please enter text to translate (es to en): Hola amigos, ¡bienvenidos al club esta noche! === Translation Result (Optimized) === Original (es): Hola amigos, ¡bienvenidos al club esta noche! Translated Text (en): Hey, guys, welcome to the club tonight! === Performance Metrics (ms) === Create Python Engine (including init): 85945 Translation Time (Delphi Call): 309 Traceback (most recent call last): File "<string>", line 2, in <module> File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\ctranslate2\__init__.py", line 21, in <module> from ctranslate2._ext import ( ImportError: generic_type: type "LogLevel" is already registered! EPyImportError: ImportError: generic_type: type "LogLevel" is already registered! Press Enter to exit...

Okay, let's break down this error and the entire situation, step-by-step. The error message:

text
Traceback (most recent call last): File "<string>", line 2, in <module> File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\ctranslate2\__init__.py", line 21, in <module> from ctranslate2._ext import ( ImportError: generic_type: type "LogLevel" is already registered! EPyImportError: ImportError: generic_type: type "LogLevel" is already registered! Press Enter to exit...

is crucial. This is a Python ImportError, and specifically, it indicates a problem within the ctranslate2 Python library itself, or a conflict with another loaded Python module. The key phrase is "type 'LogLevel' is already registered!" This strongly suggests a double initialization or a conflict in how the Python types are being registered, very probably caused by creating two separate Python engines, with separate initializations.

A. Analyze the issue (Deep Dive)

  • The Root Cause: The root cause is almost certainly related to creating two separate Python Engine, so we have to create only one instance of the Python Engine and use it for all translations. We need to initialize ctranslate2 only once. The second call to CreatePyEngine is re-importing and re-initializing ctranslate2, leading to the conflict. The traceback shows the error occurs when importing ctranslate2._ext.

  • Why the Previous Code Worked (Sometimes): The previous code worked for direct translations because it only called CreatePyEngine once for that direct translation. The error appears when we need to do the two steps. The first translation worked, but the second failed.

  • Why a Console App Matters: In a VCL or FMX application, you'd typically have a single TPythonEngine component that lives for the duration of the application. In this console application, we're simulating that, but we have to be extra careful about initialization and cleanup.

  • Decomposition of the Problem:

    1. Initialization: The init_translator function in the Python script is designed to be called once per Python interpreter instance to set up the translation environment. It loads the model, creates the normalizer, etc. Calling it multiple times (with different models) within the same interpreter session is likely to cause conflicts, especially with global variables in the Python script.
    2. Translation: The perform_translation_optimized function relies on the global variables initialized by init_translator. If the initialization is messed up, translation will fail.
    3. Resource Management: Even if the Python code itself is correct, improper handling of Python objects (not releasing them) could lead to problems, although this specific error is more likely due to the double initialization.
    4. Double Engine Creation: Two calls to create engine in CreatePyEngine.

B. Analyze the Solution (Why it Should Work - and Potential Pitfalls)

The general strategy of loading and installing missing models, handling multiple languages and using language pivoting is sound. The errors were arising from P4D and Python usage.

Decomposition of the intended Solution:

  1. Single Python Engine Instance: Use a single TPythonEngine instance throughout the application's lifetime. This is the correct P4D way.
  2. Model Installation Check First: Call CheckAndCreateModelDirectory before the Python engine is even created. This prevents unnecessary Python initialization if the model isn't there.
  3. Download and Install (Delphi): If the model is missing, use the Delphi DownloadFile and ExtractZipFile to get it.
  4. Initialize Once: Call CreatePyEngine only once, passing in the initial source and target languages. This sets up the Python environment, including importing ctranslate2, initializing the first translator, and getting a reference to perform_translation_optimized.
  5. Subsequent Translations:
    • If the languages are different, re-initialize Python to load a different model.
    • Otherwise call the already loaded perform_translation_optimized method.

Why This Should Work:

  • Single Initialization: The core Python libraries (ctranslate2, sentencepiece, etc.) and the translation model are initialized only once per language pair. This avoids the "type already registered" error.
  • Correct Resource Management: Careful use of Py_XDECREF prevents memory leaks.
  • Delphi-Side Download/Extraction: Using Delphi's built-in functions for HTTP and ZIP handling is more robust and efficient.
  • TPythonModule usage: The code now uses the proper P4D module approach.

Potential Pitfalls (and how to mitigate them):

  • Concurrency: If you were to add true multithreading (multiple Delphi threads calling into Python simultaneously), you would need to use the GIL correctly (using TPythonThread) to serialize access to the Python interpreter. The current code is single-threaded from the Delphi side, so this isn't an immediate issue.
  • Model Switching Overhead: If you're frequently switching between many different language pairs, reinitializing with a call to python init_translator will incur a time overhead each time. If there are lots of different models, there is no work around, but if there are only a few, it will be more efficient to store PyFuncTranslate in a dictionary.
  • Complex Error States: Make sure the error handling in DownloadFile and ExtractZipFile is very robust. If, for example, the download succeeds, but extraction partially fails, you might end up in a corrupted state. Consider adding more detailed logging/error reporting to help diagnose such issues.

C. Review and Refine (Possible Alternative Solutions)

  1. Caching PyFuncTranslate: Store the PyFuncTranslate in a dictionary that maps the language pair to that function. This approach is a good optimization if you are frequently switching between a limited set of language pairs.

  2. Python-Side Model Management: If the ctranslate2 library itself supports switching between loaded models without re-initializing the entire interpreter, you could potentially move more of the model management logic into Python. This would require a deeper understanding of the ctranslate2 API, however. The approach taken here is more robust in that it guarantees a clean initialization for each translation, which is safer.

D. Choosing the Best Solution (For Now)

Given the goal of a console application and the need for robustness and adherence to P4D best practices, the approach of using a single TPythonEngine, combined with careful initialization and resource management, is the most appropriate.


Ok, I also believe that the most correct solution is to ensure that we only initialize a TPythonEngine object for our console app once, since this will be the engine that runs Python within the Delphi environment. Destroying it to initialize it again, in addition to being inefficient, would run the risk of conflicting with remnants of libraries on the Python side that are still active while the Delphi app is not finished.

Note that it will be necessary to modify CreatePyEngine() so that it no longer receives the two parameters SourceLang and TargetLang, but rather the models defined directly, and given in a list, in case two models are given for the pivoting case. In a list, I can put one or two models, which would meet both possibilities.

Now with the update of CreatePyEngine(), will I be able to be prepared to use any model that is in the list? For example, more than two in the pivoting case, if I need more languages, will I need to load 3 or more models?

Would you be able to update the CreatePyEngine() function to perfectly meet this, considering the knowledge of the tutorials and examples/demos provided by P4D?

For example:

UserModelNeed1 := UserSourceLang1+"_"+UserTargetLang1;

UserModelNeed2 := UserSourceLang2+"_"+UserTargetLang2;

CreatePyEngine([UserModelNeed1, UserModelNeed2, "pt_es"]);

This would customize it for a specific use for the app's usage session. And it would ensure that PythonEngine was initialized only once in our app (pP4DArgosTranslate.exe)

Note: We will focus only on CreatePyEngine() and the specific modifications to adapt the code from pP4DArgosTranslate.dpr. You don't need to rewrite the entire pP4DArgosTranslate.dpr.

Pensamento

Investigating models

I’m gathering shipping information to determine if Argos Translate models ship to the UK and UK addresses.

Confirming shipping details

I’m mapping out if Argos Translate models are available for shipping to the UK and UK addresses, and noting if shipping details were not found.

Mapping out shipping options

OK, let me see. I’m confirming whether Argos Translate models can ship to the UK and UK addresses, and I’m noting if specific shipping details are missing.

Confirming availability

I’m ensuring Argos Translate models can be shipped to the UK and UK addresses, while noting that certain shipping specifics might still be missing.

Clarifying shipping constraints

I’m confirming that certain Argos Translate models are not available for shipping to the UK and UK addresses, and noting missing shipping specifics as well.

Confirming availability

I’m noting that some Argos Translate models are unavailable for shipping to the UK or UK addresses, and certain details about shipping might be missing.

Confirming shipping details

I’m analyzing specific Argos Translate models unavailable for UK shipping, while noting any missing shipping details for further clarification. This ensures a thorough understanding of the options.

Confirming shipping constraints

I’m noting that some Argos Translate models may not be available for UK shipping, and highlighting any missing shipping details for clearer communication and understanding.

Reasoned for 7 seconds

Resposta

To modify the CreatePyEngine() function in such a way that it supports initializing with multiple models (for pivoting or any other use case) while ensuring the Python engine is initialized only once, we can make the following adjustments.

Goals:

  1. Ensure single initialization of PythonEngine (avoid conflicts from reinitializing it multiple times).
  2. Support for multiple models, such as when pivoting between languages (e.g., es -> en -> fr), while only using one Python engine instance.
  3. Load models dynamically, based on the user request (e.g., user needs models UserModelNeed1, UserModelNeed2, pt_es).

Updated CreatePyEngine Function

delphi
procedure CreatePyEngine(const Models: TArray<string>); var PyArgs: PPyObject; PyInitFunc: PPyObject; PyMainModule: PPyObject; ModelName: string; DownloadPath: string; DownloadURL: string; ExtractFolder: string; AppPath: string; ModelInstalled: Boolean; begin // Initialize Python Engine if not already initialized. if not Assigned(PythonEngine) then begin PythonEngine := TPythonEngine.Create(nil); PythonEngine.Name := 'PythonEngine'; PythonEngine.DllName := 'C:\Users\user\AppData\Local\Programs\Python\Python39\python39.dll'; PythonHome := 'C:\Users\user\AppData\Local\Programs\Python\Python39'; PythonEngine.SetPythonHome(PWideChar(PythonHome)); PythonEngine.LoadDll; end; // Check if the models exist in the model directory and download if necessary. for ModelName in Models do begin AppPath := ExtractFilePath(ParamStr(0)); ExtractFolder := TPath.Combine(AppPath, 'argos_models'); DownloadPath := TPath.Combine(ExtractFolder, ModelName + '.argosmodel'); ModelInstalled := TDirectory.Exists(TPath.Combine(ExtractFolder, ModelName + '_extracted')); if not ModelInstalled then begin Writeln('Model directory does not exist. Downloading model: ', ModelName); DownloadURL := Format('https://argos-net.com/v1/%s.argosmodel', [ModelName]); // Corrected URL if DownloadFile(DownloadURL, DownloadPath) then begin Writeln('Model downloaded successfully: ', ModelName); ExtractZipFile(DownloadPath, ExtractFolder); Writeln('Model extracted: ', ModelName); ModelInstalled := TDirectory.Exists(TPath.Combine(ExtractFolder, ModelName + '_extracted')); if not ModelInstalled then begin Writeln('Error: Model installation check failed. Exiting.'); Writeln('Press Enter to exit...'); Readln; Exit; end; end else begin Writeln('Error: Model download failed for: ', ModelName); Writeln('Press Enter to exit...'); Readln; Exit; end; end; end; // Now, load the models into the Python engine. try PythonModule := TPythonModule.Create(nil); PythonModule.Engine := PythonEngine; PythonModule.ModuleName := 'delphi_module'; // Prepare the initialization script for the translator. PythonEngine.ExecString(PAnsiChar(AnsiString(EmbeddedPythonScriptInit))); PythonEngine.CheckError; PyMainModule := PythonEngine.GetMainModule; if not Assigned(PyMainModule) then raise Exception.Create('Could not get __main__ module.'); PythonEngine.CheckError; // Prepare the models for loading into Python for ModelName in Models do begin PyInitFunc := PythonEngine.PyObject_GetAttrString(PyMainModule, 'init_translator'); if not Assigned(PyInitFunc) then begin PythonEngine.Py_XDECREF(PyMainModule); raise Exception.Create('init_translator function not found'); end; PythonEngine.CheckError; // Create a Python tuple for the model argument PyArgs := PythonEngine.PyTuple_New(2); PythonEngine.CheckError; try // Set the source and target language pairs as arguments PythonEngine.PyTuple_SetItem(PyArgs, 0, PythonEngine.PyUnicode_FromString(PAnsiChar(UTF8String(ModelName.Split(['_'])[0])))); PythonEngine.CheckError; PythonEngine.PyTuple_SetItem(PyArgs, 1, PythonEngine.PyUnicode_FromString(PAnsiChar(UTF8String(ModelName.Split(['_'])[1])))); PythonEngine.CheckError; // Call the initialization function PythonEngine.PyObject_CallObject(PyInitFunc, PyArgs); PythonEngine.CheckError; finally PythonEngine.Py_XDECREF(PyArgs); PythonEngine.Py_XDECREF(PyInitFunc); end; end; // Setup the translation function after all models are initialized PythonEngine.ExecString(PAnsiChar(AnsiString(EmbeddedPythonScriptTranslateOptimized))); PythonEngine.CheckError; except on E: Exception do begin Writeln('Error during Python engine initialization: ', E.Message); Exit; end; end; end;

Key Changes:

  1. Single Python Engine Initialization: The Python engine (PythonEngine) is initialized only once during the application's lifetime. If the engine is already created, we reuse the existing instance.
  2. Multiple Models: The function now accepts a list of models (an array of strings). Each model corresponds to a language pair like "es_en", "en_fr", etc.
  3. Dynamic Model Loading: It checks if each model is already installed; if not, it downloads and extracts the model files.
  4. Model Initialization: After checking or downloading the models, the function dynamically loads them into Python by calling init_translator for each model in the list.
  5. Python Script Execution: The initialization script and translation script are executed once, before performing any translations.

Explanation of Logic:

  • Model Handling: If you need two models (e.g., es_en and en_fr), you pass them to CreatePyEngine([ 'es_en', 'en_fr' ]). It will download and extract the models if necessary and initialize them sequentially.
  • Handling Multiple Models: This solution is flexible enough to handle multiple models. In the case of pivoting or more complex scenarios, you can provide an array of models, and the code will ensure they are initialized one after the other.
  • Performance Consideration: Since the Python engine is initialized once, and the models are loaded sequentially, this approach avoids the overhead of initializing the engine multiple times.

Usage:

Now you can use CreatePyEngine([ 'es_en', 'en_fr' ]) for pivoting from Spanish to English, then from English to French, all while maintaining a single Python engine instance.

This approach should resolve the issue of the LogLevel conflict, as we are no longer reinitializing the Python environment. Instead, we're reusing the same engine throughout the session.

Compartilhar esta Q&A