Commit 618a614c authored by yangjianbo's avatar yangjianbo
Browse files

feat(Sora): 完成Sora网关接入与媒体能力

新增 Sora 网关路由、账号调度与同步服务\n补充媒体代理与签名 URL、模型列表动态拉取\n完善计费配置、前端支持与相关测试
parent 99dc3b59
...@@ -19,6 +19,12 @@ ...@@ -19,6 +19,12 @@
<svg v-else-if="platform === 'antigravity'" :class="sizeClass" viewBox="0 0 24 24" fill="currentColor"> <svg v-else-if="platform === 'antigravity'" :class="sizeClass" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z" /> <path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z" />
</svg> </svg>
<!-- Sora logo (sparkle) -->
<svg v-else-if="platform === 'sora'" :class="sizeClass" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 2.5l2.1 4.7 5.1.5-3.9 3.4 1.2 5-4.5-2.6-4.5 2.6 1.2-5-3.9-3.4 5.1-.5L12 2.5z"
/>
</svg>
<!-- Fallback: generic platform icon --> <!-- Fallback: generic platform icon -->
<svg v-else :class="sizeClass" fill="currentColor" viewBox="0 0 24 24"> <svg v-else :class="sizeClass" fill="currentColor" viewBox="0 0 24 24">
<path <path
......
...@@ -48,6 +48,7 @@ const platformLabel = computed(() => { ...@@ -48,6 +48,7 @@ const platformLabel = computed(() => {
if (props.platform === 'anthropic') return 'Anthropic' if (props.platform === 'anthropic') return 'Anthropic'
if (props.platform === 'openai') return 'OpenAI' if (props.platform === 'openai') return 'OpenAI'
if (props.platform === 'antigravity') return 'Antigravity' if (props.platform === 'antigravity') return 'Antigravity'
if (props.platform === 'sora') return 'Sora'
return 'Gemini' return 'Gemini'
}) })
...@@ -74,6 +75,9 @@ const platformClass = computed(() => { ...@@ -74,6 +75,9 @@ const platformClass = computed(() => {
if (props.platform === 'antigravity') { if (props.platform === 'antigravity') {
return 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400' return 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400'
} }
if (props.platform === 'sora') {
return 'bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-400'
}
return 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400' return 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400'
}) })
...@@ -87,6 +91,9 @@ const typeClass = computed(() => { ...@@ -87,6 +91,9 @@ const typeClass = computed(() => {
if (props.platform === 'antigravity') { if (props.platform === 'antigravity') {
return 'bg-purple-100 text-purple-600 dark:bg-purple-900/30 dark:text-purple-400' return 'bg-purple-100 text-purple-600 dark:bg-purple-900/30 dark:text-purple-400'
} }
if (props.platform === 'sora') {
return 'bg-rose-100 text-rose-600 dark:bg-rose-900/30 dark:text-rose-400'
}
return 'bg-blue-100 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400' return 'bg-blue-100 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400'
}) })
</script> </script>
...@@ -52,6 +52,22 @@ const geminiModels = [ ...@@ -52,6 +52,22 @@ const geminiModels = [
'gemini-3-pro-preview' 'gemini-3-pro-preview'
] ]
// Sora (sora2api)
const soraModels = [
'gpt-image', 'gpt-image-landscape', 'gpt-image-portrait',
'sora2-landscape-10s', 'sora2-portrait-10s',
'sora2-landscape-15s', 'sora2-portrait-15s',
'sora2-landscape-25s', 'sora2-portrait-25s',
'sora2pro-landscape-10s', 'sora2pro-portrait-10s',
'sora2pro-landscape-15s', 'sora2pro-portrait-15s',
'sora2pro-landscape-25s', 'sora2pro-portrait-25s',
'sora2pro-hd-landscape-10s', 'sora2pro-hd-portrait-10s',
'sora2pro-hd-landscape-15s', 'sora2pro-hd-portrait-15s',
'prompt-enhance-short-10s', 'prompt-enhance-short-15s', 'prompt-enhance-short-20s',
'prompt-enhance-medium-10s', 'prompt-enhance-medium-15s', 'prompt-enhance-medium-20s',
'prompt-enhance-long-10s', 'prompt-enhance-long-15s', 'prompt-enhance-long-20s'
]
// 智谱 GLM // 智谱 GLM
const zhipuModels = [ const zhipuModels = [
'glm-4', 'glm-4v', 'glm-4-plus', 'glm-4-0520', 'glm-4', 'glm-4v', 'glm-4-plus', 'glm-4-0520',
...@@ -182,6 +198,7 @@ const allModelsList: string[] = [ ...@@ -182,6 +198,7 @@ const allModelsList: string[] = [
...openaiModels, ...openaiModels,
...claudeModels, ...claudeModels,
...geminiModels, ...geminiModels,
...soraModels,
...zhipuModels, ...zhipuModels,
...qwenModels, ...qwenModels,
...deepseekModels, ...deepseekModels,
...@@ -227,6 +244,8 @@ const openaiPresetMappings = [ ...@@ -227,6 +244,8 @@ const openaiPresetMappings = [
{ label: 'GPT-5.1 Codex', from: 'gpt-5.1-codex', to: 'gpt-5.1-codex', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' } { label: 'GPT-5.1 Codex', from: 'gpt-5.1-codex', to: 'gpt-5.1-codex', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' }
] ]
const soraPresetMappings: { label: string; from: string; to: string; color: string }[] = []
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' },
...@@ -258,6 +277,7 @@ export function getModelsByPlatform(platform: string): string[] { ...@@ -258,6 +277,7 @@ export function getModelsByPlatform(platform: string): string[] {
case 'anthropic': case 'anthropic':
case 'claude': return claudeModels case 'claude': return claudeModels
case 'gemini': return geminiModels case 'gemini': return geminiModels
case 'sora': return soraModels
case 'zhipu': return zhipuModels case 'zhipu': return zhipuModels
case 'qwen': return qwenModels case 'qwen': return qwenModels
case 'deepseek': return deepseekModels case 'deepseek': return deepseekModels
...@@ -281,6 +301,7 @@ export function getModelsByPlatform(platform: string): string[] { ...@@ -281,6 +301,7 @@ export function getModelsByPlatform(platform: string): string[] {
export function getPresetMappingsByPlatform(platform: string) { export function getPresetMappingsByPlatform(platform: string) {
if (platform === 'openai') return openaiPresetMappings if (platform === 'openai') return openaiPresetMappings
if (platform === 'gemini') return geminiPresetMappings if (platform === 'gemini') return geminiPresetMappings
if (platform === 'sora') return soraPresetMappings
return anthropicPresetMappings return anthropicPresetMappings
} }
......
...@@ -895,7 +895,8 @@ export default { ...@@ -895,7 +895,8 @@ export default {
anthropic: 'Anthropic', anthropic: 'Anthropic',
openai: 'OpenAI', openai: 'OpenAI',
gemini: 'Gemini', gemini: 'Gemini',
antigravity: 'Antigravity' antigravity: 'Antigravity',
sora: 'Sora'
}, },
deleteConfirm: deleteConfirm:
"Are you sure you want to delete '{name}'? All associated API keys will no longer belong to any group.", "Are you sure you want to delete '{name}'? All associated API keys will no longer belong to any group.",
...@@ -920,6 +921,14 @@ export default { ...@@ -920,6 +921,14 @@ export default {
title: 'Image Generation Pricing', title: 'Image Generation Pricing',
description: 'Configure pricing for gemini-3-pro-image model. Leave empty to use default prices.' description: 'Configure pricing for gemini-3-pro-image model. Leave empty to use default prices.'
}, },
soraPricing: {
title: 'Sora Per-Request Pricing',
description: 'Configure per-request pricing for Sora image/video generation. Leave empty to disable billing.',
image360: 'Image 360px ($)',
image540: 'Image 540px ($)',
video: 'Video (standard) ($)',
videoHd: 'Video (Pro-HD) ($)'
},
claudeCode: { claudeCode: {
title: 'Claude Code Client Restriction', title: 'Claude Code Client Restriction',
tooltip: 'When enabled, this group only allows official Claude Code clients. Non-Claude Code requests will be rejected or fallback to the specified group.', tooltip: 'When enabled, this group only allows official Claude Code clients. Non-Claude Code requests will be rejected or fallback to the specified group.',
...@@ -1079,7 +1088,8 @@ export default { ...@@ -1079,7 +1088,8 @@ export default {
claude: 'Claude', claude: 'Claude',
openai: 'OpenAI', openai: 'OpenAI',
gemini: 'Gemini', gemini: 'Gemini',
antigravity: 'Antigravity' antigravity: 'Antigravity',
sora: 'Sora'
}, },
types: { types: {
oauth: 'OAuth', oauth: 'OAuth',
...@@ -1257,6 +1267,9 @@ export default { ...@@ -1257,6 +1267,9 @@ export default {
'Map request models to actual models. Left is the requested model, right is the actual model sent to API.', 'Map request models to actual models. Left is the requested model, right is the actual model sent to API.',
selectedModels: 'Selected {count} model(s)', selectedModels: 'Selected {count} model(s)',
supportsAllModels: '(supports all models)', supportsAllModels: '(supports all models)',
soraModelsLoadFailed: 'Failed to load Sora models, fallback to default list',
soraModelsLoading: 'Loading Sora models...',
soraModelsRetry: 'Load failed, click to retry',
requestModel: 'Request model', requestModel: 'Request model',
actualModel: 'Actual model', actualModel: 'Actual model',
addMapping: 'Add Mapping', addMapping: 'Add Mapping',
......
...@@ -941,7 +941,8 @@ export default { ...@@ -941,7 +941,8 @@ export default {
anthropic: 'Anthropic', anthropic: 'Anthropic',
openai: 'OpenAI', openai: 'OpenAI',
gemini: 'Gemini', gemini: 'Gemini',
antigravity: 'Antigravity' antigravity: 'Antigravity',
sora: 'Sora'
}, },
saving: '保存中...', saving: '保存中...',
noGroups: '暂无分组', noGroups: '暂无分组',
...@@ -995,6 +996,14 @@ export default { ...@@ -995,6 +996,14 @@ export default {
title: '图片生成计费', title: '图片生成计费',
description: '配置 gemini-3-pro-image 模型的图片生成价格,留空则使用默认价格' description: '配置 gemini-3-pro-image 模型的图片生成价格,留空则使用默认价格'
}, },
soraPricing: {
title: 'Sora 按次计费',
description: '配置 Sora 图片/视频按次收费价格,留空则默认不计费',
image360: '图片 360px ($)',
image540: '图片 540px ($)',
video: '视频(标准)($)',
videoHd: '视频(Pro-HD)($)'
},
claudeCode: { claudeCode: {
title: 'Claude Code 客户端限制', title: 'Claude Code 客户端限制',
tooltip: '启用后,此分组仅允许 Claude Code 官方客户端访问。非 Claude Code 请求将被拒绝或降级到指定分组。', tooltip: '启用后,此分组仅允许 Claude Code 官方客户端访问。非 Claude Code 请求将被拒绝或降级到指定分组。',
...@@ -1199,7 +1208,8 @@ export default { ...@@ -1199,7 +1208,8 @@ export default {
openai: 'OpenAI', openai: 'OpenAI',
anthropic: 'Anthropic', anthropic: 'Anthropic',
gemini: 'Gemini', gemini: 'Gemini',
antigravity: 'Antigravity' antigravity: 'Antigravity',
sora: 'Sora'
}, },
types: { types: {
oauth: 'OAuth', oauth: 'OAuth',
...@@ -1391,6 +1401,9 @@ export default { ...@@ -1391,6 +1401,9 @@ export default {
mapRequestModels: '将请求模型映射到实际模型。左边是请求的模型,右边是发送到 API 的实际模型。', mapRequestModels: '将请求模型映射到实际模型。左边是请求的模型,右边是发送到 API 的实际模型。',
selectedModels: '已选择 {count} 个模型', selectedModels: '已选择 {count} 个模型',
supportsAllModels: '(支持所有模型)', supportsAllModels: '(支持所有模型)',
soraModelsLoadFailed: '加载 Sora 模型列表失败,已回退到默认列表',
soraModelsLoading: '正在加载 Sora 模型...',
soraModelsRetry: '加载失败,点击重试',
requestModel: '请求模型', requestModel: '请求模型',
actualModel: '实际模型', actualModel: '实际模型',
addMapping: '添加映射', addMapping: '添加映射',
......
...@@ -252,7 +252,7 @@ export interface PaginationConfig { ...@@ -252,7 +252,7 @@ export interface PaginationConfig {
// ==================== API Key & Group Types ==================== // ==================== API Key & Group Types ====================
export type GroupPlatform = 'anthropic' | 'openai' | 'gemini' | 'antigravity' export type GroupPlatform = 'anthropic' | 'openai' | 'gemini' | 'antigravity' | 'sora'
export type SubscriptionType = 'standard' | 'subscription' export type SubscriptionType = 'standard' | 'subscription'
...@@ -272,6 +272,11 @@ export interface Group { ...@@ -272,6 +272,11 @@ export interface Group {
image_price_1k: number | null image_price_1k: number | null
image_price_2k: number | null image_price_2k: number | null
image_price_4k: number | null image_price_4k: number | null
// Sora 按次计费配置
sora_image_price_360: number | null
sora_image_price_540: number | null
sora_video_price_per_request: number | null
sora_video_price_per_request_hd: number | null
// Claude Code 客户端限制 // Claude Code 客户端限制
claude_code_only: boolean claude_code_only: boolean
fallback_group_id: number | null fallback_group_id: number | null
...@@ -331,6 +336,10 @@ export interface CreateGroupRequest { ...@@ -331,6 +336,10 @@ export interface CreateGroupRequest {
image_price_1k?: number | null image_price_1k?: number | null
image_price_2k?: number | null image_price_2k?: number | null
image_price_4k?: number | null image_price_4k?: number | null
sora_image_price_360?: number | null
sora_image_price_540?: number | null
sora_video_price_per_request?: number | null
sora_video_price_per_request_hd?: number | null
claude_code_only?: boolean claude_code_only?: boolean
fallback_group_id?: number | null fallback_group_id?: number | null
} }
...@@ -349,13 +358,17 @@ export interface UpdateGroupRequest { ...@@ -349,13 +358,17 @@ export interface UpdateGroupRequest {
image_price_1k?: number | null image_price_1k?: number | null
image_price_2k?: number | null image_price_2k?: number | null
image_price_4k?: number | null image_price_4k?: number | null
sora_image_price_360?: number | null
sora_image_price_540?: number | null
sora_video_price_per_request?: number | null
sora_video_price_per_request_hd?: number | null
claude_code_only?: boolean claude_code_only?: boolean
fallback_group_id?: number | null fallback_group_id?: number | null
} }
// ==================== Account & Proxy Types ==================== // ==================== Account & Proxy Types ====================
export type AccountPlatform = 'anthropic' | 'openai' | 'gemini' | 'antigravity' export type AccountPlatform = 'anthropic' | 'openai' | 'gemini' | 'antigravity' | 'sora'
export type AccountType = 'oauth' | 'setup-token' | 'apikey' export type AccountType = 'oauth' | 'setup-token' | 'apikey'
export type OAuthAddMethod = 'oauth' | 'setup-token' export type OAuthAddMethod = 'oauth' | 'setup-token'
export type ProxyProtocol = 'http' | 'https' | 'socks5' | 'socks5h' export type ProxyProtocol = 'http' | 'https' | 'socks5' | 'socks5h'
......
...@@ -404,6 +404,64 @@ ...@@ -404,6 +404,64 @@
</div> </div>
</div> </div>
<!-- Sora 按次计费配置 -->
<div v-if="createForm.platform === 'sora'" class="border-t pt-4">
<label class="block mb-2 font-medium text-gray-700 dark:text-gray-300">
{{ t('admin.groups.soraPricing.title') }}
</label>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
{{ t('admin.groups.soraPricing.description') }}
</p>
<div class="grid grid-cols-2 gap-3 mb-4">
<div>
<label class="input-label">{{ t('admin.groups.soraPricing.image360') }}</label>
<input
v-model.number="createForm.sora_image_price_360"
type="number"
step="0.001"
min="0"
class="input"
placeholder="0.05"
/>
</div>
<div>
<label class="input-label">{{ t('admin.groups.soraPricing.image540') }}</label>
<input
v-model.number="createForm.sora_image_price_540"
type="number"
step="0.001"
min="0"
class="input"
placeholder="0.08"
/>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="input-label">{{ t('admin.groups.soraPricing.video') }}</label>
<input
v-model.number="createForm.sora_video_price_per_request"
type="number"
step="0.001"
min="0"
class="input"
placeholder="0.5"
/>
</div>
<div>
<label class="input-label">{{ t('admin.groups.soraPricing.videoHd') }}</label>
<input
v-model.number="createForm.sora_video_price_per_request_hd"
type="number"
step="0.001"
min="0"
class="input"
placeholder="0.8"
/>
</div>
</div>
</div>
<!-- Claude Code 客户端限制 anthropic 平台 --> <!-- Claude Code 客户端限制 anthropic 平台 -->
<div v-if="createForm.platform === 'anthropic'" class="border-t pt-4"> <div v-if="createForm.platform === 'anthropic'" class="border-t pt-4">
<div class="mb-1.5 flex items-center gap-1"> <div class="mb-1.5 flex items-center gap-1">
...@@ -848,6 +906,64 @@ ...@@ -848,6 +906,64 @@
</div> </div>
</div> </div>
<!-- Sora 按次计费配置 -->
<div v-if="editForm.platform === 'sora'" class="border-t pt-4">
<label class="block mb-2 font-medium text-gray-700 dark:text-gray-300">
{{ t('admin.groups.soraPricing.title') }}
</label>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
{{ t('admin.groups.soraPricing.description') }}
</p>
<div class="grid grid-cols-2 gap-3 mb-4">
<div>
<label class="input-label">{{ t('admin.groups.soraPricing.image360') }}</label>
<input
v-model.number="editForm.sora_image_price_360"
type="number"
step="0.001"
min="0"
class="input"
placeholder="0.05"
/>
</div>
<div>
<label class="input-label">{{ t('admin.groups.soraPricing.image540') }}</label>
<input
v-model.number="editForm.sora_image_price_540"
type="number"
step="0.001"
min="0"
class="input"
placeholder="0.08"
/>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="input-label">{{ t('admin.groups.soraPricing.video') }}</label>
<input
v-model.number="editForm.sora_video_price_per_request"
type="number"
step="0.001"
min="0"
class="input"
placeholder="0.5"
/>
</div>
<div>
<label class="input-label">{{ t('admin.groups.soraPricing.videoHd') }}</label>
<input
v-model.number="editForm.sora_video_price_per_request_hd"
type="number"
step="0.001"
min="0"
class="input"
placeholder="0.8"
/>
</div>
</div>
</div>
<!-- Claude Code 客户端限制 anthropic 平台 --> <!-- Claude Code 客户端限制 anthropic 平台 -->
<div v-if="editForm.platform === 'anthropic'" class="border-t pt-4"> <div v-if="editForm.platform === 'anthropic'" class="border-t pt-4">
<div class="mb-1.5 flex items-center gap-1"> <div class="mb-1.5 flex items-center gap-1">
...@@ -1152,7 +1268,8 @@ const platformOptions = computed(() => [ ...@@ -1152,7 +1268,8 @@ const platformOptions = computed(() => [
{ value: 'anthropic', label: 'Anthropic' }, { value: 'anthropic', label: 'Anthropic' },
{ value: 'openai', label: 'OpenAI' }, { value: 'openai', label: 'OpenAI' },
{ value: 'gemini', label: 'Gemini' }, { value: 'gemini', label: 'Gemini' },
{ value: 'antigravity', label: 'Antigravity' } { value: 'antigravity', label: 'Antigravity' },
{ value: 'sora', label: 'Sora' }
]) ])
const platformFilterOptions = computed(() => [ const platformFilterOptions = computed(() => [
...@@ -1160,7 +1277,8 @@ const platformFilterOptions = computed(() => [ ...@@ -1160,7 +1277,8 @@ const platformFilterOptions = computed(() => [
{ value: 'anthropic', label: 'Anthropic' }, { value: 'anthropic', label: 'Anthropic' },
{ value: 'openai', label: 'OpenAI' }, { value: 'openai', label: 'OpenAI' },
{ value: 'gemini', label: 'Gemini' }, { value: 'gemini', label: 'Gemini' },
{ value: 'antigravity', label: 'Antigravity' } { value: 'antigravity', label: 'Antigravity' },
{ value: 'sora', label: 'Sora' }
]) ])
const editStatusOptions = computed(() => [ const editStatusOptions = computed(() => [
...@@ -1240,6 +1358,16 @@ const createForm = reactive({ ...@@ -1240,6 +1358,16 @@ const createForm = reactive({
image_price_1k: null as number | null, image_price_1k: null as number | null,
image_price_2k: null as number | null, image_price_2k: null as number | null,
image_price_4k: null as number | null, image_price_4k: null as number | null,
// Sora 按次计费配置
sora_image_price_360: null as number | null,
sora_image_price_540: null as number | null,
sora_video_price_per_request: null as number | null,
sora_video_price_per_request_hd: null as number | null,
// Sora 按次计费配置
sora_image_price_360: null as number | null,
sora_image_price_540: null as number | null,
sora_video_price_per_request: null as number | null,
sora_video_price_per_request_hd: null as number | null,
// Claude Code 客户端限制(仅 anthropic 平台使用) // Claude Code 客户端限制(仅 anthropic 平台使用)
claude_code_only: false, claude_code_only: false,
fallback_group_id: null as number | null, fallback_group_id: null as number | null,
...@@ -1411,6 +1539,11 @@ const editForm = reactive({ ...@@ -1411,6 +1539,11 @@ const editForm = reactive({
image_price_1k: null as number | null, image_price_1k: null as number | null,
image_price_2k: null as number | null, image_price_2k: null as number | null,
image_price_4k: null as number | null, image_price_4k: null as number | null,
// Sora 按次计费配置
sora_image_price_360: null as number | null,
sora_image_price_540: null as number | null,
sora_video_price_per_request: null as number | null,
sora_video_price_per_request_hd: null as number | null,
// Claude Code 客户端限制(仅 anthropic 平台使用) // Claude Code 客户端限制(仅 anthropic 平台使用)
claude_code_only: false, claude_code_only: false,
fallback_group_id: null as number | null, fallback_group_id: null as number | null,
...@@ -1495,6 +1628,10 @@ const closeCreateModal = () => { ...@@ -1495,6 +1628,10 @@ const closeCreateModal = () => {
createForm.image_price_1k = null createForm.image_price_1k = null
createForm.image_price_2k = null createForm.image_price_2k = null
createForm.image_price_4k = null createForm.image_price_4k = null
createForm.sora_image_price_360 = null
createForm.sora_image_price_540 = null
createForm.sora_video_price_per_request = null
createForm.sora_video_price_per_request_hd = null
createForm.claude_code_only = false createForm.claude_code_only = false
createForm.fallback_group_id = null createForm.fallback_group_id = null
createModelRoutingRules.value = [] createModelRoutingRules.value = []
...@@ -1544,6 +1681,10 @@ const handleEdit = async (group: AdminGroup) => { ...@@ -1544,6 +1681,10 @@ const handleEdit = async (group: AdminGroup) => {
editForm.image_price_1k = group.image_price_1k editForm.image_price_1k = group.image_price_1k
editForm.image_price_2k = group.image_price_2k editForm.image_price_2k = group.image_price_2k
editForm.image_price_4k = group.image_price_4k editForm.image_price_4k = group.image_price_4k
editForm.sora_image_price_360 = group.sora_image_price_360
editForm.sora_image_price_540 = group.sora_image_price_540
editForm.sora_video_price_per_request = group.sora_video_price_per_request
editForm.sora_video_price_per_request_hd = group.sora_video_price_per_request_hd
editForm.claude_code_only = group.claude_code_only || false editForm.claude_code_only = group.claude_code_only || false
editForm.fallback_group_id = group.fallback_group_id editForm.fallback_group_id = group.fallback_group_id
editForm.model_routing_enabled = group.model_routing_enabled || false editForm.model_routing_enabled = group.model_routing_enabled || false
......
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