GameSentenceMiner 0.0.0.dev0__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-0.0.0.dev0/GameSentenceMiner/__init__.py +39 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/__init__.py +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/ai_prompting.py +121 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/contracts.py +33 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/features/__init__.py +1 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/features/character_context.py +50 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/features/character_summary.py +25 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/parsing/__init__.py +1 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/parsing/output_parser.py +29 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/prompts/__init__.py +1 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/prompts/builder.py +118 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/prompts/templates.py +74 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/providers/__init__.py +1 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/providers/base.py +10 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/providers/gemini_client.py +107 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/providers/groq_client.py +51 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/providers/ollama_client.py +58 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/providers/openai_client.py +91 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/registry.py +119 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ai/service.py +230 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/anki.py +1769 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/assets/__init__.py +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/assets/anki_confirmation_example.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/assets/icon.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/assets/icon128.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/assets/icon256.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/assets/icon32.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/assets/icon512.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/assets/icon64.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/assets/pickaxe.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/gametext.py +478 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/gsm.py +849 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/locales/en_us.json +806 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/locales/es_es.json +793 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/locales/ja_jp.json +761 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/locales/zh_cn.json +750 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/longplay_handler.py +311 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/LICENSE +661 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/README.md +29 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/__init__.py +9 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/__main__.py +15 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/basic_mecab_controller.py +148 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/basic_types.py +128 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/compound_furigana.py +93 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/format.py +70 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/kana_conv.py +80 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/lru_cache.py +46 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/mecab_controller.py +182 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/mecab_exe_finder.py +45 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/replace_mistakes.py +284 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/LICENSE +5 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/LICENSE.MECAB-IPADIC +70 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/char.bin +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/dicrc +29 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/itaijidict +564 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/kanwadict +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/libmecab.2.dylib +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/libmecab.dll +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/libmecab.so.1 +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/matrix.bin +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/mecab.exe +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/mecab.lin +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/mecab.mac +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/mecabrc +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/sys.dic +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/unk.dic +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/support/user_dic.dic +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/mecab/unify_readings.py +136 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/obs.py +2313 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/__init__.py +96 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/compare.py +199 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/coordinate_math.py +91 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/gsm_ocr.py +2401 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/gsm_ocr_config.py +283 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/image_scaling.py +363 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/ocr_format_converter.py +337 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/ocrconfig.py +136 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/owocr_area_selector_qt.py +1501 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/owocr_helper.py +5 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/ss_picker_qt.py +598 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ocr/two_pass_ocr.py +28 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/__init__.py +1 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/__main__.py +9 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/config.py +149 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_betterproto.py +1237 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/aim_communication_pb2.py +48 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/aim_query_pb2.py +44 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_client_context_pb2.py +48 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_client_logs_pb2.py +40 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_client_platform_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_cluster_info_pb2.py +38 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_contextual_inputs_pb2.py +40 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_deep_gleam_data_pb2.py +54 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_document_pb2.py +39 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_filters_pb2.py +43 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_geometry_pb2.py +42 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_image_crop_pb2.py +39 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_image_data_pb2.py +42 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_interaction_request_metadata_pb2.py +51 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_knowledge_intent_query_pb2.py +46 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_knowledge_query_pb2.py +38 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_math_solver_query_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_message_set_pb2.py +38 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_overlay_object_pb2.py +44 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_payload_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_phase_latencies_metadata_pb2.py +45 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_platform_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_polygon_pb2.py +43 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_request_id_pb2.py +40 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_request_type_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_routing_info_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_selection_type_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_server_pb2.py +55 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_service_deps_pb2.py +78 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_stickiness_signals_pb2.py +43 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_surface_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_text_pb2.py +56 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_text_query_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_translate_stickiness_signals_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_video_context_input_params_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_video_params_pb2.py +38 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_visual_input_type_pb2.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_visual_search_interaction_data_pb2.py +42 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/lens_protos/lens_overlay_visual_search_interaction_log_data_pb2.py +44 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/ocr.py +5402 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/ocr_upstream.py +2530 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/run.py +3407 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/owocr/owocr/screen_ai_downloader.py +219 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/replay_handler.py +450 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/tools/__init__.py +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/tools/window_transparency.py +214 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/__init__.py +230 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/anki_confirmation_qt.py +1051 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/audio_waveform_widget.py +309 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/__init__.py +3 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/binding.py +127 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/editor.py +70 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/i18n.py +21 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/labels.py +53 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/prompt_help.py +114 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/services/__init__.py +3 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/services/ai_models.py +135 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/styles.py +87 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/__init__.py +3 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/advanced.py +50 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/ai.py +289 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/anki.py +307 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/audio.py +62 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/experimental.py +147 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/features.py +25 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/general.py +281 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/gsm_cloud.py +29 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/obs.py +72 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/overlay.py +80 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/paths.py +47 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/port_widget.py +91 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/profiles.py +58 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/required.py +309 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/screenshot.py +66 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/text_processing.py +281 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/vad.py +67 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config/tabs/websocket_sources.py +159 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/config_gui_qt.py +3240 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/furigana_filter_preview_qt.py +828 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/qt_main.py +480 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/ui/screenshot_selector_qt.py +393 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/__init__.py +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/clients/__init__.py +4 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/clients/anilist_api_client.py +896 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/clients/discord_rpc.py +230 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/clients/jiten_api_client.py +369 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/clients/vndb_api_client.py +840 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cloud_sync/__init__.py +3 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cloud_sync/service.py +616 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/communication/__init__.py +21 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/communication/electron_ipc.py +124 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/communication/ocr_ipc.py +200 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/communication/send.py +19 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/config/__init__.py +3 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/config/configuration.py +2269 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/config/electron_config.py +482 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/config/feature_flags.py +40 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/__init__.py +12 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/daily_rollup.py +781 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/jiten_update.py +843 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/jiten_upgrader.py +341 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/jiten_upgrader_cron.py +178 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/populate_games.py +154 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/run_crons.py +314 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/setup_populate_games_cron.py +119 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/setup_user_plugins_cron.py +107 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/cron/user_plugins.py +97 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/database/cron_table.py +371 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/database/db.py +1757 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/database/games_table.py +643 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/database/stats_rollup_table.py +227 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/docs.py +7 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/downloader/Untitled_json.py +585 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/downloader/__init__.py +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/downloader/download_tools.py +418 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/downloader/oneocr_dl.py +300 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/gsm_cloud_auth_cache.py +109 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/gsm_utils.py +731 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/logging_config.py +403 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/media/__init__.py +2 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/media/audio_player.py +322 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/media/ffmpeg.py +1159 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/models/__init__.py +1 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/models/model.py +232 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/overlay/__init__.py +2 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/overlay/get_overlay_coords.py +1809 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/platform/__init__.py +8 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/platform/hotkey.py +215 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/platform/magpie_compat.py +91 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/platform/notification.py +194 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/platform/window_state_monitor.py +1854 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/port_diagnostics.py +178 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/shared/__init__.py +50 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/shared/base_api_client.py +260 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/shared/game_update_service.py +346 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/shared/image_utils.py +265 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/shared/spoiler_utils.py +204 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/stats/__init__.py +3 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/stats/live_stats.py +77 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/stats/stats_rollup_table.py +227 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/stats/stats_util.py +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/text_log.py +247 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/text_processing.py +67 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/yomitan_dict/__init__.py +24 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/yomitan_dict/content_builder.py +419 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/yomitan_dict/dict_builder.py +379 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/yomitan_dict/image_handler.py +94 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/util/yomitan_dict/name_parser.py +318 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/vad.py +488 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/__init__.py +21 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/anki_api_endpoints.py +923 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/cloud_sync_api.py +147 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/database_api.py +2660 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/events.py +95 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/goals_api.py +2261 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/gsm_websocket.py +718 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/jiten_database_api.py +28 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/overlay_handler.py +260 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/read_only_stats.py +7 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/rollup_stats.py +953 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/routes/__init__.py +40 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/routes/cron_routes.py +79 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/routes/debug_routes.py +68 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/routes/game_management_routes.py +579 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/routes/jiten_linking_routes.py +610 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/routes/search_routes.py +383 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/service.py +350 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/__init__.py +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/css/dashboard-shared.css +349 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/css/goals.css +373 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/css/kanji-grid.css +202 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/css/loading-skeleton.css +41 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/css/overview.css +1514 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/css/popups-shared.css +126 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/css/search.css +614 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/css/shared.css +1854 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/css/stats.css +1366 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/favicon-96x96.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/favicon.ico +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/favicon.svg +4 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/anki_stats.js +478 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/components/bar-chart.js +339 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/database-bulk-operations.js +320 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/database-game-data.js +390 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/database-game-operations.js +213 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/database-helpers.js +44 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/database-jiten-integration.js +1123 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/database-popups.js +89 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/database-tabs.js +64 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/database-text-management.js +371 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/database.js +315 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/goals.js +2506 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/heatmap.js +489 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/kanji-grid.js +466 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/overview.js +2298 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/regex-patterns.js +100 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/search.js +780 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/shared.js +770 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/stats.js +2999 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/js/unified-search.js +446 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/site.webmanifest +21 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/style.css +292 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/stats.py +1644 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/stats_api.py +2344 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/anki_stats.html +378 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/date-range.html +144 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/html-head.html +79 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/js-config.html +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/basic_kanji_book_bkb_v1_v2.json +17 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/duolingo_kanji.json +29 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/grade.json +17 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/hk_primary_learning.json +17 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/hkscs2016.json +13 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/hsk_levels.json +33 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/humanum_frequency_list.json +41 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/jis_levels.json +25 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/jlpt_level.json +29 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/jpdb_kanji_frequency_list.json +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/jpdbv2_kanji_frequency_list.json +161 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/jun_das_modern_chinese_character_frequency_list.json +13 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/kanji_in_context_revised_edition.json +37 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/kanji_kentei_level.json +61 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/mainland_china_elementary_textbook_characters.json +33 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/moe_way_quiz.json +47 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/official_kanji.json +25 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/remembering_the_kanji.json +25 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/standard_form_of_national_characters.json +25 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/table_of_general_standard_chinese_characters.json +21 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/the_kodansha_kanji_learners_course_klc.json +45 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/thousand_character_classic.json +13 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/wanikani_levels.json +249 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/kanji_grid/words_hk_frequency_list.json +33 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/navigation.html +21 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/popups.html +15 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/regex-input.html +160 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/settings-modal.html +146 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/components/theme-styles.html +128 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/database.html +927 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/goals.html +518 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/index.html +51 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/overview.html +286 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/search.html +264 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/stats.html +373 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/templates/utility.html +483 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/texthooking_page.py +1359 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/web/yomitan_api.py +339 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner/wip/__init___.py +0 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner.egg-info/PKG-INFO +211 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner.egg-info/SOURCES.txt +346 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner.egg-info/dependency_links.txt +1 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner.egg-info/entry_points.txt +2 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner.egg-info/requires.txt +72 -0
- gamesentenceminer-0.0.0.dev0/GameSentenceMiner.egg-info/top_level.txt +1 -0
- gamesentenceminer-0.0.0.dev0/LICENSE +674 -0
- gamesentenceminer-0.0.0.dev0/PKG-INFO +211 -0
- gamesentenceminer-0.0.0.dev0/README.md +130 -0
- gamesentenceminer-0.0.0.dev0/pyproject.toml +116 -0
- gamesentenceminer-0.0.0.dev0/setup.cfg +4 -0
- gamesentenceminer-0.0.0.dev0/tests/test_anki.py +667 -0
- gamesentenceminer-0.0.0.dev0/tests/test_port_diagnostics.py +114 -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'
|
|
File without changes
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from GameSentenceMiner.ai.features.character_summary import CharacterSummaryService
|
|
5
|
+
from GameSentenceMiner.ai.service import AIService, snapshot_config
|
|
6
|
+
from GameSentenceMiner.util.config.configuration import (
|
|
7
|
+
AI_GEMINI,
|
|
8
|
+
AI_GROQ,
|
|
9
|
+
AI_GSM_CLOUD,
|
|
10
|
+
AI_LM_STUDIO,
|
|
11
|
+
AI_OLLAMA,
|
|
12
|
+
AI_OPENAI,
|
|
13
|
+
Ai,
|
|
14
|
+
get_config,
|
|
15
|
+
logger,
|
|
16
|
+
)
|
|
17
|
+
from GameSentenceMiner.util.text_log import GameLine
|
|
18
|
+
|
|
19
|
+
# Suppress debug logs from httpcore
|
|
20
|
+
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
|
21
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
22
|
+
logging.getLogger("groq._base_client").setLevel(logging.WARNING)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_ai_prompt_result(
|
|
26
|
+
lines: List[GameLine],
|
|
27
|
+
sentence: str,
|
|
28
|
+
current_line: GameLine,
|
|
29
|
+
game_title: str = "",
|
|
30
|
+
force_refresh: bool = False,
|
|
31
|
+
custom_prompt=None,
|
|
32
|
+
) -> str:
|
|
33
|
+
try:
|
|
34
|
+
config = get_config()
|
|
35
|
+
snapshot = snapshot_config(config.ai, config.general)
|
|
36
|
+
service = AIService(config_snapshot=snapshot, logger=logger)
|
|
37
|
+
return service.translate(
|
|
38
|
+
lines=lines,
|
|
39
|
+
sentence=sentence,
|
|
40
|
+
current_line=current_line,
|
|
41
|
+
game_title=game_title,
|
|
42
|
+
custom_prompt=custom_prompt,
|
|
43
|
+
)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
logger.error(
|
|
46
|
+
"Error caught while trying to get AI prompt result. Check logs for more details.",
|
|
47
|
+
exc_info=True,
|
|
48
|
+
)
|
|
49
|
+
logger.debug(e, exc_info=True)
|
|
50
|
+
return ""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def ai_config_changed(config: Ai, current: Optional[Ai]) -> bool:
|
|
54
|
+
if not current:
|
|
55
|
+
return True
|
|
56
|
+
if config.provider != current.provider:
|
|
57
|
+
return True
|
|
58
|
+
if config.provider == AI_GEMINI and (
|
|
59
|
+
config.gemini_api_key != current.gemini_api_key
|
|
60
|
+
or config.gemini_model != current.gemini_model
|
|
61
|
+
or config.gemini_backup_model != current.gemini_backup_model
|
|
62
|
+
):
|
|
63
|
+
return True
|
|
64
|
+
if config.provider == AI_GROQ and (
|
|
65
|
+
config.groq_api_key != current.groq_api_key
|
|
66
|
+
or config.groq_model != current.groq_model
|
|
67
|
+
or config.groq_backup_model != current.groq_backup_model
|
|
68
|
+
):
|
|
69
|
+
return True
|
|
70
|
+
if config.provider == AI_OPENAI and (
|
|
71
|
+
config.open_ai_api_key != current.open_ai_api_key
|
|
72
|
+
or config.open_ai_model != current.open_ai_model
|
|
73
|
+
or config.open_ai_backup_model != current.open_ai_backup_model
|
|
74
|
+
or config.open_ai_url != current.open_ai_url
|
|
75
|
+
):
|
|
76
|
+
return True
|
|
77
|
+
if config.provider == AI_GSM_CLOUD and (
|
|
78
|
+
config.gsm_cloud_access_token != current.gsm_cloud_access_token
|
|
79
|
+
or config.gsm_cloud_models != current.gsm_cloud_models
|
|
80
|
+
or config.gsm_cloud_api_url != current.gsm_cloud_api_url
|
|
81
|
+
):
|
|
82
|
+
return True
|
|
83
|
+
if config.provider == AI_OLLAMA and (
|
|
84
|
+
config.ollama_url != current.ollama_url
|
|
85
|
+
or config.ollama_model != current.ollama_model
|
|
86
|
+
or config.ollama_backup_model != current.ollama_backup_model
|
|
87
|
+
):
|
|
88
|
+
return True
|
|
89
|
+
if config.provider == AI_LM_STUDIO and (
|
|
90
|
+
config.lm_studio_url != current.lm_studio_url
|
|
91
|
+
or config.lm_studio_model != current.lm_studio_model
|
|
92
|
+
or config.lm_studio_backup_model != current.lm_studio_backup_model
|
|
93
|
+
or config.lm_studio_api_key != current.lm_studio_api_key
|
|
94
|
+
):
|
|
95
|
+
return True
|
|
96
|
+
if config.custom_prompt != current.custom_prompt:
|
|
97
|
+
return True
|
|
98
|
+
if config.custom_full_prompt != current.custom_full_prompt:
|
|
99
|
+
return True
|
|
100
|
+
if config.use_canned_translation_prompt != current.use_canned_translation_prompt:
|
|
101
|
+
return True
|
|
102
|
+
if config.use_canned_context_prompt != current.use_canned_context_prompt:
|
|
103
|
+
return True
|
|
104
|
+
if config.temperature != current.temperature:
|
|
105
|
+
return True
|
|
106
|
+
if config.max_output_tokens != current.max_output_tokens:
|
|
107
|
+
return True
|
|
108
|
+
if config.top_p != current.top_p:
|
|
109
|
+
return True
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def generate_character_summary(character_data: dict) -> Optional[str]:
|
|
114
|
+
if not character_data:
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
config = get_config()
|
|
118
|
+
snapshot = snapshot_config(config.ai, config.general)
|
|
119
|
+
service = AIService(config_snapshot=snapshot, logger=logger)
|
|
120
|
+
summary_service = CharacterSummaryService(logger=logger)
|
|
121
|
+
return summary_service.generate_from_vndb(character_data, service)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Optional, Dict, Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class AIRequest:
|
|
9
|
+
provider: str
|
|
10
|
+
model: str
|
|
11
|
+
prompt: str
|
|
12
|
+
temperature: float
|
|
13
|
+
top_p: float
|
|
14
|
+
max_tokens: int
|
|
15
|
+
game_title: str = ""
|
|
16
|
+
request_kind: str = "translation"
|
|
17
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class AIResponse:
|
|
22
|
+
provider: str
|
|
23
|
+
model: str
|
|
24
|
+
text: str
|
|
25
|
+
raw_text: str
|
|
26
|
+
latency_ms: int
|
|
27
|
+
usage: Optional[Dict[str, Any]] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AIError(Exception):
|
|
31
|
+
def __init__(self, message: str, transient: bool = False):
|
|
32
|
+
super().__init__(message)
|
|
33
|
+
self.transient = transient
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CharacterContextProvider:
|
|
7
|
+
def __init__(self, summary_service, logger):
|
|
8
|
+
self.summary_service = summary_service
|
|
9
|
+
self.logger = logger
|
|
10
|
+
|
|
11
|
+
def get_character_context(self, game_title: str, ai_service) -> str:
|
|
12
|
+
if not game_title:
|
|
13
|
+
return ""
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from GameSentenceMiner.util.database.games_table import GamesTable
|
|
17
|
+
|
|
18
|
+
game = GamesTable.get_by_obs_scene_name(game_title)
|
|
19
|
+
if not game:
|
|
20
|
+
game = GamesTable.get_by_title(game_title)
|
|
21
|
+
|
|
22
|
+
if not game:
|
|
23
|
+
return ""
|
|
24
|
+
|
|
25
|
+
self.logger.debug(
|
|
26
|
+
f"Found game '{game.title_original}' (id={game.id}) for scene '{game_title}'"
|
|
27
|
+
)
|
|
28
|
+
if game.character_summary:
|
|
29
|
+
return game.character_summary
|
|
30
|
+
|
|
31
|
+
if game.vndb_character_data:
|
|
32
|
+
try:
|
|
33
|
+
if isinstance(game.vndb_character_data, dict):
|
|
34
|
+
vndb_data = game.vndb_character_data
|
|
35
|
+
else:
|
|
36
|
+
vndb_data = json.loads(game.vndb_character_data)
|
|
37
|
+
summary = self.summary_service.generate_from_vndb(vndb_data, ai_service)
|
|
38
|
+
if summary:
|
|
39
|
+
game.character_summary = summary
|
|
40
|
+
game.save()
|
|
41
|
+
self.logger.info(f"Generated and stored character summary for {game_title}")
|
|
42
|
+
return summary
|
|
43
|
+
except json.JSONDecodeError:
|
|
44
|
+
self.logger.warning(f"Failed to parse VNDB data for {game_title}")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
self.logger.error(f"Failed to generate character summary for {game_title}: {e}")
|
|
47
|
+
except Exception as e:
|
|
48
|
+
self.logger.error(f"Error fetching character context: {e}")
|
|
49
|
+
|
|
50
|
+
return ""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from GameSentenceMiner.ai.prompts.templates import CHARACTER_SUMMARY_PROMPT
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CharacterSummaryService:
|
|
10
|
+
def __init__(self, logger):
|
|
11
|
+
self.logger = logger
|
|
12
|
+
|
|
13
|
+
def generate_from_vndb(self, character_data: dict, ai_service) -> Optional[str]:
|
|
14
|
+
if not character_data:
|
|
15
|
+
return None
|
|
16
|
+
|
|
17
|
+
character_json = json.dumps(character_data, ensure_ascii=False, indent=2)
|
|
18
|
+
prompt = CHARACTER_SUMMARY_PROMPT.format(character_json=character_json)
|
|
19
|
+
try:
|
|
20
|
+
result = ai_service.generate_raw_prompt(prompt, request_kind="character_summary")
|
|
21
|
+
if result:
|
|
22
|
+
return result.strip()
|
|
23
|
+
except Exception as e:
|
|
24
|
+
self.logger.error(f"Failed to generate character summary: {e}")
|
|
25
|
+
return None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class OutputParser:
|
|
10
|
+
compat_mode: bool = True
|
|
11
|
+
|
|
12
|
+
def parse(self, raw_text: str) -> str:
|
|
13
|
+
if not raw_text:
|
|
14
|
+
return raw_text
|
|
15
|
+
|
|
16
|
+
if not self.compat_mode:
|
|
17
|
+
return raw_text
|
|
18
|
+
|
|
19
|
+
if "{" in raw_text and "}" in raw_text:
|
|
20
|
+
try:
|
|
21
|
+
json_output = raw_text[raw_text.find("{"):raw_text.rfind("}") + 1]
|
|
22
|
+
json_output = json_output.replace("{output:", '{"output":')
|
|
23
|
+
parsed = json.loads(json_output)
|
|
24
|
+
if isinstance(parsed, dict) and "output" in parsed:
|
|
25
|
+
return parsed["output"]
|
|
26
|
+
except Exception:
|
|
27
|
+
return raw_text
|
|
28
|
+
|
|
29
|
+
return raw_text
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from GameSentenceMiner.ai.prompts.templates import (
|
|
7
|
+
DIALOGUE_CONTEXT_TEMPLATE,
|
|
8
|
+
FULL_PROMPT_TEMPLATE,
|
|
9
|
+
build_context_prompt,
|
|
10
|
+
build_translation_prompt,
|
|
11
|
+
)
|
|
12
|
+
from GameSentenceMiner.util.text_log import GameLine
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class PromptSelection:
|
|
17
|
+
prompt_text: str
|
|
18
|
+
prompt_kind: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DialogueContextBuilder:
|
|
22
|
+
@staticmethod
|
|
23
|
+
def build(lines: List[GameLine], current_line: GameLine, context_length: int) -> str:
|
|
24
|
+
if context_length == 0:
|
|
25
|
+
return "No dialogue context available."
|
|
26
|
+
|
|
27
|
+
if context_length == -1:
|
|
28
|
+
start_index = 0
|
|
29
|
+
end_index = len(lines)
|
|
30
|
+
else:
|
|
31
|
+
start_index = max(0, current_line.index - context_length)
|
|
32
|
+
end_index = min(len(lines), current_line.index + 1 + context_length)
|
|
33
|
+
|
|
34
|
+
context_lines_text = []
|
|
35
|
+
for i in range(start_index, end_index):
|
|
36
|
+
if i < len(lines):
|
|
37
|
+
context_lines_text.append(lines[i].text)
|
|
38
|
+
|
|
39
|
+
return DIALOGUE_CONTEXT_TEMPLATE.format("\n".join(context_lines_text))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class PromptSelector:
|
|
43
|
+
@staticmethod
|
|
44
|
+
def select(
|
|
45
|
+
use_canned_translation_prompt: bool,
|
|
46
|
+
use_canned_context_prompt: bool,
|
|
47
|
+
custom_prompt: str,
|
|
48
|
+
native_language_name: str,
|
|
49
|
+
custom_prompt_override: Optional[str] = None,
|
|
50
|
+
) -> PromptSelection:
|
|
51
|
+
if custom_prompt_override:
|
|
52
|
+
return PromptSelection(prompt_text=custom_prompt_override, prompt_kind="custom")
|
|
53
|
+
if use_canned_translation_prompt:
|
|
54
|
+
return PromptSelection(
|
|
55
|
+
prompt_text=build_translation_prompt(native_language_name),
|
|
56
|
+
prompt_kind="translation",
|
|
57
|
+
)
|
|
58
|
+
if use_canned_context_prompt:
|
|
59
|
+
return PromptSelection(
|
|
60
|
+
prompt_text=build_context_prompt(native_language_name),
|
|
61
|
+
prompt_kind="context",
|
|
62
|
+
)
|
|
63
|
+
return PromptSelection(prompt_text=custom_prompt, prompt_kind="custom")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class FullPromptRenderer:
|
|
67
|
+
@staticmethod
|
|
68
|
+
def render(
|
|
69
|
+
game_title: str,
|
|
70
|
+
character_context: str,
|
|
71
|
+
dialogue_context: str,
|
|
72
|
+
prompt_to_use: str,
|
|
73
|
+
sentence: str,
|
|
74
|
+
) -> str:
|
|
75
|
+
return FULL_PROMPT_TEMPLATE.format(
|
|
76
|
+
game_title=game_title or "Unknown",
|
|
77
|
+
character_context=character_context,
|
|
78
|
+
dialogue_context=dialogue_context,
|
|
79
|
+
prompt_to_use=prompt_to_use,
|
|
80
|
+
sentence=sentence,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class PromptBuilder:
|
|
85
|
+
def __init__(self, native_language_name: str):
|
|
86
|
+
self.native_language_name = native_language_name
|
|
87
|
+
|
|
88
|
+
def build(
|
|
89
|
+
self,
|
|
90
|
+
lines: List[GameLine],
|
|
91
|
+
sentence: str,
|
|
92
|
+
current_line: GameLine,
|
|
93
|
+
game_title: str,
|
|
94
|
+
dialogue_context_length: int,
|
|
95
|
+
use_canned_translation_prompt: bool,
|
|
96
|
+
use_canned_context_prompt: bool,
|
|
97
|
+
custom_prompt: str,
|
|
98
|
+
custom_prompt_override: Optional[str] = None,
|
|
99
|
+
character_context: str = "",
|
|
100
|
+
) -> tuple[str, str]:
|
|
101
|
+
dialogue_context = DialogueContextBuilder.build(
|
|
102
|
+
lines, current_line, dialogue_context_length
|
|
103
|
+
)
|
|
104
|
+
selection = PromptSelector.select(
|
|
105
|
+
use_canned_translation_prompt=use_canned_translation_prompt,
|
|
106
|
+
use_canned_context_prompt=use_canned_context_prompt,
|
|
107
|
+
custom_prompt=custom_prompt,
|
|
108
|
+
native_language_name=self.native_language_name,
|
|
109
|
+
custom_prompt_override=custom_prompt_override,
|
|
110
|
+
)
|
|
111
|
+
full_prompt = FullPromptRenderer.render(
|
|
112
|
+
game_title=game_title,
|
|
113
|
+
character_context=character_context,
|
|
114
|
+
dialogue_context=dialogue_context,
|
|
115
|
+
prompt_to_use=selection.prompt_text,
|
|
116
|
+
sentence=sentence,
|
|
117
|
+
)
|
|
118
|
+
return full_prompt, selection.prompt_kind
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def build_translation_prompt(native_language_name: str) -> str:
|
|
5
|
+
return f"""
|
|
6
|
+
**Professional Game Localization Task**
|
|
7
|
+
|
|
8
|
+
**Task Directive:**
|
|
9
|
+
Translate ONLY the provided line(s) of game dialogue specified below into natural-sounding, context-aware {native_language_name}. The translation must preserve the original tone and intent of the source.
|
|
10
|
+
|
|
11
|
+
**Output Requirements:**
|
|
12
|
+
- Provide only the single, best {native_language_name} translation.
|
|
13
|
+
- Expletives are okay, only if they absolutely 100% fit the context and tone of the original line, and are commonly used in {native_language_name} localizations of similar games.
|
|
14
|
+
- 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.
|
|
15
|
+
- If there are no HTML tags present in the original text, do not add any in the translation whatsoever.
|
|
16
|
+
- Do not include notes, alternatives, explanations, or any other surrounding text. Absolutely nothing but the translated line(s).
|
|
17
|
+
|
|
18
|
+
**Line(s) to Translate:**
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_context_prompt(native_language_name: str) -> str:
|
|
23
|
+
return f"""
|
|
24
|
+
|
|
25
|
+
**Task Directive:**
|
|
26
|
+
Provide a very brief summary of the scene in {native_language_name} based on the provided dialogue and context. Focus on the characters' actions and the immediate situation being described.
|
|
27
|
+
|
|
28
|
+
Current Line(s):
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
DIALOGUE_CONTEXT_TEMPLATE = """
|
|
33
|
+
Dialogue Context:
|
|
34
|
+
|
|
35
|
+
{0}
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
FULL_PROMPT_TEMPLATE = """
|
|
40
|
+
**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.
|
|
41
|
+
|
|
42
|
+
Character Context:
|
|
43
|
+
{character_context}
|
|
44
|
+
|
|
45
|
+
{dialogue_context}
|
|
46
|
+
|
|
47
|
+
{prompt_to_use}
|
|
48
|
+
|
|
49
|
+
{sentence}
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
CHARACTER_SUMMARY_PROMPT = """
|
|
54
|
+
You are a helpful assistant that creates concise character summaries for game localization.
|
|
55
|
+
|
|
56
|
+
Given the following character data from a visual novel, create a CHARACTER LIST in this exact format:
|
|
57
|
+
|
|
58
|
+
**CHARACTER LIST**:
|
|
59
|
+
[Japanese Name] -> [Romanized Name] (brief one-line description)
|
|
60
|
+
|
|
61
|
+
Rules:
|
|
62
|
+
- Include age if available (e.g., "17yo")
|
|
63
|
+
- Include gender (male/female)
|
|
64
|
+
- Include 2-3 key personality traits that will aid in translation.
|
|
65
|
+
- Keep each line under 120 characters
|
|
66
|
+
- Use Format Japanese name (romanization name): tags
|
|
67
|
+
- Mention what pronoun they use and mark it as their pronoun if they have one listed
|
|
68
|
+
- Example: 陽見 恵凪 (Harumi Ena): Clumsy, Dandere, Hotblooded 19yo girl atashi pronoun
|
|
69
|
+
|
|
70
|
+
Character Data:
|
|
71
|
+
{character_json}
|
|
72
|
+
|
|
73
|
+
Generate the CHARACTER LIST now:
|
|
74
|
+
"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from google import genai
|
|
5
|
+
from google.genai import types
|
|
6
|
+
from typing import Optional, Any, Dict
|
|
7
|
+
|
|
8
|
+
from GameSentenceMiner.ai.contracts import AIRequest, AIResponse, AIError
|
|
9
|
+
from GameSentenceMiner.util.config.configuration import normalize_gemini_model_name
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GeminiClient:
|
|
13
|
+
def __init__(self, api_key: str, model_name: str, logger):
|
|
14
|
+
self.api_key = api_key
|
|
15
|
+
self.model_name = model_name
|
|
16
|
+
self.logger = logger
|
|
17
|
+
self.client = None
|
|
18
|
+
self._safety_settings = [
|
|
19
|
+
types.SafetySetting(
|
|
20
|
+
category=types.HarmCategory.HARM_CATEGORY_HARASSMENT,
|
|
21
|
+
threshold=types.HarmBlockThreshold.BLOCK_NONE,
|
|
22
|
+
),
|
|
23
|
+
types.SafetySetting(
|
|
24
|
+
category=types.HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
|
25
|
+
threshold=types.HarmBlockThreshold.BLOCK_NONE,
|
|
26
|
+
),
|
|
27
|
+
types.SafetySetting(
|
|
28
|
+
category=types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
|
29
|
+
threshold=types.HarmBlockThreshold.BLOCK_NONE,
|
|
30
|
+
),
|
|
31
|
+
types.SafetySetting(
|
|
32
|
+
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
|
33
|
+
threshold=types.HarmBlockThreshold.BLOCK_NONE,
|
|
34
|
+
),
|
|
35
|
+
]
|
|
36
|
+
try:
|
|
37
|
+
self.client = genai.Client(api_key=self.api_key)
|
|
38
|
+
except Exception as e:
|
|
39
|
+
self.logger.error(f"Failed to initialize Gemini API: {e}")
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def _get_thinking_budget(model_name: str) -> Optional[int]:
|
|
43
|
+
model = (model_name or "").lower()
|
|
44
|
+
if "gemini-2.5" in model or "gemini-3" in model:
|
|
45
|
+
return -1 if "-pro" in model else 0
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
def _build_generation_config(self, request: AIRequest, model_name: str) -> types.GenerateContentConfig:
|
|
49
|
+
config = types.GenerateContentConfig(
|
|
50
|
+
temperature=request.temperature,
|
|
51
|
+
max_output_tokens=request.max_tokens,
|
|
52
|
+
top_p=request.top_p,
|
|
53
|
+
stop_sequences=None,
|
|
54
|
+
safety_settings=self._safety_settings,
|
|
55
|
+
)
|
|
56
|
+
thinking_budget = self._get_thinking_budget(model_name)
|
|
57
|
+
if thinking_budget is not None:
|
|
58
|
+
config.thinking_config = types.ThinkingConfig(thinking_budget=thinking_budget)
|
|
59
|
+
return config
|
|
60
|
+
|
|
61
|
+
def generate(self, request: AIRequest) -> AIResponse:
|
|
62
|
+
if self.client is None:
|
|
63
|
+
raise AIError("Gemini model not initialized.", transient=False)
|
|
64
|
+
|
|
65
|
+
start_time = time.time()
|
|
66
|
+
try:
|
|
67
|
+
model_name = normalize_gemini_model_name(request.model)
|
|
68
|
+
generation_config = self._build_generation_config(request, model_name)
|
|
69
|
+
contents = [
|
|
70
|
+
types.Content(
|
|
71
|
+
role="user",
|
|
72
|
+
parts=[types.Part.from_text(text=request.prompt)],
|
|
73
|
+
),
|
|
74
|
+
]
|
|
75
|
+
response = self.client.models.generate_content(
|
|
76
|
+
model=model_name,
|
|
77
|
+
contents=contents,
|
|
78
|
+
config=generation_config,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
self.logger.debug(f"Gemini raw response: {response}")
|
|
82
|
+
|
|
83
|
+
result = ""
|
|
84
|
+
if response.candidates:
|
|
85
|
+
for candidate in response.candidates:
|
|
86
|
+
if candidate.content and candidate.content.parts:
|
|
87
|
+
for part in candidate.content.parts:
|
|
88
|
+
if hasattr(part, "text") and part.text:
|
|
89
|
+
result += part.text
|
|
90
|
+
|
|
91
|
+
raw_text = result.strip()
|
|
92
|
+
usage: Optional[Dict[str, Any]] = None
|
|
93
|
+
if hasattr(response, "usage_metadata") and response.usage_metadata:
|
|
94
|
+
usage = response.usage_metadata.model_dump() if hasattr(response.usage_metadata, "model_dump") else dict(response.usage_metadata)
|
|
95
|
+
|
|
96
|
+
latency_ms = int((time.time() - start_time) * 1000)
|
|
97
|
+
return AIResponse(
|
|
98
|
+
provider=request.provider,
|
|
99
|
+
model=model_name,
|
|
100
|
+
text=raw_text,
|
|
101
|
+
raw_text=raw_text,
|
|
102
|
+
latency_ms=latency_ms,
|
|
103
|
+
usage=usage,
|
|
104
|
+
)
|
|
105
|
+
except Exception as e:
|
|
106
|
+
self.logger.error(f"Gemini processing failed: {e}")
|
|
107
|
+
raise AIError(f"Processing failed: {e}", transient=True)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from groq import Groq
|
|
5
|
+
from typing import Optional, Any, Dict
|
|
6
|
+
|
|
7
|
+
from GameSentenceMiner.ai.contracts import AIRequest, AIResponse, AIError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GroqClient:
|
|
11
|
+
def __init__(self, api_key: str, logger):
|
|
12
|
+
self.api_key = api_key
|
|
13
|
+
self.logger = logger
|
|
14
|
+
self.client = None
|
|
15
|
+
try:
|
|
16
|
+
self.client = Groq(api_key=self.api_key)
|
|
17
|
+
except Exception as e:
|
|
18
|
+
self.logger.error(f"Failed to initialize Groq client: {e}")
|
|
19
|
+
|
|
20
|
+
def generate(self, request: AIRequest) -> AIResponse:
|
|
21
|
+
if self.client is None:
|
|
22
|
+
raise AIError("Groq client not initialized.", transient=False)
|
|
23
|
+
|
|
24
|
+
start_time = time.time()
|
|
25
|
+
try:
|
|
26
|
+
completion = self.client.chat.completions.create(
|
|
27
|
+
model=request.model,
|
|
28
|
+
messages=[{"role": "user", "content": request.prompt}],
|
|
29
|
+
temperature=request.temperature,
|
|
30
|
+
max_completion_tokens=request.max_tokens,
|
|
31
|
+
top_p=request.top_p,
|
|
32
|
+
stream=False,
|
|
33
|
+
stop=None,
|
|
34
|
+
)
|
|
35
|
+
raw_text = completion.choices[0].message.content.strip()
|
|
36
|
+
usage: Optional[Dict[str, Any]] = None
|
|
37
|
+
if hasattr(completion, "usage") and completion.usage:
|
|
38
|
+
usage = completion.usage.model_dump() if hasattr(completion.usage, "model_dump") else dict(completion.usage)
|
|
39
|
+
|
|
40
|
+
latency_ms = int((time.time() - start_time) * 1000)
|
|
41
|
+
return AIResponse(
|
|
42
|
+
provider=request.provider,
|
|
43
|
+
model=request.model,
|
|
44
|
+
text=raw_text,
|
|
45
|
+
raw_text=raw_text,
|
|
46
|
+
latency_ms=latency_ms,
|
|
47
|
+
usage=usage,
|
|
48
|
+
)
|
|
49
|
+
except Exception as e:
|
|
50
|
+
self.logger.error(f"Groq processing failed: {e}")
|
|
51
|
+
raise AIError(f"Processing failed: {e}", transient=True)
|