Unverified Commit f0e08e76 authored by Wesley Liddick's avatar Wesley Liddick Committed by GitHub
Browse files

Merge pull request #930 from GuangYiDing/feat/gemini-25-flash-image-support

feat: 修复 Gemini 生图接口并新增前端生图测试能力
parents 10b82592 3fcefe6c
...@@ -959,6 +959,23 @@ function generateOpenCodeConfig(platform: string, baseUrl: string, apiKey: strin ...@@ -959,6 +959,23 @@ function generateOpenCodeConfig(platform: string, baseUrl: string, apiKey: strin
} }
} }
}, },
'gemini-2.5-flash-image': {
name: 'Gemini 2.5 Flash Image',
limit: {
context: 1048576,
output: 65536
},
modalities: {
input: ['text', 'image'],
output: ['image']
},
options: {
thinking: {
budgetTokens: 24576,
type: 'enabled'
}
}
},
'gemini-3.1-flash-image': { 'gemini-3.1-flash-image': {
name: 'Gemini 3.1 Flash Image', name: 'Gemini 3.1 Flash Image',
limit: { limit: {
......
import { describe, expect, it } from 'vitest' import { describe, expect, it, vi } from 'vitest'
vi.mock('@/api/admin/accounts', () => ({
getAntigravityDefaultModelMapping: vi.fn()
}))
import { buildModelMappingObject, getModelsByPlatform } from '../useModelWhitelist' import { buildModelMappingObject, getModelsByPlatform } from '../useModelWhitelist'
describe('useModelWhitelist', () => { describe('useModelWhitelist', () => {
...@@ -12,10 +17,27 @@ describe('useModelWhitelist', () => { ...@@ -12,10 +17,27 @@ describe('useModelWhitelist', () => {
it('antigravity 模型列表包含图片模型兼容项', () => { it('antigravity 模型列表包含图片模型兼容项', () => {
const models = getModelsByPlatform('antigravity') const models = getModelsByPlatform('antigravity')
expect(models).toContain('gemini-2.5-flash-image')
expect(models).toContain('gemini-3.1-flash-image') expect(models).toContain('gemini-3.1-flash-image')
expect(models).toContain('gemini-3-pro-image') expect(models).toContain('gemini-3-pro-image')
}) })
it('gemini 模型列表包含原生生图模型', () => {
const models = getModelsByPlatform('gemini')
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 模式会忽略通配符条目', () => { it('whitelist 模式会忽略通配符条目', () => {
const mapping = buildModelMappingObject('whitelist', ['claude-*', 'gemini-3.1-flash-image'], []) const mapping = buildModelMappingObject('whitelist', ['claude-*', 'gemini-3.1-flash-image'], [])
expect(mapping).toEqual({ expect(mapping).toEqual({
......
...@@ -51,6 +51,8 @@ export const claudeModels = [ ...@@ -51,6 +51,8 @@ export const claudeModels = [
const geminiModels = [ const geminiModels = [
// Keep in sync with backend curated Gemini lists. // Keep in sync with backend curated Gemini lists.
// This list is intentionally conservative (models commonly available across OAuth/API key). // 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.0-flash',
'gemini-2.5-flash', 'gemini-2.5-flash',
'gemini-2.5-pro', 'gemini-2.5-pro',
...@@ -85,6 +87,8 @@ const antigravityModels = [ ...@@ -85,6 +87,8 @@ const antigravityModels = [
'claude-sonnet-4-5', 'claude-sonnet-4-5',
'claude-sonnet-4-5-thinking', 'claude-sonnet-4-5-thinking',
// Gemini 2.5 系列 // Gemini 2.5 系列
'gemini-3.1-flash-image',
'gemini-2.5-flash-image',
'gemini-2.5-flash', 'gemini-2.5-flash',
'gemini-2.5-flash-lite', 'gemini-2.5-flash-lite',
'gemini-2.5-flash-thinking', 'gemini-2.5-flash-thinking',
...@@ -96,7 +100,6 @@ const antigravityModels = [ ...@@ -96,7 +100,6 @@ const antigravityModels = [
// Gemini 3.1 系列 // Gemini 3.1 系列
'gemini-3.1-pro-high', 'gemini-3.1-pro-high',
'gemini-3.1-pro-low', 'gemini-3.1-pro-low',
'gemini-3.1-flash-image',
'gemini-3-pro-image', 'gemini-3-pro-image',
// 其他 // 其他
'gpt-oss-120b-medium', 'gpt-oss-120b-medium',
...@@ -291,7 +294,9 @@ const soraPresetMappings: { label: string; from: string; to: string; color: stri ...@@ -291,7 +294,9 @@ const soraPresetMappings: { label: string; from: string; to: string; color: stri
const geminiPresetMappings = [ const geminiPresetMappings = [
{ label: 'Flash 2.0', from: 'gemini-2.0-flash', to: 'gemini-2.0-flash', color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400' }, { label: 'Flash 2.0', from: 'gemini-2.0-flash', to: 'gemini-2.0-flash', color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400' },
{ label: '2.5 Flash', from: 'gemini-2.5-flash', to: 'gemini-2.5-flash', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' }, { label: '2.5 Flash', from: 'gemini-2.5-flash', to: 'gemini-2.5-flash', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' },
{ label: '2.5 Pro', from: 'gemini-2.5-pro', to: 'gemini-2.5-pro', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' } { label: '2.5 Image', from: 'gemini-2.5-flash-image', to: 'gemini-2.5-flash-image', color: 'bg-sky-100 text-sky-700 hover:bg-sky-200 dark:bg-sky-900/30 dark:text-sky-400' },
{ label: '2.5 Pro', from: 'gemini-2.5-pro', to: 'gemini-2.5-pro', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' },
{ 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' }
] ]
// Antigravity 预设映射(支持通配符) // Antigravity 预设映射(支持通配符)
...@@ -314,6 +319,9 @@ const antigravityPresetMappings = [ ...@@ -314,6 +319,9 @@ const antigravityPresetMappings = [
// Gemini 通配符映射 // Gemini 通配符映射
{ label: 'Gemini 3→Flash', from: 'gemini-3*', to: 'gemini-3-flash', color: 'bg-yellow-100 text-yellow-700 hover:bg-yellow-200 dark:bg-yellow-900/30 dark:text-yellow-400' }, { label: 'Gemini 3→Flash', from: 'gemini-3*', to: 'gemini-3-flash', color: 'bg-yellow-100 text-yellow-700 hover:bg-yellow-200 dark:bg-yellow-900/30 dark:text-yellow-400' },
{ label: 'Gemini 2.5→Flash', from: 'gemini-2.5*', to: 'gemini-2.5-flash', color: 'bg-orange-100 text-orange-700 hover:bg-orange-200 dark:bg-orange-900/30 dark:text-orange-400' }, { label: 'Gemini 2.5→Flash', from: 'gemini-2.5*', to: 'gemini-2.5-flash', color: 'bg-orange-100 text-orange-700 hover:bg-orange-200 dark:bg-orange-900/30 dark:text-orange-400' },
{ label: '2.5-Flash-Image透传', from: 'gemini-2.5-flash-image', to: 'gemini-2.5-flash-image', color: 'bg-sky-100 text-sky-700 hover:bg-sky-200 dark:bg-sky-900/30 dark:text-sky-400' },
{ label: '3.1-Flash-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' },
{ label: '3-Pro-Image→3.1', from: 'gemini-3-pro-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' },
{ label: '3-Flash透传', from: 'gemini-3-flash', to: 'gemini-3-flash', color: 'bg-lime-100 text-lime-700 hover:bg-lime-200 dark:bg-lime-900/30 dark:text-lime-400' }, { label: '3-Flash透传', from: 'gemini-3-flash', to: 'gemini-3-flash', color: 'bg-lime-100 text-lime-700 hover:bg-lime-200 dark:bg-lime-900/30 dark:text-lime-400' },
{ label: '2.5-Flash-Lite透传', from: 'gemini-2.5-flash-lite', to: 'gemini-2.5-flash-lite', color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400' }, { label: '2.5-Flash-Lite透传', from: 'gemini-2.5-flash-lite', to: 'gemini-2.5-flash-lite', color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400' },
// 精确映射 // 精确映射
......
...@@ -2416,6 +2416,7 @@ export default { ...@@ -2416,6 +2416,7 @@ export default {
connectedToApi: 'Connected to API', connectedToApi: 'Connected to API',
usingModel: 'Using model: {model}', usingModel: 'Using model: {model}',
sendingTestMessage: 'Sending test message: "hi"', sendingTestMessage: 'Sending test message: "hi"',
sendingGeminiImageRequest: 'Sending Gemini image generation test request...',
response: 'Response:', response: 'Response:',
startTest: 'Start Test', startTest: 'Start Test',
testing: 'Testing...', testing: 'Testing...',
...@@ -2427,6 +2428,13 @@ export default { ...@@ -2427,6 +2428,13 @@ export default {
selectTestModel: 'Select Test Model', selectTestModel: 'Select Test Model',
testModel: 'Test model', testModel: 'Test model',
testPrompt: 'Prompt: "hi"', testPrompt: 'Prompt: "hi"',
geminiImagePromptLabel: 'Image prompt',
geminiImagePromptPlaceholder: 'Example: Generate an orange cat astronaut sticker in pixel-art style on a solid background.',
geminiImagePromptDefault: 'Generate a cute orange cat astronaut sticker on a clean pastel background.',
geminiImageTestHint: 'When a Gemini image model is selected, this test sends a real image-generation request and previews the returned image below.',
geminiImageTestMode: 'Mode: Gemini image generation test',
geminiImagePreview: 'Generated images:',
geminiImageReceived: 'Received test image #{count}',
soraUpstreamBaseUrlHint: 'Upstream Sora service URL (another Sub2API instance or compatible API)', soraUpstreamBaseUrlHint: 'Upstream Sora service URL (another Sub2API instance or compatible API)',
soraTestHint: 'Sora test runs connectivity and capability checks (/backend/me, subscription, Sora2 invite and remaining quota).', soraTestHint: 'Sora test runs connectivity and capability checks (/backend/me, subscription, Sora2 invite and remaining quota).',
soraTestTarget: 'Target: Sora account capability', soraTestTarget: 'Target: Sora account capability',
......
...@@ -2545,6 +2545,7 @@ export default { ...@@ -2545,6 +2545,7 @@ export default {
connectedToApi: '已连接到 API', connectedToApi: '已连接到 API',
usingModel: '使用模型:{model}', usingModel: '使用模型:{model}',
sendingTestMessage: '发送测试消息:"hi"', sendingTestMessage: '发送测试消息:"hi"',
sendingGeminiImageRequest: '发送 Gemini 生图测试请求...',
response: '响应:', response: '响应:',
startTest: '开始测试', startTest: '开始测试',
retry: '重试', retry: '重试',
...@@ -2555,6 +2556,13 @@ export default { ...@@ -2555,6 +2556,13 @@ export default {
selectTestModel: '选择测试模型', selectTestModel: '选择测试模型',
testModel: '测试模型', testModel: '测试模型',
testPrompt: '提示词:"hi"', testPrompt: '提示词:"hi"',
geminiImagePromptLabel: '生图提示词',
geminiImagePromptPlaceholder: '例如:生成一只戴宇航员头盔的橘猫,像素插画风格,纯色背景。',
geminiImagePromptDefault: 'Generate a cute orange cat astronaut sticker on a clean pastel background.',
geminiImageTestHint: '选择 Gemini 图片模型后,这里会直接发起生图测试,并在下方展示返回图片。',
geminiImageTestMode: '模式:Gemini 生图测试',
geminiImagePreview: '生成结果:',
geminiImageReceived: '已收到第 {count} 张测试图片',
soraUpstreamBaseUrlHint: '上游 Sora 服务地址(另一个 Sub2API 实例或兼容 API)', soraUpstreamBaseUrlHint: '上游 Sora 服务地址(另一个 Sub2API 实例或兼容 API)',
soraTestHint: 'Sora 测试将执行连通性与能力检测(/backend/me、订阅信息、Sora2 邀请码与剩余额度)。', soraTestHint: 'Sora 测试将执行连通性与能力检测(/backend/me、订阅信息、Sora2 邀请码与剩余额度)。',
soraTestTarget: '检测目标:Sora 账号能力', soraTestTarget: '检测目标:Sora 账号能力',
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment