Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
3fcefe6c
Commit
3fcefe6c
authored
Mar 11, 2026
by
Rose Ding
Browse files
feat: prioritize new gemini image models in frontend
parent
bf6585a4
Changes
7
Show whitespace changes
Inline
Side-by-side
frontend/src/components/account/AccountTestModal.vue
View file @
3fcefe6c
...
...
@@ -260,6 +260,7 @@ const loadingModels = ref(false)
let
eventSource
:
EventSource
|
null
=
null
const
isSoraAccount
=
computed
(()
=>
props
.
account
?.
platform
===
'
sora
'
)
const
generatedImages
=
ref
<
PreviewImage
[]
>
([])
const
prioritizedGeminiModels
=
[
'
gemini-3.1-flash-image
'
,
'
gemini-2.5-flash-image
'
,
'
gemini-2.5-flash
'
,
'
gemini-2.5-pro
'
,
'
gemini-3-flash-preview
'
,
'
gemini-3-pro-preview
'
,
'
gemini-2.0-flash
'
]
const
supportsGeminiImageTest
=
computed
(()
=>
{
if
(
isSoraAccount
.
value
)
return
false
const
modelID
=
selectedModelId
.
value
.
toLowerCase
()
...
...
@@ -268,6 +269,17 @@ const supportsGeminiImageTest = computed(() => {
return
props
.
account
?.
platform
===
'
gemini
'
||
(
props
.
account
?.
platform
===
'
antigravity
'
&&
props
.
account
?.
type
===
'
apikey
'
)
})
const
sortTestModels
=
(
models
:
ClaudeModel
[])
=>
{
const
priorityMap
=
new
Map
(
prioritizedGeminiModels
.
map
((
id
,
index
)
=>
[
id
,
index
]))
return
[...
models
].
sort
((
a
,
b
)
=>
{
const
aPriority
=
priorityMap
.
get
(
a
.
id
)
??
Number
.
MAX_SAFE_INTEGER
const
bPriority
=
priorityMap
.
get
(
b
.
id
)
??
Number
.
MAX_SAFE_INTEGER
if
(
aPriority
!==
bPriority
)
return
aPriority
-
bPriority
return
0
})
}
// Load available models when modal opens
watch
(
()
=>
props
.
show
,
...
...
@@ -300,17 +312,14 @@ const loadAvailableModels = async () => {
loadingModels
.
value
=
true
selectedModelId
.
value
=
''
// Reset selection before loading
try
{
availableModels
.
value
=
await
adminAPI
.
accounts
.
getAvailableModels
(
props
.
account
.
id
)
const
models
=
await
adminAPI
.
accounts
.
getAvailableModels
(
props
.
account
.
id
)
availableModels
.
value
=
props
.
account
.
platform
===
'
gemini
'
||
props
.
account
.
platform
===
'
antigravity
'
?
sortTestModels
(
models
)
:
models
// Default selection by platform
if
(
availableModels
.
value
.
length
>
0
)
{
if
(
props
.
account
.
platform
===
'
gemini
'
)
{
const
preferred
=
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-2.0-flash
'
)
||
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-2.5-flash
'
)
||
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-2.5-pro
'
)
||
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-3-flash-preview
'
)
||
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-3-pro-preview
'
)
selectedModelId
.
value
=
preferred
?.
id
||
availableModels
.
value
[
0
].
id
selectedModelId
.
value
=
availableModels
.
value
[
0
].
id
}
else
{
// Try to select Sonnet as default, otherwise use first model
const
sonnetModel
=
availableModels
.
value
.
find
((
m
)
=>
m
.
id
.
includes
(
'
sonnet
'
))
...
...
frontend/src/components/account/BulkEditAccountModal.vue
View file @
3fcefe6c
...
...
@@ -959,11 +959,11 @@ const allModels = [
{
value
:
'
gpt-5.1-2025-11-13
'
,
label
:
'
GPT-5.1
'
}
,
{
value
:
'
gpt-5.1-codex-mini
'
,
label
:
'
GPT-5.1 Codex Mini
'
}
,
{
value
:
'
gpt-5-2025-08-07
'
,
label
:
'
GPT-5
'
}
,
{
value
:
'
gemini-3.1-flash-image
'
,
label
:
'
Gemini 3.1 Flash Image
'
}
,
{
value
:
'
gemini-2.5-flash-image
'
,
label
:
'
Gemini 2.5 Flash Image
'
}
,
{
value
:
'
gemini-2.0-flash
'
,
label
:
'
Gemini 2.0 Flash
'
}
,
{
value
:
'
gemini-2.5-flash
'
,
label
:
'
Gemini 2.5 Flash
'
}
,
{
value
:
'
gemini-2.5-flash-image
'
,
label
:
'
Gemini 2.5 Flash Image
'
}
,
{
value
:
'
gemini-2.5-pro
'
,
label
:
'
Gemini 2.5 Pro
'
}
,
{
value
:
'
gemini-3.1-flash-image
'
,
label
:
'
Gemini 3.1 Flash Image
'
}
,
{
value
:
'
gemini-3-pro-image
'
,
label
:
'
Gemini 3 Pro Image (Legacy)
'
}
,
{
value
:
'
gemini-3-flash-preview
'
,
label
:
'
Gemini 3 Flash Preview
'
}
,
{
value
:
'
gemini-3-pro-preview
'
,
label
:
'
Gemini 3 Pro Preview
'
}
...
...
frontend/src/components/account/__tests__/BulkEditAccountModal.spec.ts
View file @
3fcefe6c
...
...
@@ -18,6 +18,10 @@ vi.mock('@/api/admin', () => ({
}
}))
vi
.
mock
(
'
@/api/admin/accounts
'
,
()
=>
({
getAntigravityDefaultModelMapping
:
vi
.
fn
()
}))
vi
.
mock
(
'
vue-i18n
'
,
async
()
=>
{
const
actual
=
await
vi
.
importActual
<
typeof
import
(
'
vue-i18n
'
)
>
(
'
vue-i18n
'
)
return
{
...
...
frontend/src/components/admin/account/AccountTestModal.vue
View file @
3fcefe6c
...
...
@@ -260,6 +260,7 @@ const loadingModels = ref(false)
let
eventSource
:
EventSource
|
null
=
null
const
isSoraAccount
=
computed
(()
=>
props
.
account
?.
platform
===
'
sora
'
)
const
generatedImages
=
ref
<
PreviewImage
[]
>
([])
const
prioritizedGeminiModels
=
[
'
gemini-3.1-flash-image
'
,
'
gemini-2.5-flash-image
'
,
'
gemini-2.5-flash
'
,
'
gemini-2.5-pro
'
,
'
gemini-3-flash-preview
'
,
'
gemini-3-pro-preview
'
,
'
gemini-2.0-flash
'
]
const
supportsGeminiImageTest
=
computed
(()
=>
{
if
(
isSoraAccount
.
value
)
return
false
const
modelID
=
selectedModelId
.
value
.
toLowerCase
()
...
...
@@ -268,6 +269,17 @@ const supportsGeminiImageTest = computed(() => {
return
props
.
account
?.
platform
===
'
gemini
'
||
(
props
.
account
?.
platform
===
'
antigravity
'
&&
props
.
account
?.
type
===
'
apikey
'
)
})
const
sortTestModels
=
(
models
:
ClaudeModel
[])
=>
{
const
priorityMap
=
new
Map
(
prioritizedGeminiModels
.
map
((
id
,
index
)
=>
[
id
,
index
]))
return
[...
models
].
sort
((
a
,
b
)
=>
{
const
aPriority
=
priorityMap
.
get
(
a
.
id
)
??
Number
.
MAX_SAFE_INTEGER
const
bPriority
=
priorityMap
.
get
(
b
.
id
)
??
Number
.
MAX_SAFE_INTEGER
if
(
aPriority
!==
bPriority
)
return
aPriority
-
bPriority
return
0
})
}
// Load available models when modal opens
watch
(
()
=>
props
.
show
,
...
...
@@ -300,17 +312,14 @@ const loadAvailableModels = async () => {
loadingModels
.
value
=
true
selectedModelId
.
value
=
''
// Reset selection before loading
try
{
availableModels
.
value
=
await
adminAPI
.
accounts
.
getAvailableModels
(
props
.
account
.
id
)
const
models
=
await
adminAPI
.
accounts
.
getAvailableModels
(
props
.
account
.
id
)
availableModels
.
value
=
props
.
account
.
platform
===
'
gemini
'
||
props
.
account
.
platform
===
'
antigravity
'
?
sortTestModels
(
models
)
:
models
// Default selection by platform
if
(
availableModels
.
value
.
length
>
0
)
{
if
(
props
.
account
.
platform
===
'
gemini
'
)
{
const
preferred
=
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-2.0-flash
'
)
||
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-2.5-flash
'
)
||
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-2.5-pro
'
)
||
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-3-flash-preview
'
)
||
availableModels
.
value
.
find
((
m
)
=>
m
.
id
===
'
gemini-3-pro-preview
'
)
selectedModelId
.
value
=
preferred
?.
id
||
availableModels
.
value
[
0
].
id
selectedModelId
.
value
=
availableModels
.
value
[
0
].
id
}
else
{
// Try to select Sonnet as default, otherwise use first model
const
sonnetModel
=
availableModels
.
value
.
find
((
m
)
=>
m
.
id
.
includes
(
'
sonnet
'
))
...
...
frontend/src/components/admin/account/__tests__/AccountTestModal.spec.ts
View file @
3fcefe6c
...
...
@@ -89,7 +89,9 @@ function mountModal() {
describe
(
'
AccountTestModal
'
,
()
=>
{
beforeEach
(()
=>
{
getAvailableModels
.
mockResolvedValue
([
{
id
:
'
gemini-2.5-flash-image
'
,
display_name
:
'
Gemini 2.5 Flash Image
'
}
{
id
:
'
gemini-2.0-flash
'
,
display_name
:
'
Gemini 2.0 Flash
'
},
{
id
:
'
gemini-2.5-flash-image
'
,
display_name
:
'
Gemini 2.5 Flash Image
'
},
{
id
:
'
gemini-3.1-flash-image
'
,
display_name
:
'
Gemini 3.1 Flash Image
'
}
])
copyToClipboard
.
mockReset
()
Object
.
defineProperty
(
globalThis
,
'
localStorage
'
,
{
...
...
@@ -134,7 +136,7 @@ describe('AccountTestModal', () => {
expect
(
global
.
fetch
).
toHaveBeenCalledTimes
(
1
)
const
[,
request
]
=
(
global
.
fetch
as
any
).
mock
.
calls
[
0
]
expect
(
JSON
.
parse
(
request
.
body
)).
toEqual
({
model_id
:
'
gemini-
2.5
-flash-image
'
,
model_id
:
'
gemini-
3.1
-flash-image
'
,
prompt
:
'
draw a tiny orange cat astronaut
'
})
...
...
frontend/src/composables/__tests__/useModelWhitelist.spec.ts
View file @
3fcefe6c
...
...
@@ -27,6 +27,15 @@ describe('useModelWhitelist', () => {
expect
(
models
).
toContain
(
'
gemini-2.5-flash-image
'
)
expect
(
models
).
toContain
(
'
gemini-3.1-flash-image
'
)
expect
(
models
.
indexOf
(
'
gemini-3.1-flash-image
'
)).
toBeLessThan
(
models
.
indexOf
(
'
gemini-2.0-flash
'
))
expect
(
models
.
indexOf
(
'
gemini-2.5-flash-image
'
)).
toBeLessThan
(
models
.
indexOf
(
'
gemini-2.5-flash
'
))
})
it
(
'
antigravity 模型列表会把新的 Gemini 图片模型排在前面
'
,
()
=>
{
const
models
=
getModelsByPlatform
(
'
antigravity
'
)
expect
(
models
.
indexOf
(
'
gemini-3.1-flash-image
'
)).
toBeLessThan
(
models
.
indexOf
(
'
gemini-2.5-flash
'
))
expect
(
models
.
indexOf
(
'
gemini-2.5-flash-image
'
)).
toBeLessThan
(
models
.
indexOf
(
'
gemini-2.5-flash-lite
'
))
})
it
(
'
whitelist 模式会忽略通配符条目
'
,
()
=>
{
...
...
frontend/src/composables/useModelWhitelist.ts
View file @
3fcefe6c
...
...
@@ -51,13 +51,13 @@ export const claudeModels = [
const
geminiModels
=
[
// Keep in sync with backend curated Gemini lists.
// This list is intentionally conservative (models commonly available across OAuth/API key).
'
gemini-3.1-flash-image
'
,
'
gemini-2.5-flash-image
'
,
'
gemini-2.0-flash
'
,
'
gemini-2.5-flash
'
,
'
gemini-2.5-flash-image
'
,
'
gemini-2.5-pro
'
,
'
gemini-3-flash-preview
'
,
'
gemini-3-pro-preview
'
,
'
gemini-3.1-flash-image
'
'
gemini-3-pro-preview
'
]
// Sora
...
...
@@ -87,8 +87,9 @@ const antigravityModels = [
'
claude-sonnet-4-5
'
,
'
claude-sonnet-4-5-thinking
'
,
// Gemini 2.5 系列
'
gemini-
2.5
-flash
'
,
'
gemini-
3.1
-flash
-image
'
,
'
gemini-2.5-flash-image
'
,
'
gemini-2.5-flash
'
,
'
gemini-2.5-flash-lite
'
,
'
gemini-2.5-flash-thinking
'
,
'
gemini-2.5-pro
'
,
...
...
@@ -99,7 +100,6 @@ const antigravityModels = [
// Gemini 3.1 系列
'
gemini-3.1-pro-high
'
,
'
gemini-3.1-pro-low
'
,
'
gemini-3.1-flash-image
'
,
'
gemini-3-pro-image
'
,
// 其他
'
gpt-oss-120b-medium
'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment