From 26be82cc85802157405e5fd3523290c34c3f7453 Mon Sep 17 00:00:00 2001 From: IanShaw <131567472+IanShaw027@users.noreply.github.com> Date: Wed, 19 Aug 2026 05:30:17 -0700 Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=EF=BC=9A=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=B8=A0=E9=81=93=E5=80=8D=E7=8E=87=E5=B9=B6=E7=B2=BE=E7=AE=80?= =?UTF-8?q?=E9=95=BF=E4=B8=8A=E4=B8=8B=E6=96=87=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/admin/channels.ts | 6 ++ .../components/account/CreateAccountModal.vue | 7 +- .../components/account/EditAccountModal.vue | 7 +- .../__tests__/longContextBilling.spec.ts | 29 +++++++ .../components/account/longContextBilling.ts | 11 +++ .../components/admin/channel/IntervalRow.vue | 87 ++++++++++++------- .../admin/channel/PricingEntryCard.vue | 22 ++++- .../PricingEntryCard.timePricing.spec.ts | 15 ++++ .../admin/channel/__tests__/types.spec.ts | 52 +++++++++++ .../src/components/admin/channel/types.ts | 34 ++++++++ .../src/i18n/locales/en/admin/channels.ts | 9 ++ .../src/i18n/locales/en/admin/overview.ts | 2 +- .../src/i18n/locales/zh/admin/channels.ts | 9 ++ .../src/i18n/locales/zh/admin/overview.ts | 2 +- frontend/src/views/admin/ChannelsView.vue | 19 +++- 15 files changed, 276 insertions(+), 35 deletions(-) create mode 100644 frontend/src/components/account/__tests__/longContextBilling.spec.ts create mode 100644 frontend/src/components/account/longContextBilling.ts diff --git a/frontend/src/api/admin/channels.ts b/frontend/src/api/admin/channels.ts index 6556417ced..9ce50bcd0a 100644 --- a/frontend/src/api/admin/channels.ts +++ b/frontend/src/api/admin/channels.ts @@ -17,6 +17,10 @@ export interface PricingInterval { output_price: number | null cache_write_price: number | null cache_read_price: number | null + input_multiplier: number | null + output_multiplier: number | null + cache_write_multiplier: number | null + cache_read_multiplier: number | null per_request_price: number | null sort_order: number } @@ -41,6 +45,8 @@ export interface ChannelModelPricing { output_price: number | null cache_write_price: number | null cache_read_price: number | null + fast_multiplier?: number | null + flex_multiplier?: number | null image_input_price: number | null image_output_price: number | null per_request_price: number | null diff --git a/frontend/src/components/account/CreateAccountModal.vue b/frontend/src/components/account/CreateAccountModal.vue index 55a0831ae1..fdcf33107a 100644 --- a/frontend/src/components/account/CreateAccountModal.vue +++ b/frontend/src/components/account/CreateAccountModal.vue @@ -2957,7 +2957,7 @@
@@ -3740,6 +3740,7 @@ import Toggle from '@/components/common/Toggle.vue' import GrokBaseUrlPresets from '@/components/account/GrokBaseUrlPresets.vue' import CnBaseUrlPresets from '@/components/account/CnBaseUrlPresets.vue' import HeaderOverrideEditor from '@/components/account/HeaderOverrideEditor.vue' +import { allSelectedGroupsEnableLongContextPricing } from '@/components/account/longContextBilling' import { applyAntigravityProjectID, applyHeaderOverride, @@ -3857,6 +3858,10 @@ const emit = defineEmits<{ const appStore = useAppStore() +const hideAccountLongContextBilling = computed(() => { + return allSelectedGroupsEnableLongContextPricing(form.group_ids, props.groups) +}) + // OAuth composables const oauth = useAccountOAuth() // For Anthropic OAuth const openaiOAuth = useOpenAIOAuth() // For OpenAI OAuth diff --git a/frontend/src/components/account/EditAccountModal.vue b/frontend/src/components/account/EditAccountModal.vue index 9369b229aa..6495b61e6a 100644 --- a/frontend/src/components/account/EditAccountModal.vue +++ b/frontend/src/components/account/EditAccountModal.vue @@ -1966,7 +1966,7 @@
@@ -2811,6 +2811,7 @@ import { } from '@/components/account/credentialsBuilder' import { formatDateTime, formatDateTimeLocalInput, parseDateTimeLocalInput } from '@/utils/format' import { createStableObjectKeyResolver } from '@/utils/stableObjectKey' +import { allSelectedGroupsEnableLongContextPricing } from '@/components/account/longContextBilling' import { VERTEX_LOCATION_OPTIONS } from '@/constants/account' import { OPENAI_WS_MODE_CTX_POOL, @@ -2851,6 +2852,10 @@ const authStore = useAuthStore() // 故隐藏代理选择器。 const isSparkShadow = computed(() => props.account?.parent_account_id != null) +const hideAccountLongContextBilling = computed(() => { + return allSelectedGroupsEnableLongContextPricing(form.group_ids, props.groups) +}) + const handleOllamaCloudUsageUpdated = (state: OllamaCloudUsageState) => { if (props.account) emit('updated', { ...props.account, ollama_cloud_usage: state }) } diff --git a/frontend/src/components/account/__tests__/longContextBilling.spec.ts b/frontend/src/components/account/__tests__/longContextBilling.spec.ts new file mode 100644 index 0000000000..70e2c55695 --- /dev/null +++ b/frontend/src/components/account/__tests__/longContextBilling.spec.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from 'vitest' + +import { allSelectedGroupsEnableLongContextPricing } from '../longContextBilling' + +const group = (id: number, enabled: boolean) => ({ + id, + long_context_pricing_enabled: enabled, +}) as any + +describe('allSelectedGroupsEnableLongContextPricing', () => { + it('hides the account override when every selected group enables tier pricing', () => { + expect(allSelectedGroupsEnableLongContextPricing( + [1, 2], + [group(1, true), group(2, true)] + )).toBe(true) + }) + + it('keeps the account override when any selected group disables tier pricing', () => { + expect(allSelectedGroupsEnableLongContextPricing( + [1, 2], + [group(1, true), group(2, false)] + )).toBe(false) + }) + + it('keeps the account override when selection is empty or group data is incomplete', () => { + expect(allSelectedGroupsEnableLongContextPricing([], [group(1, true)])).toBe(false) + expect(allSelectedGroupsEnableLongContextPricing([1, 2], [group(1, true)])).toBe(false) + }) +}) diff --git a/frontend/src/components/account/longContextBilling.ts b/frontend/src/components/account/longContextBilling.ts new file mode 100644 index 0000000000..38c64391b5 --- /dev/null +++ b/frontend/src/components/account/longContextBilling.ts @@ -0,0 +1,11 @@ +import type { AdminGroup } from '@/types' + +export function allSelectedGroupsEnableLongContextPricing( + groupIds: number[], + groups: AdminGroup[] +): boolean { + if (groupIds.length === 0) return false + const selectedGroups = groups.filter(group => groupIds.includes(group.id)) + return selectedGroups.length === groupIds.length && + selectedGroups.every(group => group.long_context_pricing_enabled === true) +} diff --git a/frontend/src/components/admin/channel/IntervalRow.vue b/frontend/src/components/admin/channel/IntervalRow.vue index 7036a82986..0166e510a6 100644 --- a/frontend/src/components/admin/channel/IntervalRow.vue +++ b/frontend/src/components/admin/channel/IntervalRow.vue @@ -3,35 +3,59 @@ :class="isEmpty ? 'border-red-400 bg-red-50 dark:border-red-500 dark:bg-red-950/20' : 'border-gray-200 bg-white dark:border-dark-500 dark:bg-dark-700'"> @@ -79,6 +103,7 @@ const { t } = useI18n() const props = defineProps<{ interval: IntervalFormEntry mode: BillingMode + enableMultipliers?: boolean }>() const emit = defineEmits<{ @@ -93,6 +118,10 @@ const isEmpty = computed(() => { (iv.output_price == null || iv.output_price === '') && (iv.cache_write_price == null || iv.cache_write_price === '') && (iv.cache_read_price == null || iv.cache_read_price === '') && + (iv.input_multiplier == null || iv.input_multiplier === '') && + (iv.output_multiplier == null || iv.output_multiplier === '') && + (iv.cache_write_multiplier == null || iv.cache_write_multiplier === '') && + (iv.cache_read_multiplier == null || iv.cache_read_multiplier === '') && (iv.per_request_price == null || iv.per_request_price === '') }) diff --git a/frontend/src/components/admin/channel/PricingEntryCard.vue b/frontend/src/components/admin/channel/PricingEntryCard.vue index f2a0d6502b..58151455c4 100644 --- a/frontend/src/components/admin/channel/PricingEntryCard.vue +++ b/frontend/src/components/admin/channel/PricingEntryCard.vue @@ -139,7 +139,20 @@
- +
+
+ + +
+
+ + +
+
+ +