diff --git a/src/api/strategy.js b/src/api/strategy.js index 3b29a13e..ee4e7bad 100644 --- a/src/api/strategy.js +++ b/src/api/strategy.js @@ -16,6 +16,7 @@ const api = { verifyCode: '/api/strategies/verify', aiGenerate: '/api/strategies/generate', aiWorkspace: '/api/strategies/ai-workspace', + aiWorkspaceAccountModels: '/api/strategies/ai-workspace/account-models', scriptTemplates: '/api/strategies/script-templates', reviewReport: '/api/strategies/review-report', reviewReportHistory: '/api/strategies/review-report/history', @@ -253,6 +254,13 @@ export function runStrategyAiTurn (data) { }) } +export function getStrategyAiAccountModels () { + return request({ + url: api.aiWorkspaceAccountModels, + method: 'get' + }) +} + export function setStrategyAiCandidateStatus (changeId, status) { return request({ url: `${api.aiWorkspace}/changes/${changeId}/status`, diff --git a/src/views/strategy-ide/index.vue b/src/views/strategy-ide/index.vue index cf4a93d9..0d9a59fa 100644 --- a/src/views/strategy-ide/index.vue +++ b/src/views/strategy-ide/index.vue @@ -303,6 +303,24 @@
+
+ + ChatGPT account + API key + + + + {{ item.name || item.id }} + + + {{ aiAuthMode === 'chatgpt_account' ? 'ChatGPT plan ยท no API-key fallback' : 'Explicit API billing mode' }} +
item.id === this.aiAccountModel)) { + this.aiAccountModel = this.aiAccountModels[0].id + } + } catch (e) { + this.aiAccountModels = [] + } finally { + this.aiAccountModelsLoading = false + } + }, async loadStrategyAiWorkspace (sourceId) { const id = Number(sourceId || 0) if (!id || this.scriptCodeHidden) { @@ -1433,6 +1472,8 @@ export default { existingCode, interactionMode, generationMode: 'authoring', + authMode: this.aiAuthMode, + model: this.aiAuthMode === 'chatgpt_account' ? this.aiAccountModel : undefined, context: { source: 'strategy_ide' } }) const data = (res && res.data) || {} @@ -3054,6 +3095,18 @@ export default { background: #fff; } +.strategy-ai-auth-row { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; +} + +.strategy-ai-auth-hint { + color: #7f8a9b; + font-size: 12px; +} + .strategy-ai-workspace--collapsed { min-height: 42px; height: 42px; diff --git a/tests/unit/strategyAiWorkspace.test.mjs b/tests/unit/strategyAiWorkspace.test.mjs index 2446ec8d..34cdccb3 100644 --- a/tests/unit/strategyAiWorkspace.test.mjs +++ b/tests/unit/strategyAiWorkspace.test.mjs @@ -39,6 +39,17 @@ test('strategy AI workspace keeps memory source-bound and uses explicit candidat assert.match(page, /currentAssetType \(\) \{[\s\S]*this\.aiStrategyPrompt = ''[\s\S]*this\.aiInteractionMode = 'auto'/) }) +test('strategy AI workspace exposes explicit account and API-key modes without fallback', () => { + const api = read('src/api/strategy.js') + const page = read('src/views/strategy-ide/index.vue') + assert.match(page, /value="chatgpt_account">ChatGPT account/) + assert.match(page, /value="api_key">API key/) + assert.match(page, /no API-key fallback/) + assert.match(page, /authMode: this\.aiAuthMode/) + assert.match(page, /model: this\.aiAuthMode === 'chatgpt_account'/) + assert.match(api, /aiWorkspaceAccountModels: '\/api\/strategies\/ai-workspace\/account-models'/) +}) + test('hidden purchased strategies show a locked AI workspace and never load or send source context', () => { const page = read('src/views/strategy-ide/index.vue') assert.match(page, /v-if="scriptCodeHidden" class="strategy-ai-locked"/)