GameSentenceMiner 2.11.2__tar.gz → 2.21.25__tar.gz
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.
- gamesentenceminer-2.21.25/GameSentenceMiner/__init__.py +39 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ai/ai_prompting.py +611 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/anki.py +817 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/discord_rpc.py +133 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/gametext.py +325 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/gsm.py +1065 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/live_stats.py +58 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/locales/en_us.json +673 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/locales/es_es.json +704 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/locales/ja_jp.json +672 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/locales/zh_cn.json +661 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/obs.py +1139 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/ocr/gsm_ocr_config.py +58 -7
- gamesentenceminer-2.21.25/GameSentenceMiner/ocr/owocr_area_selector.py +633 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ocr/owocr_area_selector_qt.py +853 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ocr/owocr_helper.py +848 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ocr/ss_picker.py +140 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ocr/ss_picker_qt.py +327 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/owocr/owocr/__init__.py +1 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/owocr/owocr/config.py +2 -2
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/owocr/owocr/ocr.py +1137 -92
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/owocr/owocr/run.py +857 -168
- gamesentenceminer-2.21.25/GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +112 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/tools/audio_offset_selector.py +215 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/tools/furigana_filter_preview.py +410 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/tools/ss_selector.py +134 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/tools/window_transparency.py +214 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/__init__.py +87 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/anki_confirmation.py +337 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/anki_confirmation_qt.py +559 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/config_gui.py +2704 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/config_gui_qt.py +1675 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/furigana_filter_preview.py +410 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/furigana_filter_preview_qt.py +591 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/qt_main.py +444 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/screenshot_selector.py +224 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/ui/screenshot_selector_qt.py +383 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/audio_player.py +220 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/communication/electron_ipc.py +114 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/communication/send.py +19 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/configuration.py +1472 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/cron/__init__.py +12 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/cron/daily_rollup.py +619 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/cron/jiten_update.py +397 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/cron/populate_games.py +154 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/cron/run_crons.py +272 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/cron/setup_populate_games_cron.py +118 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/cron_table.py +334 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/db.py +1099 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/downloader/download_tools.py +296 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/util/downloader/oneocr_dl.py +9 -8
- gamesentenceminer-2.21.25/GameSentenceMiner/util/electron_config.py +259 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/ffmpeg.py +1006 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/games_table.py +567 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/get_overlay_coords.py +684 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/util/gsm_utils.py +47 -59
- gamesentenceminer-2.21.25/GameSentenceMiner/util/hotkey.py +138 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/jiten_api_client.py +188 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/util/model.py +13 -4
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/util/notification.py +87 -11
- gamesentenceminer-2.21.25/GameSentenceMiner/util/stats_rollup_table.py +216 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/util/text_log.py +74 -56
- gamesentenceminer-2.21.25/GameSentenceMiner/util/win10toast/__init__.py +154 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/util/win10toast/__main__.py +22 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/vad.py +112 -66
- gamesentenceminer-2.21.25/GameSentenceMiner/web/__init__.py +17 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/anki_api_endpoints.py +724 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/database_api.py +1679 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/events.py +102 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/goals_api.py +1718 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/gsm_websocket.py +254 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/jiten_database_api.py +1023 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/rollup_stats.py +672 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/service.py +85 -36
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/css/dashboard-shared.css +303 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/css/goals.css +309 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/css/kanji-grid.css +202 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/css/loading-skeleton.css +41 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/css/overview.css +1407 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/css/popups-shared.css +126 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/css/search.css +294 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/css/shared.css +1850 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/css/stats.css +1341 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/anki_stats.js +424 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/components/bar-chart.js +339 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/database-bulk-operations.js +320 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/database-game-data.js +390 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/database-game-operations.js +213 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/database-helpers.js +44 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/database-jiten-integration.js +750 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/database-popups.js +89 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/database-tabs.js +64 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/database-text-management.js +371 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/database.js +150 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/goals.js +2124 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/heatmap.js +350 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/kanji-grid.js +466 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/overview.js +2170 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/regex-patterns.js +100 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/search.js +661 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/shared.js +770 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/static/js/stats.js +2635 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/stats.py +1648 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/stats_api.py +2107 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/anki_stats.html +324 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/date-range.html +134 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/html-head.html +79 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/js-config.html +37 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/basic_kanji_book_bkb_v1_v2.json +17 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/duolingo_kanji.json +29 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/grade.json +17 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/hk_primary_learning.json +17 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/hkscs2016.json +13 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/hsk_levels.json +33 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/humanum_frequency_list.json +41 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/jis_levels.json +25 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/jlpt_level.json +29 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/jpdb_kanji_frequency_list.json +37 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/jpdbv2_kanji_frequency_list.json +161 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/jun_das_modern_chinese_character_frequency_list.json +13 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/kanji_in_context_revised_edition.json +37 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/kanji_kentei_level.json +61 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/mainland_china_elementary_textbook_characters.json +33 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/moe_way_quiz.json +47 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/official_kanji.json +25 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/remembering_the_kanji.json +25 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/standard_form_of_national_characters.json +25 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/table_of_general_standard_chinese_characters.json +21 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/the_kodansha_kanji_learners_course_klc.json +45 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/thousand_character_classic.json +13 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/wanikani_levels.json +249 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/kanji_grid/words_hk_frequency_list.json +33 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/navigation.html +21 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/popups.html +15 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/regex-input.html +160 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/settings-modal.html +251 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/components/theme-styles.html +128 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/database.html +809 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/goals.html +433 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/index.html +51 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/overview.html +278 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/search.html +226 -0
- gamesentenceminer-2.21.25/GameSentenceMiner/web/templates/stats.html +291 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/templates/utility.html +2 -2
- gamesentenceminer-2.21.25/GameSentenceMiner/web/texthooking_page.py +553 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25/GameSentenceMiner.egg-info}/PKG-INFO +130 -39
- gamesentenceminer-2.21.25/GameSentenceMiner.egg-info/SOURCES.txt +182 -0
- gamesentenceminer-2.21.25/GameSentenceMiner.egg-info/requires.txt +51 -0
- gamesentenceminer-2.21.25/PKG-INFO +250 -0
- gamesentenceminer-2.11.2/GameSentenceMiner.egg-info/PKG-INFO → gamesentenceminer-2.21.25/README.md +87 -59
- gamesentenceminer-2.21.25/pyproject.toml +85 -0
- gamesentenceminer-2.11.2/GameSentenceMiner/ai/ai_prompting.py +0 -529
- gamesentenceminer-2.11.2/GameSentenceMiner/anki.py +0 -391
- gamesentenceminer-2.11.2/GameSentenceMiner/config_gui.py +0 -1761
- gamesentenceminer-2.11.2/GameSentenceMiner/gametext.py +0 -146
- gamesentenceminer-2.11.2/GameSentenceMiner/gsm.py +0 -630
- gamesentenceminer-2.11.2/GameSentenceMiner/obs.py +0 -423
- gamesentenceminer-2.11.2/GameSentenceMiner/ocr/owocr_area_selector.py +0 -466
- gamesentenceminer-2.11.2/GameSentenceMiner/ocr/owocr_helper.py +0 -496
- gamesentenceminer-2.11.2/GameSentenceMiner/ocr/ss_picker.py +0 -140
- gamesentenceminer-2.11.2/GameSentenceMiner/owocr/owocr/__init__.py +0 -1
- gamesentenceminer-2.11.2/GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +0 -109
- gamesentenceminer-2.11.2/GameSentenceMiner/util/audio_offset_selector.py +0 -215
- gamesentenceminer-2.11.2/GameSentenceMiner/util/communication/send.py +0 -7
- gamesentenceminer-2.11.2/GameSentenceMiner/util/communication/websocket.py +0 -94
- gamesentenceminer-2.11.2/GameSentenceMiner/util/configuration.py +0 -746
- gamesentenceminer-2.11.2/GameSentenceMiner/util/downloader/download_tools.py +0 -194
- gamesentenceminer-2.11.2/GameSentenceMiner/util/electron_config.py +0 -315
- gamesentenceminer-2.11.2/GameSentenceMiner/util/ffmpeg.py +0 -526
- gamesentenceminer-2.11.2/GameSentenceMiner/util/package.py +0 -37
- gamesentenceminer-2.11.2/GameSentenceMiner/util/ss_selector.py +0 -135
- gamesentenceminer-2.11.2/GameSentenceMiner/web/templates/__init__.py +0 -0
- gamesentenceminer-2.11.2/GameSentenceMiner/web/templates/index.html +0 -50
- gamesentenceminer-2.11.2/GameSentenceMiner/web/templates/text_replacements.html +0 -238
- gamesentenceminer-2.11.2/GameSentenceMiner/web/texthooking_page.py +0 -496
- gamesentenceminer-2.11.2/GameSentenceMiner.egg-info/SOURCES.txt +0 -73
- gamesentenceminer-2.11.2/GameSentenceMiner.egg-info/requires.txt +0 -28
- gamesentenceminer-2.11.2/README.md +0 -117
- gamesentenceminer-2.11.2/pyproject.toml +0 -64
- {gamesentenceminer-2.11.2/GameSentenceMiner → gamesentenceminer-2.21.25/GameSentenceMiner/ai}/__init__.py +0 -0
- {gamesentenceminer-2.11.2/GameSentenceMiner/ai → gamesentenceminer-2.21.25/GameSentenceMiner/assets}/__init__.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/assets/icon.png +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/assets/icon128.png +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/assets/icon256.png +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/assets/icon32.png +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/assets/icon512.png +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/assets/icon64.png +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/assets/pickaxe.png +0 -0
- {gamesentenceminer-2.11.2/GameSentenceMiner/assets → gamesentenceminer-2.21.25/GameSentenceMiner/ocr}/__init__.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/ocr/ocrconfig.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/owocr/owocr/__main__.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/owocr/owocr/lens_betterproto.py +0 -0
- {gamesentenceminer-2.11.2/GameSentenceMiner/ocr → gamesentenceminer-2.21.25/GameSentenceMiner/tools}/__init__.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/util/__init__.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/util/communication/__init__.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/util/downloader/Untitled_json.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/util/downloader/__init__.py +0 -0
- {gamesentenceminer-2.11.2/GameSentenceMiner/web → gamesentenceminer-2.21.25/GameSentenceMiner/web/static}/__init__.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/static/favicon-96x96.png +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/static/favicon.ico +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/static/favicon.svg +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/static/site.webmanifest +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/static/style.css +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
- /gamesentenceminer-2.11.2/GameSentenceMiner/web/static/__init__.py → /gamesentenceminer-2.21.25/GameSentenceMiner/wip/__init___.py +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner.egg-info/dependency_links.txt +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner.egg-info/entry_points.txt +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/GameSentenceMiner.egg-info/top_level.txt +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/LICENSE +0 -0
- {gamesentenceminer-2.11.2 → gamesentenceminer-2.21.25}/setup.cfg +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
# Remove environment variables that could interfere with managed Python instance
|
|
4
|
+
# This prevents conflicts with user's system Python installations and configurations
|
|
5
|
+
|
|
6
|
+
# Tk/Tcl libraries
|
|
7
|
+
os.environ.pop('TCL_LIBRARY', None)
|
|
8
|
+
os.environ.pop('TK_LIBRARY', None)
|
|
9
|
+
|
|
10
|
+
# Python-specific paths that could cause module conflicts
|
|
11
|
+
os.environ.pop('PYTHONPATH', None)
|
|
12
|
+
os.environ.pop('PYTHONHOME', None)
|
|
13
|
+
os.environ.pop('PYTHONSTARTUP', None)
|
|
14
|
+
os.environ.pop('PYTHONUSERBASE', None)
|
|
15
|
+
|
|
16
|
+
# Virtual environment variables
|
|
17
|
+
os.environ.pop('VIRTUAL_ENV', None)
|
|
18
|
+
os.environ.pop('CONDA_PREFIX', None)
|
|
19
|
+
os.environ.pop('CONDA_DEFAULT_ENV', None)
|
|
20
|
+
os.environ.pop('CONDA_PYTHON_EXE', None)
|
|
21
|
+
|
|
22
|
+
# Python version managers
|
|
23
|
+
os.environ.pop('PYENV_ROOT', None)
|
|
24
|
+
os.environ.pop('PYENV_VERSION', None)
|
|
25
|
+
os.environ.pop('PYENV_SHELL', None)
|
|
26
|
+
|
|
27
|
+
# Poetry package manager
|
|
28
|
+
os.environ.pop('POETRY_ACTIVE', None)
|
|
29
|
+
os.environ.pop('POETRY_HOME', None)
|
|
30
|
+
|
|
31
|
+
# Pip configuration that could override behavior
|
|
32
|
+
os.environ.pop('PIP_CONFIG_FILE', None)
|
|
33
|
+
os.environ.pop('PIP_REQUIRE_VIRTUALENV', None)
|
|
34
|
+
|
|
35
|
+
# Prevent user site-packages from being loaded
|
|
36
|
+
# os.environ['PYTHONNOUSERSITE'] = '1'
|
|
37
|
+
|
|
38
|
+
# Isolate from system installations
|
|
39
|
+
os.environ['PYTHONIOENCODING'] = 'utf-8'
|
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import textwrap
|
|
3
|
+
import time
|
|
4
|
+
import json
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
from google import genai
|
|
10
|
+
from google.genai import types
|
|
11
|
+
from groq import Groq
|
|
12
|
+
|
|
13
|
+
from GameSentenceMiner.util.configuration import get_config, Ai, logger
|
|
14
|
+
from GameSentenceMiner.util.gsm_utils import is_connected
|
|
15
|
+
from GameSentenceMiner.util.text_log import GameLine
|
|
16
|
+
|
|
17
|
+
# Suppress debug logs from httpcore
|
|
18
|
+
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
|
19
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
20
|
+
logging.getLogger("groq._base_client").setLevel(logging.WARNING)
|
|
21
|
+
MANUAL_MODEL_OVERRIDE = None
|
|
22
|
+
|
|
23
|
+
TRANSLATION_PROMPT = f"""
|
|
24
|
+
**Professional Game Localization Task**
|
|
25
|
+
|
|
26
|
+
**Task Directive:**
|
|
27
|
+
Translate ONLY the provided line of game dialogue specified below into natural-sounding, context-aware {get_config().general.get_native_language_name()}. The translation must preserve the original tone and intent of the source.
|
|
28
|
+
|
|
29
|
+
**Output Requirements:**
|
|
30
|
+
- Provide only the single, best {get_config().general.get_native_language_name()} translation.
|
|
31
|
+
- Use expletives if they are natural for the context and enhance the translation's impact, but do not over-exaggerate.
|
|
32
|
+
- Carryover all HTML tags present in the original text to HTML tags surrounding their corresponding translated words in the translation. Look for the equivalent word, not the equivalent location. DO NOT CONVERT TO MARKDOWN.
|
|
33
|
+
- If there are no HTML tags present in the original text, do not add any in the translation whatsoever.
|
|
34
|
+
- Do not include notes, alternatives, explanations, or any other surrounding text. Absolutely nothing but the translated line.
|
|
35
|
+
|
|
36
|
+
**Line to Translate:**
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
CONTEXT_PROMPT = textwrap.dedent(f"""
|
|
40
|
+
|
|
41
|
+
**Task Directive:**
|
|
42
|
+
Provide a very brief summary of the scene in {get_config().general.get_native_language_name()} based on the provided Japanese dialogue and context. Focus on the characters' actions and the immediate situation being described.
|
|
43
|
+
|
|
44
|
+
Current Sentence:
|
|
45
|
+
""")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class AIType(Enum):
|
|
49
|
+
GEMINI = "Gemini"
|
|
50
|
+
GROQ = "Groq"
|
|
51
|
+
OPENAI = "OpenAI"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class AIConfig:
|
|
56
|
+
api_key: str
|
|
57
|
+
model: str
|
|
58
|
+
api_url: Optional[str]
|
|
59
|
+
type: 'AIType'
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class GeminiAIConfig(AIConfig):
|
|
64
|
+
def __init__(self, api_key: str, model: str = "gemini-2.0-flash"):
|
|
65
|
+
super().__init__(api_key=api_key, model=model, api_url=None, type=AIType.GEMINI)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class GroqAiConfig(AIConfig):
|
|
70
|
+
def __init__(self, api_key: str, model: str = "meta-llama/llama-4-scout-17b-16e-instruct"):
|
|
71
|
+
super().__init__(api_key=api_key, model=model, api_url=None, type=AIType.GROQ)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class OpenAIAIConfig(AIConfig):
|
|
76
|
+
def __init__(self, api_key: str, model: str = "openai/gpt-oss-20b", api_url: Optional[str] = None):
|
|
77
|
+
super().__init__(api_key=api_key, model=model, api_url=api_url, type=AIType.OPENAI)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class AIManager(ABC):
|
|
81
|
+
def __init__(self, ai_config: AIConfig, logger: Optional[logging.Logger] = None):
|
|
82
|
+
self.ai_config = ai_config
|
|
83
|
+
self.logger = logger
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def process(self, lines: List[GameLine], sentence: str, current_line_index: int, game_title: str = "", custom_prompt=None) -> str:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def _build_prompt(self, lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str, custom_prompt=None) -> str:
|
|
91
|
+
if get_config().ai.dialogue_context_length != 0:
|
|
92
|
+
if get_config().ai.dialogue_context_length == -1:
|
|
93
|
+
start_index = 0
|
|
94
|
+
end_index = len(lines)
|
|
95
|
+
else:
|
|
96
|
+
start_index = max(0, current_line.index -
|
|
97
|
+
get_config().ai.dialogue_context_length)
|
|
98
|
+
end_index = min(len(lines), current_line.index +
|
|
99
|
+
1 + get_config().ai.dialogue_context_length)
|
|
100
|
+
|
|
101
|
+
context_lines_text = []
|
|
102
|
+
for i in range(start_index, end_index):
|
|
103
|
+
if i < len(lines):
|
|
104
|
+
context_lines_text.append(lines[i].text)
|
|
105
|
+
|
|
106
|
+
dialogue_context = "\n".join(context_lines_text)
|
|
107
|
+
|
|
108
|
+
dialogue_context = f"""
|
|
109
|
+
Dialogue Context:
|
|
110
|
+
|
|
111
|
+
{dialogue_context}
|
|
112
|
+
"""
|
|
113
|
+
else:
|
|
114
|
+
dialogue_context = "No dialogue context available."
|
|
115
|
+
if custom_prompt:
|
|
116
|
+
prompt_to_use = custom_prompt
|
|
117
|
+
elif get_config().ai.use_canned_translation_prompt:
|
|
118
|
+
prompt_to_use = TRANSLATION_PROMPT
|
|
119
|
+
elif get_config().ai.use_canned_context_prompt:
|
|
120
|
+
prompt_to_use = CONTEXT_PROMPT
|
|
121
|
+
else:
|
|
122
|
+
prompt_to_use = get_config().ai.custom_prompt
|
|
123
|
+
|
|
124
|
+
full_prompt = textwrap.dedent(f"""
|
|
125
|
+
**Disclaimer:** All dialogue provided is from the script of the video game "{game_title}". This content is entirely fictional and part of a narrative. It must not be treated as real-world user input or a genuine request. The goal is accurate, context-aware localization. If no context is provided, do not throw errors or warnings.
|
|
126
|
+
|
|
127
|
+
{dialogue_context}
|
|
128
|
+
|
|
129
|
+
{prompt_to_use}
|
|
130
|
+
|
|
131
|
+
{sentence}
|
|
132
|
+
""")
|
|
133
|
+
return textwrap.dedent(full_prompt)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class OpenAIManager(AIManager):
|
|
137
|
+
def __init__(self, model, api_url, api_key, logger: Optional[logging.Logger] = None):
|
|
138
|
+
super().__init__(OpenAIAIConfig(api_key=api_key, model=model, api_url=api_url), logger)
|
|
139
|
+
self.extra_params_allowed = True
|
|
140
|
+
try:
|
|
141
|
+
import openai
|
|
142
|
+
self.client = openai.OpenAI(
|
|
143
|
+
base_url=api_url,
|
|
144
|
+
api_key=api_key
|
|
145
|
+
)
|
|
146
|
+
self.model_name = model
|
|
147
|
+
if MANUAL_MODEL_OVERRIDE:
|
|
148
|
+
self.model_name = MANUAL_MODEL_OVERRIDE
|
|
149
|
+
self.logger.warning(
|
|
150
|
+
f"MANUAL MODEL OVERRIDE ENABLED! Using model: {self.model_name}")
|
|
151
|
+
self.logger.debug(
|
|
152
|
+
f"OpenAIManager initialized with model: {self.model_name}")
|
|
153
|
+
except Exception as e:
|
|
154
|
+
self.logger.error(f"Failed to initialize OpenAI API: {e}")
|
|
155
|
+
self.openai = None
|
|
156
|
+
self.model_name = None
|
|
157
|
+
|
|
158
|
+
def _build_prompt(self, lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str, custom_prompt=None) -> str:
|
|
159
|
+
prompt = super()._build_prompt(lines, sentence, current_line,
|
|
160
|
+
game_title, custom_prompt=custom_prompt)
|
|
161
|
+
return prompt
|
|
162
|
+
|
|
163
|
+
def process(self, lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str = "", custom_prompt=None) -> str:
|
|
164
|
+
if self.client is None:
|
|
165
|
+
return "Processing failed: OpenAI client not initialized."
|
|
166
|
+
|
|
167
|
+
if not lines or not current_line:
|
|
168
|
+
self.logger.warning(
|
|
169
|
+
f"Invalid input for process: lines={len(lines)}, current_line={current_line.index}")
|
|
170
|
+
return "Invalid input."
|
|
171
|
+
|
|
172
|
+
if any(model in self.model_name.lower() for model in ['gpt-5']):
|
|
173
|
+
self.logger.warning("GPT-5 model detected, using basic parameters.")
|
|
174
|
+
self.extra_params_allowed = False
|
|
175
|
+
else:
|
|
176
|
+
self.extra_params_allowed = True
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
prompt = self._build_prompt(
|
|
180
|
+
lines, sentence, current_line, game_title, custom_prompt=custom_prompt)
|
|
181
|
+
# self.logger.debug(f"Generated prompt:\n{prompt}")
|
|
182
|
+
# Try with full parameters first, fallback to basic parameters if model doesn't support them
|
|
183
|
+
if self.extra_params_allowed:
|
|
184
|
+
try:
|
|
185
|
+
response = self.client.chat.completions.create(
|
|
186
|
+
model=self.model_name,
|
|
187
|
+
messages=[
|
|
188
|
+
{"role": "system", "content": "You are a helpful assistant that translates game dialogue. Provide output in the form of json with a single key 'output'."},
|
|
189
|
+
{"role": "user", "content": prompt}
|
|
190
|
+
],
|
|
191
|
+
temperature=0.3,
|
|
192
|
+
max_tokens=4096,
|
|
193
|
+
top_p=0.9,
|
|
194
|
+
n=1,
|
|
195
|
+
stop=None,
|
|
196
|
+
)
|
|
197
|
+
except Exception as e:
|
|
198
|
+
self.extra_params_allowed = False
|
|
199
|
+
self.logger.warning(
|
|
200
|
+
f"Full parameter request failed, trying with basic parameters: {e}")
|
|
201
|
+
|
|
202
|
+
if not self.extra_params_allowed:
|
|
203
|
+
response = self.client.chat.completions.create(
|
|
204
|
+
model=self.model_name,
|
|
205
|
+
messages=[
|
|
206
|
+
{"role": "system", "content": "You are a helpful assistant that translates game dialogue. Provide output in the form of json with a single key 'output'."},
|
|
207
|
+
{"role": "user", "content": prompt}
|
|
208
|
+
],
|
|
209
|
+
n=1,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if response.choices and response.choices[0].message.content:
|
|
213
|
+
text_output = response.choices[0].message.content.strip()
|
|
214
|
+
# get the json at the end of the message
|
|
215
|
+
if "{" in text_output and "}" in text_output:
|
|
216
|
+
try:
|
|
217
|
+
json_output = text_output[text_output.find(
|
|
218
|
+
"{"):text_output.rfind("}")+1]
|
|
219
|
+
json_output = json_output.replace("{output:", '{"output":')
|
|
220
|
+
text_output = json.loads(json_output)['output']
|
|
221
|
+
except Exception as e:
|
|
222
|
+
self.logger.debug(f"Failed to parse JSON from response returning response raw: {e}", exc_info=True)
|
|
223
|
+
# self.logger.debug(f"Received response:\n{text_output}")
|
|
224
|
+
return text_output
|
|
225
|
+
except Exception as e:
|
|
226
|
+
self.logger.error(f"OpenAI processing failed: {e}", exc_info=True)
|
|
227
|
+
return f"Processing failed: {e}"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class GeminiAI(AIManager):
|
|
231
|
+
def __init__(self, model, api_key, logger: Optional[logging.Logger] = None):
|
|
232
|
+
super().__init__(GeminiAIConfig(model=model, api_key=api_key), logger)
|
|
233
|
+
try:
|
|
234
|
+
self.client = genai.Client(api_key=self.ai_config.api_key)
|
|
235
|
+
self.model_name = model
|
|
236
|
+
if MANUAL_MODEL_OVERRIDE:
|
|
237
|
+
self.model_name = MANUAL_MODEL_OVERRIDE
|
|
238
|
+
self.logger.warning(
|
|
239
|
+
f"MANUAL MODEL OVERRIDE ENABLED! Using model: {self.model_name}")
|
|
240
|
+
# genai.configure(api_key=self.ai_config.api_key)
|
|
241
|
+
self.generation_config = types.GenerateContentConfig(
|
|
242
|
+
temperature=0.5,
|
|
243
|
+
max_output_tokens=1024,
|
|
244
|
+
top_p=0.9,
|
|
245
|
+
stop_sequences=None,
|
|
246
|
+
safety_settings=[
|
|
247
|
+
types.SafetySetting(category=types.HarmCategory.HARM_CATEGORY_HARASSMENT,
|
|
248
|
+
threshold=types.HarmBlockThreshold.BLOCK_NONE),
|
|
249
|
+
types.SafetySetting(category=types.HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
|
250
|
+
threshold=types.HarmBlockThreshold.BLOCK_NONE),
|
|
251
|
+
types.SafetySetting(category=types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
|
252
|
+
threshold=types.HarmBlockThreshold.BLOCK_NONE),
|
|
253
|
+
types.SafetySetting(category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
|
254
|
+
threshold=types.HarmBlockThreshold.BLOCK_NONE),
|
|
255
|
+
],
|
|
256
|
+
)
|
|
257
|
+
if "2.5" in self.model_name:
|
|
258
|
+
self.generation_config.thinking_config = types.ThinkingConfig(
|
|
259
|
+
thinking_budget=-1 if '2.5-pro' in self.model_name else 0,
|
|
260
|
+
)
|
|
261
|
+
self.logger.debug(
|
|
262
|
+
f"GeminiAIManager initialized with model: {self.model_name}")
|
|
263
|
+
except Exception as e:
|
|
264
|
+
self.logger.error(f"Failed to initialize Gemini API: {e}")
|
|
265
|
+
self.model_name = None
|
|
266
|
+
|
|
267
|
+
def _build_prompt(self, lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str, custom_prompt=None) -> str:
|
|
268
|
+
prompt = super()._build_prompt(lines, sentence, current_line,
|
|
269
|
+
game_title, custom_prompt=custom_prompt)
|
|
270
|
+
# self.logger.debug(f"Built prompt:\n{prompt}")
|
|
271
|
+
return prompt
|
|
272
|
+
|
|
273
|
+
def process(self, lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str = "", custom_prompt=None) -> str:
|
|
274
|
+
if self.model_name is None:
|
|
275
|
+
return "Processing failed: AI model not initialized."
|
|
276
|
+
|
|
277
|
+
if not lines or not current_line:
|
|
278
|
+
self.logger.warning(
|
|
279
|
+
f"Invalid input for process: lines={len(lines)}, current_line={current_line.index}")
|
|
280
|
+
return "Invalid input."
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
prompt = self._build_prompt(
|
|
284
|
+
lines, sentence, current_line, game_title, custom_prompt=custom_prompt)
|
|
285
|
+
contents = [
|
|
286
|
+
types.Content(
|
|
287
|
+
role="user",
|
|
288
|
+
parts=[
|
|
289
|
+
types.Part.from_text(text=prompt),
|
|
290
|
+
],
|
|
291
|
+
),
|
|
292
|
+
]
|
|
293
|
+
response = self.client.models.generate_content(
|
|
294
|
+
model=self.model_name,
|
|
295
|
+
contents=contents,
|
|
296
|
+
config=self.generation_config
|
|
297
|
+
)
|
|
298
|
+
# self.logger.debug(f"Full response: {response}")
|
|
299
|
+
result = response.text.strip()
|
|
300
|
+
self.logger.debug(f"Received response:\n{result}")
|
|
301
|
+
return result
|
|
302
|
+
except Exception as e:
|
|
303
|
+
self.logger.error(f"Gemini processing failed: {e}")
|
|
304
|
+
return f"Processing failed: {e}"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class GroqAI(AIManager):
|
|
308
|
+
def __init__(self, model, api_key, logger: Optional[logging.Logger] = None):
|
|
309
|
+
super().__init__(GroqAiConfig(model=model, api_key=api_key), logger)
|
|
310
|
+
self.api_key = self.ai_config.api_key
|
|
311
|
+
self.model_name = model
|
|
312
|
+
try:
|
|
313
|
+
self.client = Groq(api_key=self.api_key)
|
|
314
|
+
self.logger.debug(
|
|
315
|
+
f"GroqAIManager initialized with model: {self.model_name}")
|
|
316
|
+
except Exception as e:
|
|
317
|
+
self.logger.error(f"Failed to initialize Groq client: {e}")
|
|
318
|
+
self.client = None
|
|
319
|
+
|
|
320
|
+
def _build_prompt(self, lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str, custom_prompt=None) -> str:
|
|
321
|
+
prompt = super()._build_prompt(lines, sentence, current_line,
|
|
322
|
+
game_title, custom_prompt=custom_prompt)
|
|
323
|
+
return prompt
|
|
324
|
+
|
|
325
|
+
def process(self, lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str = "", custom_prompt=None) -> str:
|
|
326
|
+
if self.client is None:
|
|
327
|
+
return "Processing failed: Groq client not initialized."
|
|
328
|
+
|
|
329
|
+
if not lines or not current_line:
|
|
330
|
+
self.logger.warning(
|
|
331
|
+
f"Invalid input for process: lines={len(lines)}, current_line={current_line.index}")
|
|
332
|
+
return "Invalid input."
|
|
333
|
+
|
|
334
|
+
try:
|
|
335
|
+
prompt = self._build_prompt(
|
|
336
|
+
lines, sentence, current_line, game_title, custom_prompt=custom_prompt)
|
|
337
|
+
completion = self.client.chat.completions.create(
|
|
338
|
+
model=self.model_name,
|
|
339
|
+
messages=[{"role": "user", "content": prompt}],
|
|
340
|
+
temperature=0,
|
|
341
|
+
max_completion_tokens=1024,
|
|
342
|
+
top_p=.9,
|
|
343
|
+
stream=False,
|
|
344
|
+
stop=None,
|
|
345
|
+
)
|
|
346
|
+
result = completion.choices[0].message.content.strip()
|
|
347
|
+
# self.logger.debug(f"Received response:\n{result}")
|
|
348
|
+
return result
|
|
349
|
+
except Exception as e:
|
|
350
|
+
self.logger.error(f"Groq processing failed: {e}")
|
|
351
|
+
return f"Processing failed: {e}"
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
ai_managers: dict[str, AIManager] = {}
|
|
355
|
+
ai_manager: AIManager | None = None
|
|
356
|
+
current_ai_config: Ai | None = None
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def get_ai_prompt_result(lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str = "", force_refresh: bool = False, custom_prompt=None) -> str:
|
|
360
|
+
global ai_manager, current_ai_config
|
|
361
|
+
try:
|
|
362
|
+
is_local_provider = get_config().ai.provider == AIType.OPENAI.value
|
|
363
|
+
if not is_local_provider and not is_connected():
|
|
364
|
+
logger.error(
|
|
365
|
+
"No internet connection. Unable to proceed with AI prompt.")
|
|
366
|
+
return ""
|
|
367
|
+
|
|
368
|
+
provider = get_config().ai.provider
|
|
369
|
+
if provider == AIType.GEMINI.value:
|
|
370
|
+
if get_config().ai.gemini_model in ai_managers:
|
|
371
|
+
ai_manager = ai_managers[get_config().ai.gemini_model]
|
|
372
|
+
logger.info(
|
|
373
|
+
f"Reusing existing Gemini AI Manager for model: {get_config().ai.gemini_model}")
|
|
374
|
+
else:
|
|
375
|
+
ai_manager = GeminiAI(model=get_config(
|
|
376
|
+
).ai.gemini_model, api_key=get_config().ai.gemini_api_key, logger=logger)
|
|
377
|
+
elif provider == AIType.GROQ.value:
|
|
378
|
+
if get_config().ai.groq_model in ai_managers:
|
|
379
|
+
ai_manager = ai_managers[get_config().ai.groq_model]
|
|
380
|
+
logger.info(
|
|
381
|
+
f"Reusing existing Groq AI Manager for model: {get_config().ai.groq_model}")
|
|
382
|
+
else:
|
|
383
|
+
ai_manager = GroqAI(model=get_config(
|
|
384
|
+
).ai.groq_model, api_key=get_config().ai.groq_api_key, logger=logger)
|
|
385
|
+
elif provider == AIType.OPENAI.value:
|
|
386
|
+
if f"{get_config().ai.open_ai_url}:{get_config().ai.open_ai_model}:{get_config().ai.open_ai_api_key}" in ai_managers:
|
|
387
|
+
ai_manager = ai_managers[f"{get_config().ai.open_ai_url}:{get_config().ai.open_ai_model}:{get_config().ai.open_ai_api_key}"]
|
|
388
|
+
logger.info(
|
|
389
|
+
f"Reusing existing OpenAI AI Manager for model: {get_config().ai.open_ai_model}")
|
|
390
|
+
else:
|
|
391
|
+
ai_manager = OpenAIManager(model=get_config().ai.open_ai_model, api_key=get_config(
|
|
392
|
+
).ai.open_ai_api_key, api_url=get_config().ai.open_ai_url, logger=logger)
|
|
393
|
+
if ai_manager:
|
|
394
|
+
ai_managers[ai_manager.model_name] = ai_manager
|
|
395
|
+
current_ai_config = get_config().ai
|
|
396
|
+
|
|
397
|
+
if not ai_manager:
|
|
398
|
+
logger.error(
|
|
399
|
+
"AI is enabled but the AI Manager did not initialize. Check your AI Config IN GSM.")
|
|
400
|
+
return ""
|
|
401
|
+
return ai_manager.process(lines, sentence, current_line, game_title, custom_prompt=custom_prompt)
|
|
402
|
+
except Exception as e:
|
|
403
|
+
logger.error(
|
|
404
|
+
"Error caught while trying to get AI prompt result. Check logs for more details.")
|
|
405
|
+
logger.debug(e, exc_info=True)
|
|
406
|
+
return ""
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def ai_config_changed(config, current):
|
|
410
|
+
if not current:
|
|
411
|
+
return True
|
|
412
|
+
if config.provider != current.provider:
|
|
413
|
+
return True
|
|
414
|
+
if config.provider == AIType.GEMINI.value and (config.gemini_api_key != current.gemini_api_key or config.gemini_model != current.gemini_model):
|
|
415
|
+
return True
|
|
416
|
+
if config.provider == AIType.GROQ.value and (config.groq_api_key != current.groq_api_key or config.groq_model != current.groq_model):
|
|
417
|
+
return True
|
|
418
|
+
if config.provider == AIType.OPENAI.value and config.gemini_model != current.gemini_model:
|
|
419
|
+
return True
|
|
420
|
+
if config.custom_prompt != current.custom_prompt:
|
|
421
|
+
return True
|
|
422
|
+
if config.use_canned_translation_prompt != current.use_canned_translation_prompt:
|
|
423
|
+
return True
|
|
424
|
+
if config.use_canned_context_prompt != current.use_canned_context_prompt:
|
|
425
|
+
return True
|
|
426
|
+
return False
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
if __name__ == '__main__':
|
|
430
|
+
logger.setLevel(logging.DEBUG)
|
|
431
|
+
console_handler = logging.StreamHandler()
|
|
432
|
+
console_handler.setLevel(logging.DEBUG)
|
|
433
|
+
logger.addHandler(console_handler)
|
|
434
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
435
|
+
lines = [
|
|
436
|
+
# Sexual/Explicit Japanese words and phrases
|
|
437
|
+
GameLine(index=0, text="ねぇ、あたしのおっぱい、揉んでみない?",
|
|
438
|
+
id=None, time=None, prev=None, next=None),
|
|
439
|
+
GameLine(index=1, text="お前、本当に痴女だな。股が開いてるぜ。",
|
|
440
|
+
id=None, time=None, prev=None, next=None),
|
|
441
|
+
GameLine(index=2, text="今夜は熱い夜にしましょうね…ふふ。",
|
|
442
|
+
id=None, time=None, prev=None, next=None),
|
|
443
|
+
GameLine(index=3, text="あぁ…もっと奥まで…ダメ…イッちゃう…!",
|
|
444
|
+
id=None, time=None, prev=None, next=None),
|
|
445
|
+
GameLine(index=4, text="あんたみたいなやつ、生きてる価値ないわ。さっさと自害しろ。", id=None, time=None, prev=None,
|
|
446
|
+
next=None),
|
|
447
|
+
GameLine(index=5, text="このブス!誰がお前なんかを相手にするかよ。",
|
|
448
|
+
id=None, time=None, prev=None, next=None),
|
|
449
|
+
GameLine(index=6, text="こんにちは、元気ですか?", id=None,
|
|
450
|
+
time=None, prev=None, next=None),
|
|
451
|
+
GameLine(index=7, text="次会ったら、ぶっ殺してやるからな。",
|
|
452
|
+
id=None, time=None, prev=None, next=None),
|
|
453
|
+
GameLine(index=8, text="今日はいい天気ですね。", id=None,
|
|
454
|
+
time=None, prev=None, next=None),
|
|
455
|
+
GameLine(index=9, text="お前の体、隅々まで味わい尽くしてやる。",
|
|
456
|
+
id=None, time=None, prev=None, next=None),
|
|
457
|
+
GameLine(index=10, text="自害しろ", id=None,
|
|
458
|
+
time=None, prev=None, next=None),
|
|
459
|
+
GameLine(index=11, text="この売女!金のために魂まで売るのか?!",
|
|
460
|
+
id=None, time=None, prev=None, next=None),
|
|
461
|
+
GameLine(index=12, text="俺の股間のモノで黙らせてやるよ。",
|
|
462
|
+
id=None, time=None, prev=None, next=None),
|
|
463
|
+
GameLine(index=13, text="くっ…イク…頭が…おかしくなりそう…!",
|
|
464
|
+
id=None, time=None, prev=None, next=None),
|
|
465
|
+
]
|
|
466
|
+
|
|
467
|
+
# lines = [
|
|
468
|
+
# # A back-and-forth dialogue of insults and threats
|
|
469
|
+
# GameLine(index=0, text="お前、ここで何をしている?目障りだ。",
|
|
470
|
+
# id=None, time=None, prev=None, next=None),
|
|
471
|
+
# GameLine(index=1, text="それはこっちのセリフだ。さっさと消えろ、クズが。", id=None, time=None, prev=None,
|
|
472
|
+
# next=None),
|
|
473
|
+
# GameLine(index=2, text="口だけは達者だな。やれるもんならやってみろよ。", id=None, time=None, prev=None,
|
|
474
|
+
# next=None),
|
|
475
|
+
# GameLine(index=3, text="くっ…!調子に乗るなよ…!", id=None,
|
|
476
|
+
# time=None, prev=None, next=None),
|
|
477
|
+
# GameLine(index=4, text="あんたみたいなやつ、生きてる価値ないわ。さっさと自害しろ。", id=None, time=None, prev=None,
|
|
478
|
+
# next=None),
|
|
479
|
+
# GameLine(index=5, text="この能無しが!誰がお前なんかを相手にするかよ。", id=None, time=None, prev=None,
|
|
480
|
+
# next=None),
|
|
481
|
+
# GameLine(index=6, text="黙れ。これ以上喋るなら、その舌を引っこ抜いてやる。", id=None, time=None, prev=None,
|
|
482
|
+
# next=None),
|
|
483
|
+
# GameLine(index=7, text="次会ったら、ぶっ殺してやるからな。",
|
|
484
|
+
# id=None, time=None, prev=None, next=None),
|
|
485
|
+
# GameLine(index=8, text="はっ、望むところだ。返り討ちにしてやる。",
|
|
486
|
+
# id=None, time=None, prev=None, next=None),
|
|
487
|
+
# GameLine(index=9, text="お前の顔も見たくない。地獄に落ちろ。",
|
|
488
|
+
# id=None, time=None, prev=None, next=None),
|
|
489
|
+
# GameLine(index=10, text="自害しろ", id=None,
|
|
490
|
+
# time=None, prev=None, next=None),
|
|
491
|
+
# GameLine(index=11, text="この臆病者が!逃げることしか能がないのか?!",
|
|
492
|
+
# id=None, time=None, prev=None, next=None),
|
|
493
|
+
# GameLine(index=12, text="俺の拳で黙らせてやるよ。", id=None,
|
|
494
|
+
# time=None, prev=None, next=None),
|
|
495
|
+
# GameLine(index=13, text="くそっ…覚えてろよ…!このままじゃ終わらせない…!", id=None, time=None, prev=None,
|
|
496
|
+
# next=None),
|
|
497
|
+
# ]
|
|
498
|
+
|
|
499
|
+
# Completely neutral Japanese sentences
|
|
500
|
+
#
|
|
501
|
+
lines = [
|
|
502
|
+
GameLine(index=0, text="今日はいい天気ですね。", id=None,
|
|
503
|
+
time=None, prev=None, next=None),
|
|
504
|
+
GameLine(index=1, text="おはようございます。", id=None,
|
|
505
|
+
time=None, prev=None, next=None),
|
|
506
|
+
GameLine(index=2, text="お元気ですか?", id=None,
|
|
507
|
+
time=None, prev=None, next=None),
|
|
508
|
+
GameLine(index=3, text="これはペンです。", id=None,
|
|
509
|
+
time=None, prev=None, next=None),
|
|
510
|
+
GameLine(index=4, text="私は学生です。", id=None,
|
|
511
|
+
time=None, prev=None, next=None),
|
|
512
|
+
GameLine(index=5, text="東京は日本の首都です。", id=None,
|
|
513
|
+
time=None, prev=None, next=None),
|
|
514
|
+
GameLine(index=6, text="こんにちは、元気ですか?", id=None,
|
|
515
|
+
time=None, prev=None, next=None),
|
|
516
|
+
GameLine(index=7, text="さようなら。また会いましょう。", id=None,
|
|
517
|
+
time=None, prev=None, next=None),
|
|
518
|
+
GameLine(index=8, text="ありがとう。助かりました。", id=None,
|
|
519
|
+
time=None, prev=None, next=None),
|
|
520
|
+
GameLine(index=9, text="すみません、道に迷いました。", id=None,
|
|
521
|
+
time=None, prev=None, next=None),
|
|
522
|
+
GameLine(index=10, text="これは本です。", id=None,
|
|
523
|
+
time=None, prev=None, next=None),
|
|
524
|
+
GameLine(index=11, text="私は先生です。", id=None,
|
|
525
|
+
time=None, prev=None, next=None),
|
|
526
|
+
]
|
|
527
|
+
|
|
528
|
+
sentence = "おはようございます。"
|
|
529
|
+
current_line = lines[3]
|
|
530
|
+
game_title = "Corrupted Reality"
|
|
531
|
+
|
|
532
|
+
results = []
|
|
533
|
+
|
|
534
|
+
get_config().ai.provider = AIType.GEMINI.value
|
|
535
|
+
models = [
|
|
536
|
+
'gemini-2.5-flash']
|
|
537
|
+
|
|
538
|
+
for model in models:
|
|
539
|
+
get_config().ai.gemini_model = model
|
|
540
|
+
start_time = time.time()
|
|
541
|
+
result = get_ai_prompt_result(lines, sentence, current_line, game_title, True)
|
|
542
|
+
results.append({"model": model, "response": result, "time": time.time() - start_time, "iteration": 1})
|
|
543
|
+
|
|
544
|
+
# get_config().ai.provider = AIType.OPENAI.value
|
|
545
|
+
# get_config().ai.open_ai_url = "https://api.openai.com/v1"
|
|
546
|
+
# get_config().ai.open_ai_model = "gpt-5-nano-2025-08-07"
|
|
547
|
+
|
|
548
|
+
# for i in range(5):
|
|
549
|
+
# start_time = time.time()
|
|
550
|
+
# result = get_ai_prompt_result(
|
|
551
|
+
# lines, sentence, current_line, game_title, True)
|
|
552
|
+
# results.append({"model": get_config().ai.open_ai_model,
|
|
553
|
+
# "response": result, "time": time.time() - start_time, "iteration": i})
|
|
554
|
+
|
|
555
|
+
# get_config().ai.provider = AIType.OPENAI.value
|
|
556
|
+
# models = [
|
|
557
|
+
# # 'openai/gpt-oss-20b',
|
|
558
|
+
# # 'meta-llama-3.1-8b-instruct',
|
|
559
|
+
# 'google/gemma-3n-e4b',
|
|
560
|
+
# # 'google/gemma-2-2b-it',
|
|
561
|
+
# # 'google/gemma-2b-it',
|
|
562
|
+
# # 'facebook/nllb-200-distilled-600M',
|
|
563
|
+
# # 'meta-llama/Llama-3.2-1B-Instruct',
|
|
564
|
+
# # 'facebook/nllb-200-1.3B'
|
|
565
|
+
# ]
|
|
566
|
+
|
|
567
|
+
# results = []
|
|
568
|
+
|
|
569
|
+
# # for model in models:
|
|
570
|
+
# # get_config().ai.local_model = model
|
|
571
|
+
# # start_time = time.time()
|
|
572
|
+
# # result = get_ai_prompt_result(lines, sentence, current_line, game_title, True)
|
|
573
|
+
# # results.append({"model": model,"response": result, "time": time.time() - start_time, "iteration": 1})
|
|
574
|
+
|
|
575
|
+
# # Second Time after Already Loaded
|
|
576
|
+
|
|
577
|
+
# get_config().ai.open_ai_url = "http://127.0.0.1:1234/v1"
|
|
578
|
+
# get_config().ai.open_ai_api_key = "lm-studio"
|
|
579
|
+
# for i in range(1, 10):
|
|
580
|
+
# for model in models:
|
|
581
|
+
# get_config().ai.open_ai_model = model
|
|
582
|
+
# start_time = time.time()
|
|
583
|
+
# result = get_ai_prompt_result(lines, sentence, current_line, game_title, True)
|
|
584
|
+
# print(result)
|
|
585
|
+
# results.append({"model": model, "response": result, "time": time.time() - start_time, "iteration": i})
|
|
586
|
+
# results[model] = {"response": result, "time": time.time() - start_time}
|
|
587
|
+
|
|
588
|
+
# get_config().ai.provider = "Gemini"
|
|
589
|
+
#
|
|
590
|
+
# models = ['gemini-2.5-flash','gemini-2.0-flash', 'gemini-2.0-flash-lite',
|
|
591
|
+
# 'gemini-2.5-flash-lite-preview-06-17']
|
|
592
|
+
# # results = {}
|
|
593
|
+
# for model in models:
|
|
594
|
+
# get_config().ai.gemini_model = model
|
|
595
|
+
# start_time = time.time()
|
|
596
|
+
# result = get_ai_prompt_result(lines, sentence, current_line, game_title, True)
|
|
597
|
+
# results.append({"model": model, "response": result, "time": time.time() - start_time, "iteration": 1})
|
|
598
|
+
# # results[model] = {"response": result, "time": time.time() - start_time}
|
|
599
|
+
#
|
|
600
|
+
print("Summary of results:")
|
|
601
|
+
times = []
|
|
602
|
+
for result in results:
|
|
603
|
+
times.append(result['time'])
|
|
604
|
+
print(
|
|
605
|
+
f"Model: {result['model']}\nResult: {result['response']}\nTime: {result['time']:.2f} seconds\n{'-'*80}\n")
|
|
606
|
+
|
|
607
|
+
print(
|
|
608
|
+
f"Average time: {sum(times)/len(times):.2f} seconds over {len(times)} runs.")
|
|
609
|
+
# Set up logging
|
|
610
|
+
|
|
611
|
+
# Test the function
|