Merge pull request #3310 from heathermhuang/codex/grok-subscription-support

feat: add grok subscription support
This commit is contained in:
Wesley Liddick
2026-06-26 15:41:52 +08:00
committed by GitHub
109 changed files with 5594 additions and 224 deletions
+142
View File
@@ -0,0 +1,142 @@
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin'
import type { GrokTokenInfo } from '@/api/admin/grok'
export function useGrokOAuth() {
const appStore = useAppStore()
const { t } = useI18n()
const authUrl = ref('')
const sessionId = ref('')
const state = ref('')
const loading = ref(false)
const error = ref('')
const resetState = () => {
authUrl.value = ''
sessionId.value = ''
state.value = ''
loading.value = false
error.value = ''
}
const generateAuthUrl = async (proxyId: number | null | undefined): Promise<boolean> => {
loading.value = true
authUrl.value = ''
sessionId.value = ''
state.value = ''
error.value = ''
try {
const payload: Record<string, unknown> = {}
if (proxyId) payload.proxy_id = proxyId
const response = await adminAPI.grok.generateAuthUrl(payload)
authUrl.value = response.auth_url
sessionId.value = response.session_id
state.value = response.state
return true
} catch (err: any) {
error.value = err.response?.data?.detail || t('admin.accounts.oauth.grok.failedToGenerateUrl')
appStore.showError(error.value)
return false
} finally {
loading.value = false
}
}
const exchangeAuthCode = async (params: {
code: string
sessionId: string
state: string
proxyId?: number | null
}): Promise<GrokTokenInfo | null> => {
const code = params.code?.trim()
if (!code || !params.sessionId || !params.state) {
error.value = t('admin.accounts.oauth.grok.missingExchangeParams')
return null
}
loading.value = true
error.value = ''
try {
const payload: Record<string, unknown> = {
session_id: params.sessionId,
state: params.state,
code
}
if (params.proxyId) payload.proxy_id = params.proxyId
return await adminAPI.grok.exchangeCode(payload as any)
} catch (err: any) {
error.value = err.response?.data?.detail || t('admin.accounts.oauth.grok.failedToExchangeCode')
appStore.showError(error.value)
return null
} finally {
loading.value = false
}
}
const validateRefreshToken = async (
refreshToken: string,
proxyId?: number | null
): Promise<GrokTokenInfo | null> => {
if (!refreshToken.trim()) {
error.value = t('admin.accounts.oauth.grok.pleaseEnterRefreshToken')
return null
}
loading.value = true
error.value = ''
try {
return await adminAPI.grok.refreshGrokToken(refreshToken.trim(), proxyId)
} catch (err: any) {
error.value = err.response?.data?.detail || t('admin.accounts.oauth.grok.failedToValidateRT')
return null
} finally {
loading.value = false
}
}
const buildCredentials = (tokenInfo: GrokTokenInfo): Record<string, unknown> => {
const credentials: Record<string, unknown> = {
access_token: tokenInfo.access_token,
token_type: tokenInfo.token_type,
expires_at: tokenInfo.expires_at,
client_id: tokenInfo.client_id,
scope: tokenInfo.scope,
email: tokenInfo.email,
subscription_tier: tokenInfo.subscription_tier,
entitlement_status: tokenInfo.entitlement_status
}
if (tokenInfo.refresh_token) credentials.refresh_token = tokenInfo.refresh_token
if (tokenInfo.id_token) credentials.id_token = tokenInfo.id_token
return Object.fromEntries(Object.entries(credentials).filter(([, value]) => value !== undefined && value !== ''))
}
const buildExtraInfo = (tokenInfo: GrokTokenInfo): Record<string, unknown> => {
const extra: Record<string, unknown> = {}
if (tokenInfo.email) extra.email = tokenInfo.email
if (tokenInfo.subscription_tier) extra.subscription_tier = tokenInfo.subscription_tier
if (tokenInfo.entitlement_status) extra.entitlement_status = tokenInfo.entitlement_status
return extra
}
return {
authUrl,
sessionId,
state,
loading,
error,
resetState,
generateAuthUrl,
exchangeAuthCode,
validateRefreshToken,
buildCredentials,
buildExtraInfo
}
}
+21 -5
View File
@@ -131,10 +131,16 @@ const metaModels = [
// xAI Grok
const xaiModels = [
'grok-4', 'grok-4-0709',
'grok-3-beta', 'grok-3-mini-beta', 'grok-3-fast-beta',
'grok-2', 'grok-2-vision', 'grok-2-image',
'grok-beta', 'grok-vision-beta'
'grok-4.3',
'grok-build-0.1',
'grok-4.20-0309-reasoning',
'grok-4.20-0309-non-reasoning',
'grok-4.20-multi-agent-0309',
'grok',
'grok-latest',
'grok-build',
'grok-4.20-reasoning',
'grok-4.20-non-reasoning'
]
// Cohere
@@ -273,6 +279,14 @@ const geminiPresetMappings = [
{ label: '3.1 Image', from: 'gemini-3.1-flash-image', to: 'gemini-3.1-flash-image', color: 'bg-sky-100 text-sky-700 hover:bg-sky-200 dark:bg-sky-900/30 dark:text-sky-400' }
]
const grokPresetMappings = [
{ label: 'Grok 4.3', from: 'grok-4.3', to: 'grok-4.3', color: 'bg-slate-100 text-slate-700 hover:bg-slate-200 dark:bg-slate-800/50 dark:text-slate-300' },
{ 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' }
]
// Antigravity 预设映射(支持通配符)
const antigravityPresetMappings = [
// Claude 通配符映射
@@ -371,7 +385,8 @@ export function getModelsByPlatform(platform: string): string[] {
case 'deepseek': return deepseekModels
case 'mistral': return mistralModels
case 'meta': return metaModels
case 'xai': return xaiModels
case 'xai':
case 'grok': return xaiModels
case 'cohere': return cohereModels
case 'yi': return yiModels
case 'moonshot': return moonshotModels
@@ -389,6 +404,7 @@ export function getModelsByPlatform(platform: string): string[] {
export function getPresetMappingsByPlatform(platform: string) {
if (platform === 'openai') return openaiPresetMappings
if (platform === 'gemini') return geminiPresetMappings
if (platform === 'grok' || platform === 'xai') return grokPresetMappings
if (platform === 'antigravity') return antigravityPresetMappings
if (platform === 'bedrock') return bedrockPresetMappings
return anthropicPresetMappings
@@ -3,6 +3,10 @@ import { getConfiguredTableDefaultPageSize, normalizeTablePageSize } from '@/uti
const STORAGE_KEY = 'table-page-size'
export function getPersistedPageSize(fallback = getConfiguredTableDefaultPageSize()): number {
if (typeof window !== 'undefined' && window.__APP_CONFIG__?.table_default_page_size !== undefined) {
return normalizeTablePageSize(getConfiguredTableDefaultPageSize())
}
if (typeof window !== 'undefined') {
try {
const stored = window.localStorage.getItem(STORAGE_KEY)