🎉 Initialize module repository

This commit is contained in:
Marc Wempe
2026-04-03 23:08:58 +02:00
commit 09e436bbe4
12 changed files with 3122 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
__pycache__/
*.py[cod]
.DS_Store
.pytest_cache/
.ruff_cache/
*.log
*.swp
*~

1
__init__.py Normal file
View File

@@ -0,0 +1 @@
from . import models

30
__manifest__.py Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "MVD TCG MTG Deck OpenAI",
"summary": "OpenAI-powered deck and card-role analysis for MTG decks",
"version": "19.0.6.4.1",
"description": """
OpenAI-powered analysis layer for the MTG deckbuilder.
This module adds on-demand AI support to Magic decks:
- commander-focused deck summaries
- game-plan, pilot-tip, and risk-note analysis
- role-tag suggestion and rationale for deck lines
- alternative-card suggestions for problematic deck lines
- AI-assisted deck fill from the in-system card pool
- report integration for the generated deck analysis
The analysis follows the active user language while keeping English oracle
text as the authoritative rules input.
""",
"category": "Tools",
"author": "Mantjeverse Digital",
"license": "LGPL-3",
"depends": ["mvd_tcg_base", "mvd_tcg_deck", "mvd_tcg_mtg_deck"],
"data": [
"views/res_config_settings_views.xml",
"views/mvd_tcg_mtg_deck_views.xml",
"report/mvd_tcg_mtg_deck_report_templates.xml",
],
"application": False,
"installable": True,
}

592
i18n/de.po Normal file
View File

@@ -0,0 +1,592 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mvd_tcg_mtg_deck_openai
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0+e-20260324\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-03 05:05+0000\n"
"PO-Revision-Date: 2026-04-03 05:05+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Language: de\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
msgid "AI Card Roles"
msgstr "KI-Kartenrollen"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
msgid "AI Deck Analysis"
msgstr "KI-Deckanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "OpenAI API Base URL"
msgstr "OpenAI-API-Basis-URL"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__mtg_openai_api_key
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "API Key"
msgstr "API-Schlüssel"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__mtg_openai_alternative_candidate_limit
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "Alternative Candidate Limit"
msgstr "Alternativen-Kandidatenlimit"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
msgid "Alternative Suggestions"
msgstr "Alternativvorschläge"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
msgid "Analyze Card Roles"
msgstr "Kartenrollen analysieren"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
msgid "Analyze Deck"
msgstr "Deck analysieren"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model,name:mvd_tcg_mtg_deck_openai.model_res_config_settings
msgid "Config Settings"
msgstr "Konfiguration"
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "Configure an OpenAI API key in TCG Settings first."
msgstr "Konfiguriere zuerst einen OpenAI-API-Schlüssel in den TCG-Einstellungen."
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.report_mvd_tcg_deck_document_openai_inherit
msgid "Deck Analysis"
msgstr "Deckanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
msgid "Deck Fill"
msgstr "Deck auffüllen"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__mtg_openai_fill_batch_size
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "Deck Fill Batch Size"
msgstr "Batch-Größe für Deck-Auffüllen"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__mtg_openai_fill_candidate_limit
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "Deck Fill Candidate Limit"
msgstr "Kandidatenlimit für Deck-Auffüllen"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid ""
"Default model used for deck summaries, role tagging, deck fill, and "
"alternative suggestions."
msgstr ""
"Standardmodell für Deck-Zusammenfassungen, Rollentags, Deck-Auffüllen und "
"Alternativvorschläge."
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__display_name
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck_line__display_name
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__display_name
msgid "Display Name"
msgstr "Anzeigename"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_alternative_state__failed
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_analysis_state__failed
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_fill_state__failed
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_role_analysis_state__failed
msgid "Failed"
msgstr "Fehlgeschlagen"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
msgid "Fill Deck"
msgstr "Deck auffüllen"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.report_mvd_tcg_deck_document_openai_inherit
msgid "Game Plan"
msgstr "Spielplan"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "How many deck lines are sent in one role-analysis request."
msgstr "Wie viele Deckzeilen in einer Rollen-Analyse-Anfrage gesendet werden."
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__id
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck_line__id
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__id
msgid "ID"
msgstr "ID"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "Maximum number of cards the AI may add in one fill iteration."
msgstr "Maximale Anzahl an Karten, die die KI in einer Auffüllrunde hinzufügen darf."
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid ""
"Maximum number of in-system candidate cards considered for AI-assisted deck "
"fill."
msgstr ""
"Maximale Anzahl an im System verfügbaren Kandidatenkarten für das "
"KI-gestützte Deck-Auffüllen."
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid ""
"Maximum number of in-system replacement candidates passed for one "
"problematic deck line."
msgstr ""
"Maximale Anzahl an im System verfügbaren Ersatzkandidaten für eine "
"problematische Deckzeile."
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "Missing configured deck roles for: %s"
msgstr "Fehlende konfigurierte Deckrollen für: %s"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__mtg_openai_model_name
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "Default Model"
msgstr "Standardmodell"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_alternative_last_analyzed_at
msgid "Last Alternative Analysis"
msgstr "Letzte Alternativanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_alternative_last_error
msgid "Last Alternative Analysis Error"
msgstr "Letzter Fehler der Alternativanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_alternative_last_lang
msgid "Alternative Analysis Language"
msgstr "Sprache der Alternativanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_alternative_last_model
msgid "Alternative Analysis Model"
msgstr "Modell der Alternativanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_alternative_last_prompt
msgid "Last Alternative Analysis Prompt"
msgstr "Letzter Prompt der Alternativanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_alternative_last_response
msgid "Last Alternative Analysis Response"
msgstr "Letzte Antwort der Alternativanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_alternative_state
msgid "Alternative Analysis Status"
msgstr "Status der Alternativanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_alternative_suggestions
msgid "Alternative Suggestions"
msgstr "Alternativvorschläge"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_analysis_state
msgid "Deck Analysis Status"
msgstr "Status der Deckanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_commander_summary
msgid "Commander Summary"
msgstr "Commander-Zusammenfassung"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_fill_last_analyzed_at
msgid "Last Deck Fill"
msgstr "Letzte Deck-Auffüllung"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_fill_last_error
msgid "Last Deck Fill Error"
msgstr "Letzter Fehler beim Deck-Auffüllen"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_fill_last_lang
msgid "Deck Fill Language"
msgstr "Sprache der Deck-Auffüllung"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_fill_last_model
msgid "Deck Fill Model"
msgstr "Modell für Deck-Auffüllung"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_fill_last_prompt
msgid "Last Deck Fill Prompt"
msgstr "Letzter Prompt für Deck-Auffüllung"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_fill_last_response
msgid "Last Deck Fill Response"
msgstr "Letzte Antwort für Deck-Auffüllung"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_fill_state
msgid "Deck Fill Status"
msgstr "Status der Deck-Auffüllung"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_fill_summary
msgid "Deck Fill Summary"
msgstr "Zusammenfassung der Deck-Auffüllung"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_gameplan
msgid "Game Plan"
msgstr "Spielplan"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_last_analyzed_at
msgid "Last Deck Analysis"
msgstr "Letzte Deckanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_last_error
msgid "Last Analysis Error"
msgstr "Letzter Analysefehler"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_last_lang
msgid "Analysis Language"
msgstr "Analysesprache"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_last_model
msgid "Analysis Model"
msgstr "Analysemodell"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_last_prompt
msgid "Last Analysis Prompt"
msgstr "Letzter Analyse-Prompt"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_last_response
msgid "Last Analysis Response"
msgstr "Letzte Analyseantwort"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_pilot_tips
msgid "Pilot Tips"
msgstr "Pilottipps"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_risk_notes
msgid "Risk Notes"
msgstr "Risikohinweise"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_role_analysis_state
msgid "Role Analysis Status"
msgstr "Status der Rollenanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_role_last_analyzed_at
msgid "Last Role Analysis"
msgstr "Letzte Rollenanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_role_last_error
msgid "Last Role Analysis Error"
msgstr "Letzter Fehler der Rollenanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_role_last_lang
msgid "Role Analysis Language"
msgstr "Sprache der Rollenanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_role_last_model
msgid "Role Analysis Model"
msgstr "Modell der Rollenanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_role_last_prompt
msgid "Last Role Analysis Prompt"
msgstr "Letzter Prompt der Rollenanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck__mtg_openai_role_last_response
msgid "Last Role Analysis Response"
msgstr "Letzte Antwort der Rollenanalyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_mvd_tcg_deck_line__mtg_openai_role_rationale
msgid "Role Rationale"
msgstr "Rollenbegründung"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_alternative_state__empty
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_analysis_state__empty
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_fill_state__empty
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_role_analysis_state__empty
msgid "Not Analyzed"
msgstr "Nicht analysiert"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__mtg_openai_api_base_url
msgid "API Base URL"
msgstr "API-Basis-URL"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "OpenAI Analysis"
msgstr "OpenAI-Analyse"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__mtg_openai_request_timeout_seconds
msgid "OpenAI Request Timeout (s)"
msgstr "OpenAI-Request-Timeout (s)"
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI alternative suggestions are only available for Magic decks."
msgstr "OpenAI-Alternativvorschläge sind nur für Magic-Decks verfügbar."
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI attempted to add multiple copies of a singleton card."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI attempted to add too many cards in one batch."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI card analysis is only available for Magic decks."
msgstr "Die OpenAI-Kartenanalyse ist nur für Magic-Decks verfügbar."
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI deck analysis is only available for Magic decks."
msgstr "Die OpenAI-Deckanalyse ist nur für Magic-Decks verfügbar."
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI deck fill is only available for Magic decks."
msgstr "Das OpenAI-Deck-Auffüllen ist nur für Magic-Decks verfügbar."
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI did not return alternative rows for every issue line."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI did not return role assignments for every deck line."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI returned a card outside the allowed pool."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI returned a role analysis for an unknown deck line."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI returned alternatives for an unknown deck line."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI returned an invalid fill quantity."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI returned an unsupported replacement candidate."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI returned an unsupported role key: %(role_key)s"
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI returned duplicate alternative rows for one deck line."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI returned duplicate role analysis rows for one deck line."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "OpenAI returned the same fill card more than once."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid ""
"Override the OpenAI API base URL only for special environments or proxies."
msgstr ""
"Die OpenAI-API-Basis-URL nur für spezielle Umgebungen oder Proxys "
"überschreiben."
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.report_mvd_tcg_deck_document_openai_inherit
msgid "Pilot Tips"
msgstr "Pilothinweise"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_alternative_state__done
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_analysis_state__done
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_fill_state__done
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck_openai.selection__mvd_tcg_deck__mtg_openai_role_analysis_state__done
msgid "Ready"
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "Request Timeout"
msgstr "Request-Timeout"
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.report_mvd_tcg_deck_document_openai_inherit
msgid "Risk Notes"
msgstr "Risikohinweise"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck_openai.field_res_config_settings__mtg_openai_role_batch_size
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "Role Analysis Batch Size"
msgstr "Batch-Größe der Rollen-Analyse"
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "Select a supported MTG format before using deck fill."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "Select at least one commander card first."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid ""
"Stored in system parameters and used for OpenAI-backed MTG deck analysis."
msgstr ""
"Wird in den Systemparametern gespeichert und für die OpenAI-gestützte "
"MTG-Deckanalyse verwendet."
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
msgid "Suggest Alternatives"
msgstr "Alternativen vorschlagen"
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model,name:mvd_tcg_mtg_deck_openai.model_mvd_tcg_deck
msgid "TCG Deck"
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#: model:ir.model,name:mvd_tcg_mtg_deck_openai.model_mvd_tcg_deck_line
msgid "TCG Deck Line"
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.mvd_tcg_deck_view_form_openai_inherit
msgid "Technical"
msgstr "Technisch"
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "The mainboard is already full for the selected format."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "This deck does not have a mainboard yet."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "This deck has no cards to analyze."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck_openai/models/mvd_tcg_deck.py:0
msgid "This deck has no replaceable issue lines right now."
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "Timeout in seconds for OpenAI responses requests."
msgstr "Timeout in Sekunden für OpenAI-Responses-Anfragen."
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "gpt-5-mini"
msgstr ""
#. module: mvd_tcg_mtg_deck_openai
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck_openai.res_config_settings_view_form_mvd_tcg_mtg_deck_openai
msgid "sk-..."
msgstr ""

3
models/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
from . import mvd_tcg_deck
from . import mvd_tcg_deck_line
from . import res_config_settings

9
models/constants.py Normal file
View File

@@ -0,0 +1,9 @@
"""Shared constants for the MTG OpenAI analysis layer."""
DEFAULT_MTG_OPENAI_MODEL_NAME = "gpt-5-mini"
DEFAULT_MTG_OPENAI_API_BASE_URL = "https://api.openai.com/v1"
DEFAULT_MTG_OPENAI_REQUEST_TIMEOUT_SECONDS = 120
DEFAULT_MTG_OPENAI_ROLE_BATCH_SIZE = 24
DEFAULT_MTG_OPENAI_FILL_CANDIDATE_LIMIT = 240
DEFAULT_MTG_OPENAI_FILL_BATCH_SIZE = 24
DEFAULT_MTG_OPENAI_ALTERNATIVE_CANDIDATE_LIMIT = 36

2103
models/mvd_tcg_deck.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
"""OpenAI fields for MTG deck lines."""
from odoo import fields, models
class MvdTcgDeckLine(models.Model):
"""Extend deck lines with persisted OpenAI role-analysis output."""
_inherit = "mvd.tcg.deck.line"
mtg_openai_role_rationale = fields.Text(
readonly=True,
copy=False,
)

View File

@@ -0,0 +1,71 @@
"""Settings for the OpenAI-backed MTG deck analysis layer."""
from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError
from .constants import (
DEFAULT_MTG_OPENAI_API_BASE_URL,
DEFAULT_MTG_OPENAI_ALTERNATIVE_CANDIDATE_LIMIT,
DEFAULT_MTG_OPENAI_FILL_BATCH_SIZE,
DEFAULT_MTG_OPENAI_FILL_CANDIDATE_LIMIT,
DEFAULT_MTG_OPENAI_MODEL_NAME,
DEFAULT_MTG_OPENAI_REQUEST_TIMEOUT_SECONDS,
DEFAULT_MTG_OPENAI_ROLE_BATCH_SIZE,
)
class ResConfigSettings(models.TransientModel):
"""Expose OpenAI deck-analysis defaults through Odoo settings."""
_inherit = "res.config.settings"
mtg_openai_api_key = fields.Char(
string="API Key",
config_parameter="mvd_tcg_mtg_deck_openai.api_key",
)
mtg_openai_model_name = fields.Char(
string="Default Model",
config_parameter="mvd_tcg_mtg_deck_openai.model_name",
default=DEFAULT_MTG_OPENAI_MODEL_NAME,
)
mtg_openai_api_base_url = fields.Char(
string="OpenAI API Base URL",
config_parameter="mvd_tcg_mtg_deck_openai.api_base_url",
default=DEFAULT_MTG_OPENAI_API_BASE_URL,
)
mtg_openai_request_timeout_seconds = fields.Integer(
string="OpenAI Request Timeout (s)",
config_parameter="mvd_tcg_mtg_deck_openai.request_timeout_seconds",
default=DEFAULT_MTG_OPENAI_REQUEST_TIMEOUT_SECONDS,
)
mtg_openai_role_batch_size = fields.Integer(
string="Role Analysis Batch Size",
config_parameter="mvd_tcg_mtg_deck_openai.role_batch_size",
default=DEFAULT_MTG_OPENAI_ROLE_BATCH_SIZE,
)
mtg_openai_fill_candidate_limit = fields.Integer(
string="Deck Fill Candidate Limit",
config_parameter="mvd_tcg_mtg_deck_openai.fill_candidate_limit",
default=DEFAULT_MTG_OPENAI_FILL_CANDIDATE_LIMIT,
)
mtg_openai_fill_batch_size = fields.Integer(
string="Deck Fill Batch Size",
config_parameter="mvd_tcg_mtg_deck_openai.fill_batch_size",
default=DEFAULT_MTG_OPENAI_FILL_BATCH_SIZE,
)
mtg_openai_alternative_candidate_limit = fields.Integer(
string="Alternative Candidate Limit",
config_parameter="mvd_tcg_mtg_deck_openai.alternative_candidate_limit",
default=DEFAULT_MTG_OPENAI_ALTERNATIVE_CANDIDATE_LIMIT,
)
@api.constrains("mtg_openai_api_base_url")
def _check_mtg_openai_api_base_url(self):
"""Restrict the OpenAI connector to the official HTTPS API host."""
for settings in self:
try:
self.env["mvd.tcg.deck"]._mtg_openai_validate_base_url(
settings.mtg_openai_api_base_url
)
except UserError as exc:
raise ValidationError(str(exc)) from exc

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template
id="report_mvd_tcg_deck_document_openai_inherit"
inherit_id="mvd_tcg_deck.report_mvd_tcg_deck_document"
>
<xpath expr="//div[hasclass('o_mvd_tcg_report_board_header')]" position="after">
<div
t-if="section['board'].code == 'command_zone' and doc.mtg_openai_commander_summary"
class="o_mvd_tcg_report_section o_mvd_tcg_report_section_emphasis"
style="margin-bottom: 10px;"
>
<h3 class="o_mvd_tcg_report_section_title">Deck Analysis</h3>
<div t-field="doc.mtg_openai_commander_summary"/>
<table
t-if="doc.mtg_openai_gameplan or doc.mtg_openai_pilot_tips or doc.mtg_openai_risk_notes"
class="o_mvd_tcg_report_snapshot"
style="margin-top: 8px;"
>
<tbody>
<tr>
<td t-if="doc.mtg_openai_gameplan">
<div class="o_mvd_tcg_report_snapshot_block">
<h4>Game Plan</h4>
<div t-field="doc.mtg_openai_gameplan"/>
</div>
</td>
<td t-if="doc.mtg_openai_pilot_tips">
<div class="o_mvd_tcg_report_snapshot_block">
<h4>Pilot Tips</h4>
<div t-field="doc.mtg_openai_pilot_tips"/>
</div>
</td>
</tr>
</tbody>
</table>
<div
t-if="doc.mtg_openai_risk_notes"
class="o_mvd_tcg_report_snapshot_block"
style="margin-top: 8px;"
>
<h4>Risk Notes</h4>
<div t-field="doc.mtg_openai_risk_notes"/>
</div>
</div>
</xpath>
</template>
</odoo>

View File

@@ -0,0 +1,169 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="mvd_tcg_deck_view_form_openai_inherit" model="ir.ui.view">
<field name="name">mvd.tcg.deck.view.form.openai.inherit</field>
<field name="model">mvd.tcg.deck</field>
<field name="inherit_id" ref="mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit"/>
<field name="arch" type="xml">
<xpath expr="//page[@name='mtg_analysis']/group[1]" position="before">
<group string="AI Deck Analysis" invisible="not is_mtg_deck">
<group groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system">
<field name="mtg_openai_analysis_state" readonly="1"/>
<field name="mtg_openai_last_analyzed_at" readonly="1"/>
<field
name="mtg_openai_last_error"
readonly="1"
invisible="not mtg_openai_last_error"
groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system"
/>
</group>
<group>
<button
name="action_mtg_openai_analyze_deck"
type="object"
string="Analyze Deck"
class="btn-primary"
groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system"
/>
<button
name="action_mtg_openai_suggest_alternatives"
type="object"
string="Suggest Alternatives"
class="btn-secondary"
groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system"
/>
<button
name="action_mtg_openai_fill_deck"
type="object"
string="Fill Deck"
class="btn-secondary"
groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system"
/>
</group>
<group col="1">
<field
name="mtg_openai_commander_summary"
widget="html"
readonly="1"
nolabel="1"
invisible="not mtg_openai_commander_summary"
/>
</group>
<group string="Game Plan" col="1" invisible="not mtg_openai_gameplan">
<field name="mtg_openai_gameplan" widget="html" readonly="1" nolabel="1"/>
</group>
<group string="Pilot Tips" col="1" invisible="not mtg_openai_pilot_tips">
<field name="mtg_openai_pilot_tips" widget="html" readonly="1" nolabel="1"/>
</group>
<group string="Risk Notes" col="1" invisible="not mtg_openai_risk_notes">
<field name="mtg_openai_risk_notes" widget="html" readonly="1" nolabel="1"/>
</group>
<group string="Alternative Suggestions" invisible="not is_mtg_deck">
<group groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system">
<field name="mtg_openai_alternative_state" readonly="1"/>
<field name="mtg_openai_alternative_last_analyzed_at" readonly="1"/>
<field
name="mtg_openai_alternative_last_error"
readonly="1"
invisible="not mtg_openai_alternative_last_error"
groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system"
/>
</group>
<group col="1" invisible="not mtg_openai_alternative_suggestions">
<field
name="mtg_openai_alternative_suggestions"
widget="html"
readonly="1"
nolabel="1"
/>
</group>
</group>
<group string="Deck Fill" invisible="not is_mtg_deck">
<group groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system">
<field name="mtg_openai_fill_state" readonly="1"/>
<field name="mtg_openai_fill_last_analyzed_at" readonly="1"/>
<field
name="mtg_openai_fill_last_error"
readonly="1"
invisible="not mtg_openai_fill_last_error"
groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system"
/>
</group>
<group col="1" invisible="not mtg_openai_fill_summary">
<field
name="mtg_openai_fill_summary"
widget="html"
readonly="1"
nolabel="1"
/>
</group>
</group>
<group
string="Technical"
col="1"
groups="mvd_tcg_base.mvd_tcg_base_group_administrator,base.group_system"
>
<field name="mtg_openai_last_model" readonly="1"/>
<field name="mtg_openai_last_lang" readonly="1"/>
<field name="mtg_openai_alternative_last_model" readonly="1"/>
<field name="mtg_openai_alternative_last_lang" readonly="1"/>
<field name="mtg_openai_fill_last_model" readonly="1"/>
<field name="mtg_openai_fill_last_lang" readonly="1"/>
<field name="mtg_openai_last_prompt" readonly="1" widget="text" nolabel="1"/>
<field name="mtg_openai_last_response" readonly="1" widget="text" nolabel="1"/>
<field name="mtg_openai_alternative_last_prompt" readonly="1" widget="text" nolabel="1"/>
<field name="mtg_openai_alternative_last_response" readonly="1" widget="text" nolabel="1"/>
<field name="mtg_openai_fill_last_prompt" readonly="1" widget="text" nolabel="1"/>
<field name="mtg_openai_fill_last_response" readonly="1" widget="text" nolabel="1"/>
</group>
</group>
<group
string="AI Card Roles"
invisible="not is_mtg_deck"
groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system"
>
<group>
<field name="mtg_openai_role_analysis_state" readonly="1"/>
<field name="mtg_openai_role_last_analyzed_at" readonly="1"/>
<field
name="mtg_openai_role_last_error"
readonly="1"
invisible="not mtg_openai_role_last_error"
groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system"
/>
</group>
<group>
<button
name="action_mtg_openai_analyze_card_roles"
type="object"
string="Analyze Card Roles"
class="btn-secondary"
groups="mvd_tcg_base.mvd_tcg_base_group_manager,base.group_system"
/>
</group>
<group col="1">
<field name="line_ids" readonly="1" nolabel="1">
<list create="0" delete="0" default_order="board_sequence, sequence, id">
<field name="board_id" optional="show"/>
<field name="card_id"/>
<field name="role_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="show"/>
<field name="mtg_openai_role_rationale" optional="show"/>
</list>
</field>
</group>
<group
string="Technical"
col="1"
groups="mvd_tcg_base.mvd_tcg_base_group_administrator,base.group_system"
>
<field name="mtg_openai_role_last_model" readonly="1"/>
<field name="mtg_openai_role_last_lang" readonly="1"/>
<field name="mtg_openai_role_last_prompt" readonly="1" widget="text" nolabel="1"/>
<field name="mtg_openai_role_last_response" readonly="1" widget="text" nolabel="1"/>
</group>
</group>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form_mvd_tcg_mtg_deck_openai" model="ir.ui.view">
<field name="name">res.config.settings.view.form.mvd.tcg.mtg.deck.openai</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="mvd_tcg_base.mvd_tcg_res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//app[@name='mvd_tcg']" position="inside">
<block
title="OpenAI Analysis"
name="mvd_tcg_openai_analysis"
groups="mvd_tcg_base.mvd_tcg_base_group_administrator,base.group_system"
>
<setting
id="mvd_tcg_openai_model_name"
string="Model Name"
help="Default model used for deck summaries, role tagging, deck fill, and alternative suggestions."
>
<field name="mtg_openai_model_name" placeholder="gpt-5-mini"/>
</setting>
<setting
id="mvd_tcg_openai_api_base_url"
string="API Base URL"
help="Override the OpenAI API base URL only for special environments or proxies."
>
<field name="mtg_openai_api_base_url"/>
</setting>
<setting
id="mvd_tcg_openai_request_timeout"
string="Request Timeout"
help="Timeout in seconds for OpenAI responses requests."
>
<field name="mtg_openai_request_timeout_seconds"/>
</setting>
<setting
id="mvd_tcg_openai_role_batch_size"
string="Role Analysis Batch Size"
help="How many deck lines are sent in one role-analysis request."
>
<field name="mtg_openai_role_batch_size"/>
</setting>
<setting
id="mvd_tcg_openai_fill_candidate_limit"
string="Deck Fill Candidate Limit"
help="Maximum number of in-system candidate cards considered for AI-assisted deck fill."
>
<field name="mtg_openai_fill_candidate_limit"/>
</setting>
<setting
id="mvd_tcg_openai_fill_batch_size"
string="Deck Fill Batch Size"
help="Maximum number of cards the AI may add in one fill iteration."
>
<field name="mtg_openai_fill_batch_size"/>
</setting>
<setting
id="mvd_tcg_openai_alternative_candidate_limit"
string="Alternative Candidate Limit"
help="Maximum number of in-system replacement candidates passed for one problematic deck line."
>
<field name="mtg_openai_alternative_candidate_limit"/>
</setting>
<setting
id="mvd_tcg_openai_api_key"
string="API Key"
help="Stored in system parameters and used for OpenAI-backed MTG deck analysis."
>
<field name="mtg_openai_api_key" password="True" placeholder="sk-..."/>
</setting>
</block>
</xpath>
</field>
</record>
</odoo>