mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
fix(frontend): edit grok oauth model mapping
This commit is contained in:
@@ -630,8 +630,10 @@ Sub2API supports Grok subscription accounts through xAI OAuth and forwards OpenA
|
||||
- Public Claude-compatible target: `/v1/messages`, converted to xAI Responses and returned as Anthropic Messages output for Claude CLI style clients
|
||||
- Public Chat Completions targets: `/v1/chat/completions` and `/chat/completions`, forwarded to `${XAI_BASE_URL:-https://api.x.ai/v1}/chat/completions`
|
||||
- Codex CLI style Responses WebSocket ingress is accepted on the Responses targets and bridged to xAI HTTP/SSE Responses upstream
|
||||
- Initial models: `grok-4.3`, `grok-build-0.1`, `grok-4.20-0309-reasoning`, `grok-4.20-0309-non-reasoning`, and `grok-4.20-multi-agent-0309`
|
||||
- Out of scope for this provider: image, video, TTS, transcription, browser automation, cookies, and Grok web scraping
|
||||
- Initial text models: `grok-4.3`, `grok-build-0.1`, `grok-4.20-0309-reasoning`, `grok-4.20-0309-non-reasoning`, and `grok-4.20-multi-agent-0309`
|
||||
- Media targets for Grok groups: `/v1/images/generations`, `/images/generations`, `/v1/images/edits`, `/images/edits`, `/v1/videos/generations`, `/videos/generations`, `/v1/videos/{request_id}`, and `/videos/{request_id}`. Generation requests require the group image-generation permission.
|
||||
- Media models: `grok-imagine`, `grok-imagine-image-quality`, `grok-imagine-image`, `grok-imagine-edit`, `grok-imagine-video`, and `grok-imagine-video-1.5`
|
||||
- Out of scope for this provider: TTS, transcription, browser automation, cookies, and Grok web scraping
|
||||
|
||||
### OAuth Configuration
|
||||
|
||||
|
||||
@@ -419,9 +419,9 @@
|
||||
|
||||
</div>
|
||||
|
||||
<!-- OpenAI OAuth Model Mapping (OAuth 类型没有 apikey 容器,需要独立的模型映射区域) -->
|
||||
<!-- OpenAI/Grok OAuth Model Mapping (OAuth 类型没有 apikey 容器,需要独立的模型映射区域) -->
|
||||
<div
|
||||
v-if="account.platform === 'openai' && account.type === 'oauth'"
|
||||
v-if="(account.platform === 'openai' || account.platform === 'grok') && account.type === 'oauth'"
|
||||
class="border-t border-gray-200 pt-4 dark:border-dark-600"
|
||||
>
|
||||
<label class="input-label">{{ t('admin.accounts.modelRestriction') }}</label>
|
||||
@@ -3244,8 +3244,8 @@ const syncFormFromAccount = (newAccount: Account | null) => {
|
||||
: 'https://api.anthropic.com'
|
||||
editBaseUrl.value = platformDefaultUrl
|
||||
|
||||
// Load model mappings for OpenAI OAuth accounts
|
||||
if (newAccount.platform === 'openai' && newAccount.credentials) {
|
||||
// Load model mappings for OpenAI/Grok OAuth accounts
|
||||
if ((newAccount.platform === 'openai' || newAccount.platform === 'grok') && newAccount.credentials) {
|
||||
const oauthCredentials = newAccount.credentials as Record<string, unknown>
|
||||
loadModelRestrictionFromMapping(oauthCredentials.model_mapping as Record<string, unknown> | undefined)
|
||||
} else {
|
||||
@@ -3972,14 +3972,23 @@ const handleSubmit = async () => {
|
||||
updatePayload.credentials = newCredentials
|
||||
}
|
||||
|
||||
// OpenAI OAuth: persist model mapping to credentials
|
||||
if (props.account.platform === 'openai' && props.account.type === 'oauth') {
|
||||
// OpenAI/Grok OAuth: persist model mapping to credentials
|
||||
if ((props.account.platform === 'openai' || props.account.platform === 'grok') && props.account.type === 'oauth') {
|
||||
const currentCredentials = isSparkShadow.value
|
||||
? {}
|
||||
: (updatePayload.credentials as Record<string, unknown>) ||
|
||||
((props.account.credentials as Record<string, unknown>) || {})
|
||||
const newCredentials: Record<string, unknown> = { ...currentCredentials }
|
||||
applyOpenAIModelMappingCredentials(newCredentials)
|
||||
if (props.account.platform === 'openai') {
|
||||
applyOpenAIModelMappingCredentials(newCredentials)
|
||||
} else {
|
||||
const modelMapping = buildModelRestrictionMapping()
|
||||
if (modelMapping) {
|
||||
newCredentials.model_mapping = modelMapping
|
||||
} else {
|
||||
delete newCredentials.model_mapping
|
||||
}
|
||||
}
|
||||
|
||||
updatePayload.credentials = newCredentials
|
||||
}
|
||||
|
||||
@@ -241,6 +241,32 @@ function buildAntigravityAccount(projectId = 'configured-project') {
|
||||
} as any
|
||||
}
|
||||
|
||||
function buildGrokOAuthAccount() {
|
||||
return {
|
||||
id: 5,
|
||||
name: 'Grok OAuth',
|
||||
notes: '',
|
||||
platform: 'grok',
|
||||
type: 'oauth',
|
||||
credentials: {
|
||||
refresh_token: 'grok-rt',
|
||||
base_url: 'https://api.x.ai/v1',
|
||||
model_mapping: {
|
||||
'grok-latest': 'grok-4.3'
|
||||
}
|
||||
},
|
||||
extra: {},
|
||||
proxy_id: null,
|
||||
concurrency: 1,
|
||||
priority: 1,
|
||||
rate_multiplier: 1,
|
||||
status: 'active',
|
||||
group_ids: [],
|
||||
expires_at: null,
|
||||
auto_pause_on_expired: false
|
||||
} as any
|
||||
}
|
||||
|
||||
function buildOpenAISetupTokenAccount() {
|
||||
return {
|
||||
...buildAccount(),
|
||||
@@ -357,6 +383,35 @@ describe('EditAccountModal', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('loads and submits Grok OAuth model mapping edits', async () => {
|
||||
const account = buildGrokOAuthAccount()
|
||||
updateAccountMock.mockReset()
|
||||
checkMixedChannelRiskMock.mockReset()
|
||||
checkMixedChannelRiskMock.mockResolvedValue({ has_risk: false })
|
||||
updateAccountMock.mockResolvedValue(account)
|
||||
|
||||
const wrapper = mountModal(account)
|
||||
expect(wrapper.text()).toContain('Imagine Image')
|
||||
expect(wrapper.text()).toContain('Imagine Video')
|
||||
|
||||
const inputWithValue = (value: string) => {
|
||||
const input = wrapper
|
||||
.findAll('input')
|
||||
.find((input) => (input.element as HTMLInputElement).value === value)
|
||||
expect(input).toBeTruthy()
|
||||
return input!
|
||||
}
|
||||
|
||||
await inputWithValue('grok-latest').setValue('grok')
|
||||
await inputWithValue('grok-4.3').setValue('grok-build-0.1')
|
||||
await wrapper.get('form#edit-account-form').trigger('submit.prevent')
|
||||
|
||||
expect(updateAccountMock).toHaveBeenCalledTimes(1)
|
||||
expect(updateAccountMock.mock.calls[0]?.[1]?.credentials?.model_mapping).toEqual({
|
||||
grok: 'grok-build-0.1'
|
||||
})
|
||||
})
|
||||
|
||||
it('only submits model mapping credentials when saving an OpenAI spark shadow account', async () => {
|
||||
authIsSimpleMode.value = false
|
||||
const account = buildOpenAISparkShadowAccount()
|
||||
|
||||
@@ -141,7 +141,13 @@ const xaiModels = [
|
||||
'grok-latest',
|
||||
'grok-build',
|
||||
'grok-4.20-reasoning',
|
||||
'grok-4.20-non-reasoning'
|
||||
'grok-4.20-non-reasoning',
|
||||
'grok-imagine',
|
||||
'grok-imagine-image-quality',
|
||||
'grok-imagine-image',
|
||||
'grok-imagine-edit',
|
||||
'grok-imagine-video',
|
||||
'grok-imagine-video-1.5'
|
||||
]
|
||||
|
||||
// Cohere
|
||||
@@ -286,7 +292,10 @@ const grokPresetMappings = [
|
||||
{ label: 'Grok Latest', from: 'grok-latest', to: 'grok-4.3', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' },
|
||||
{ label: 'Build 0.1', from: 'grok-build', to: 'grok-build-0.1', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' },
|
||||
{ label: '4.20 Reasoning', from: 'grok-4.20-reasoning', to: 'grok-4.20-0309-reasoning', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' },
|
||||
{ label: '4.20 Non Reasoning', from: 'grok-4.20-non-reasoning', to: 'grok-4.20-0309-non-reasoning', color: 'bg-violet-100 text-violet-700 hover:bg-violet-200 dark:bg-violet-900/30 dark:text-violet-400' }
|
||||
{ label: '4.20 Non Reasoning', from: 'grok-4.20-non-reasoning', to: 'grok-4.20-0309-non-reasoning', color: 'bg-violet-100 text-violet-700 hover:bg-violet-200 dark:bg-violet-900/30 dark:text-violet-400' },
|
||||
{ label: 'Imagine Image', from: 'grok-imagine', to: 'grok-imagine-image-quality', color: 'bg-sky-100 text-sky-700 hover:bg-sky-200 dark:bg-sky-900/30 dark:text-sky-400' },
|
||||
{ label: 'Imagine Edit', from: 'grok-imagine-edit', to: 'grok-imagine-edit', color: 'bg-rose-100 text-rose-700 hover:bg-rose-200 dark:bg-rose-900/30 dark:text-rose-400' },
|
||||
{ label: 'Imagine Video', from: 'grok-imagine-video-1.5', to: 'grok-imagine-video-1.5', color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' }
|
||||
]
|
||||
|
||||
// Antigravity 预设映射(支持通配符)
|
||||
|
||||
Reference in New Issue
Block a user