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

Merge pull request #1455 from touwaeriol/feat/channel-management

feat(channel): add channel management with multi-mode pricing and billing integration
parents b384570d e88b2890
...@@ -181,9 +181,16 @@ ...@@ -181,9 +181,16 @@
</span> </span>
</template> </template>
<template #cell-billing_mode="{ row }">
<span class="inline-flex items-center rounded px-1.5 py-0.5 text-xs font-medium"
:class="getBillingModeBadgeClass(row.billing_mode)">
{{ getBillingModeLabel(row.billing_mode) }}
</span>
</template>
<template #cell-tokens="{ row }"> <template #cell-tokens="{ row }">
<!-- 图片生成请求 --> <!-- 图片生成请求(仅按次计费时显示图片格式) -->
<div v-if="row.image_count > 0" class="flex items-center gap-1.5"> <div v-if="row.image_count > 0 && row.billing_mode === 'image'" class="flex items-center gap-1.5">
<svg <svg
class="h-4 w-4 text-indigo-500" class="h-4 w-4 text-indigo-500"
fill="none" fill="none"
...@@ -457,7 +464,7 @@ ...@@ -457,7 +464,7 @@
<div class="flex items-center justify-between gap-6"> <div class="flex items-center justify-between gap-6">
<span class="text-gray-400">{{ t('usage.rate') }}</span> <span class="text-gray-400">{{ t('usage.rate') }}</span>
<span class="font-semibold text-blue-400" <span class="font-semibold text-blue-400"
>{{ (tooltipData?.rate_multiplier || 1).toFixed(2) }}x</span >{{ formatMultiplier(tooltipData?.rate_multiplier || 1) }}x</span
> >
</div> </div>
<div class="flex items-center justify-between gap-6"> <div class="flex items-center justify-between gap-6">
...@@ -497,6 +504,7 @@ import type { UsageLog, ApiKey, UsageQueryParams, UsageStatsResponse } from '@/t ...@@ -497,6 +504,7 @@ import type { UsageLog, ApiKey, UsageQueryParams, UsageStatsResponse } from '@/t
import type { Column } from '@/components/common/types' import type { Column } from '@/components/common/types'
import { formatDateTime, formatReasoningEffort } from '@/utils/format' import { formatDateTime, formatReasoningEffort } from '@/utils/format'
import { getPersistedPageSize } from '@/composables/usePersistedPageSize' import { getPersistedPageSize } from '@/composables/usePersistedPageSize'
import { formatCacheTokens, formatMultiplier } from '@/utils/formatters'
import { formatTokenPricePerMillion } from '@/utils/usagePricing' import { formatTokenPricePerMillion } from '@/utils/usagePricing'
import { getUsageServiceTierLabel } from '@/utils/usageServiceTier' import { getUsageServiceTierLabel } from '@/utils/usageServiceTier'
import { resolveUsageRequestType } from '@/utils/usageRequestType' import { resolveUsageRequestType } from '@/utils/usageRequestType'
...@@ -525,6 +533,7 @@ const columns = computed<Column[]>(() => [ ...@@ -525,6 +533,7 @@ const columns = computed<Column[]>(() => [
{ key: 'reasoning_effort', label: t('usage.reasoningEffort'), sortable: false }, { key: 'reasoning_effort', label: t('usage.reasoningEffort'), sortable: false },
{ key: 'endpoint', label: t('usage.endpoint'), sortable: false }, { key: 'endpoint', label: t('usage.endpoint'), sortable: false },
{ key: 'stream', label: t('usage.type'), sortable: false }, { key: 'stream', label: t('usage.type'), sortable: false },
{ key: 'billing_mode', label: t('admin.usage.billingMode'), sortable: false },
{ key: 'tokens', label: t('usage.tokens'), sortable: false }, { key: 'tokens', label: t('usage.tokens'), sortable: false },
{ key: 'cost', label: t('usage.cost'), sortable: false }, { key: 'cost', label: t('usage.cost'), sortable: false },
{ key: 'first_token', label: t('usage.firstToken'), sortable: false }, { key: 'first_token', label: t('usage.firstToken'), sortable: false },
...@@ -615,6 +624,18 @@ const getRequestTypeBadgeClass = (log: UsageLog): string => { ...@@ -615,6 +624,18 @@ const getRequestTypeBadgeClass = (log: UsageLog): string => {
return 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200' return 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200'
} }
const getBillingModeLabel = (mode: string | null | undefined): string => {
if (mode === 'per_request') return t('admin.usage.billingModePerRequest')
if (mode === 'image') return t('admin.usage.billingModeImage')
return t('admin.usage.billingModeToken')
}
const getBillingModeBadgeClass = (mode: string | null | undefined): string => {
if (mode === 'per_request') return 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-200'
if (mode === 'image') return 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200'
return 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300'
}
const getRequestTypeExportText = (log: UsageLog): string => { const getRequestTypeExportText = (log: UsageLog): string => {
const requestType = resolveUsageRequestType(log) const requestType = resolveUsageRequestType(log)
if (requestType === 'ws_v2') return 'WS' if (requestType === 'ws_v2') return 'WS'
...@@ -639,15 +660,6 @@ const formatTokens = (value: number): string => { ...@@ -639,15 +660,6 @@ const formatTokens = (value: number): string => {
return value.toLocaleString() return value.toLocaleString()
} }
// Compact format for cache tokens in table cells
const formatCacheTokens = (value: number): string => {
if (value >= 1_000_000) {
return `${(value / 1_000_000).toFixed(1)}M`
} else if (value >= 1_000) {
return `${(value / 1_000).toFixed(1)}K`
}
return value.toLocaleString()
}
const loadUsageLogs = async () => { const loadUsageLogs = async () => {
if (abortController) { if (abortController) {
...@@ -804,6 +816,7 @@ const exportToCSV = async () => { ...@@ -804,6 +816,7 @@ const exportToCSV = async () => {
'Reasoning Effort', 'Reasoning Effort',
'Inbound Endpoint', 'Inbound Endpoint',
'Type', 'Type',
'Billing Mode',
'Input Tokens', 'Input Tokens',
'Output Tokens', 'Output Tokens',
'Cache Read Tokens', 'Cache Read Tokens',
...@@ -822,6 +835,7 @@ const exportToCSV = async () => { ...@@ -822,6 +835,7 @@ const exportToCSV = async () => {
formatReasoningEffort(log.reasoning_effort), formatReasoningEffort(log.reasoning_effort),
log.inbound_endpoint || '', log.inbound_endpoint || '',
getRequestTypeExportText(log), getRequestTypeExportText(log),
getBillingModeLabel(log.billing_mode),
log.input_tokens, log.input_tokens,
log.output_tokens, log.output_tokens,
log.cache_read_tokens, log.cache_read_tokens,
......
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