mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-07 00:05:49 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6074cff68c | ||
|
|
6ab8e7d0a6 | ||
|
|
17f9ef8915 | ||
|
|
15e0a28bd0 |
@@ -52,9 +52,12 @@ pub(crate) async fn provider_account_usage(
|
||||
}
|
||||
statement.push(
|
||||
" group by pa.id, pa.provider_kind, pa.authentication_kind, pa.name, pa.email,
|
||||
pa.plan_type
|
||||
order by max(mr.started_at) desc nulls last, pa.name, pa.id limit ",
|
||||
pa.plan_type",
|
||||
);
|
||||
if query.account_ids.is_none() {
|
||||
statement.push(" having count(mr.id) > 0");
|
||||
}
|
||||
statement.push(" order by max(mr.started_at) desc nulls last, pa.name, pa.id limit ");
|
||||
statement.push_bind(i64::from(query.limit));
|
||||
let rows = statement
|
||||
.build()
|
||||
|
||||
@@ -730,8 +730,18 @@ async fn dashboard_summary_totals_include_history_outside_selected_range() {
|
||||
.await
|
||||
.expect("dashboard summary");
|
||||
assert_eq!(dashboard.requests.request_count, 2);
|
||||
assert_eq!(dashboard.account_usage[0].request_count, 0);
|
||||
assert_eq!(dashboard.account_usage[0].request_buckets.len(), 2);
|
||||
assert!(dashboard.account_usage.is_empty());
|
||||
|
||||
let explicit_account_usage = repository
|
||||
.provider_account_usage(
|
||||
ProviderAccountUsageQuery::for_accounts(range, vec!["acct_observe".to_owned()])
|
||||
.expect("account usage query"),
|
||||
)
|
||||
.await
|
||||
.expect("provider account usage");
|
||||
assert_eq!(explicit_account_usage.len(), 1);
|
||||
assert_eq!(explicit_account_usage[0].request_count, 0);
|
||||
|
||||
assert_eq!(
|
||||
(
|
||||
dashboard.totals.request_count,
|
||||
|
||||
@@ -10,6 +10,7 @@ const props = withDefaults(
|
||||
variant?: ButtonVariant
|
||||
size?: ButtonSize
|
||||
loading?: boolean
|
||||
spinIconOnLoading?: boolean
|
||||
disabled?: boolean
|
||||
active?: boolean
|
||||
iconOnly?: boolean
|
||||
@@ -21,6 +22,7 @@ const props = withDefaults(
|
||||
variant: 'primary',
|
||||
size: 'default',
|
||||
loading: false,
|
||||
spinIconOnLoading: false,
|
||||
disabled: false,
|
||||
active: false,
|
||||
iconOnly: false,
|
||||
@@ -124,7 +126,7 @@ const loadingIconClasses = computed(() => [
|
||||
:aria-busy="loading"
|
||||
>
|
||||
<span
|
||||
v-if="loading"
|
||||
v-if="loading && !spinIconOnLoading"
|
||||
:class="loadingIconClasses"
|
||||
:style="loadingIndicatorStyle"
|
||||
aria-hidden="true"
|
||||
@@ -140,7 +142,7 @@ const loadingIconClasses = computed(() => [
|
||||
<slot />
|
||||
</span>
|
||||
<span
|
||||
v-if="iconOnly && !$slots.icon && !loading"
|
||||
v-if="iconOnly && !$slots.icon && (!loading || spinIconOnLoading)"
|
||||
:class="loadingIconClasses"
|
||||
aria-hidden="true"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AccountRow } from '../constants'
|
||||
import { modelSuccessRateTextClass } from '../constants'
|
||||
|
||||
defineProps<{
|
||||
account: AccountRow
|
||||
@@ -62,7 +63,7 @@ defineProps<{
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="grid grid-cols-[1.5fr_0.7fr_0.8fr_1fr_1fr_1fr_1fr_1fr_1.4fr] gap-3 pb-2 text-[11px] font-heavy text-(--cp-text-secondary) shadow-[inset_0_-1px_0_var(--cp-divider-subtle)]"
|
||||
class="grid grid-cols-[1.5fr_0.7fr_0.8fr_1fr_1fr_1fr_1fr_1fr_1.2fr] gap-3 pb-2 text-[11px] font-heavy text-(--cp-text-secondary) shadow-[inset_0_-1px_0_var(--cp-divider-subtle)]"
|
||||
>
|
||||
<span>模型</span>
|
||||
<span>调用</span>
|
||||
@@ -84,11 +85,13 @@ defineProps<{
|
||||
<div
|
||||
v-for="model in account.usage.models"
|
||||
:key="model.model"
|
||||
class="grid grid-cols-[1.5fr_0.7fr_0.8fr_1fr_1fr_1fr_1fr_1fr_1.4fr] gap-3 pt-3 text-[12px] font-emphasis text-(--cp-text-primary)"
|
||||
class="grid grid-cols-[1.5fr_0.7fr_0.8fr_1fr_1fr_1fr_1fr_1fr_1.2fr] gap-3 pt-3 text-[12px] font-emphasis text-(--cp-text-primary)"
|
||||
>
|
||||
<span class="truncate">{{ model.model }}</span>
|
||||
<span>{{ model.requestCountDisplay }}</span>
|
||||
<span class="text-(--cp-warning-text)">{{ model.successRateDisplay }}</span>
|
||||
<span :class="modelSuccessRateTextClass(model.successRate)">
|
||||
{{ model.successRateDisplay }}
|
||||
</span>
|
||||
<span>{{ model.inputTokensDisplay }}</span>
|
||||
<span>{{ model.outputTokensDisplay }}</span>
|
||||
<span>{{ model.cachedTokensDisplay }}</span>
|
||||
|
||||
@@ -211,6 +211,18 @@ export function quotaWindowPercentTextClass(window: AccountQuotaWindow) {
|
||||
return 'text-(--cp-success-text)'
|
||||
}
|
||||
|
||||
export function modelSuccessRateTextClass(successRate: number | null) {
|
||||
if (successRate === null)
|
||||
return 'text-(--cp-text-muted)'
|
||||
if (successRate >= 99.5)
|
||||
return 'text-(--cp-success-text)'
|
||||
if (successRate >= 98)
|
||||
return 'text-(--cp-normal-text)'
|
||||
if (successRate >= 95)
|
||||
return 'text-(--cp-warning-text)'
|
||||
return 'text-(--cp-danger-text)'
|
||||
}
|
||||
|
||||
function compareQuotaWindows(left: AccountQuotaWindow, right: AccountQuotaWindow) {
|
||||
const groupDifference = groupOrder(left) - groupOrder(right)
|
||||
if (groupDifference !== 0)
|
||||
|
||||
@@ -262,9 +262,9 @@ const {
|
||||
</template>
|
||||
|
||||
<template #groups="{ row }">
|
||||
<AccountGroupMarks
|
||||
:groups="row.groups"
|
||||
/>
|
||||
<div class="flex w-full justify-center">
|
||||
<AccountGroupMarks :groups="row.groups" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #lastUsedAt="{ row }">
|
||||
|
||||
@@ -9,8 +9,8 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid gap-1.5">
|
||||
<div v-if="apiKey.routingScope === 'all'" class="flex items-center gap-2">
|
||||
<div class="grid w-full justify-items-center gap-1.5">
|
||||
<div v-if="apiKey.routingScope === 'all'" class="flex items-center justify-center gap-2">
|
||||
<span class="inline-flex h-6 items-center rounded-lg bg-(--cp-warning-bg) px-2 text-[11px] font-bold text-(--cp-warning-text)">
|
||||
全部账号权限
|
||||
</span>
|
||||
|
||||
@@ -46,6 +46,7 @@ const {
|
||||
class="text-(--cp-normal)"
|
||||
size="md"
|
||||
label="刷新概览"
|
||||
spin-icon-on-loading
|
||||
:loading="loading || refreshing"
|
||||
:disabled="loading || refreshing"
|
||||
@click="refresh"
|
||||
|
||||
@@ -69,6 +69,7 @@ function showDetail(record: Awaited<ReturnType<typeof getOpsErrors>>['items'][nu
|
||||
variant="ghost"
|
||||
size="md"
|
||||
label="刷新错误明细"
|
||||
spin-icon-on-loading
|
||||
:loading="refreshing"
|
||||
:disabled="loading || refreshing"
|
||||
@click="refresh"
|
||||
|
||||
@@ -32,6 +32,7 @@ const search = defineModel<string>('search', { required: true })
|
||||
variant="ghost"
|
||||
size="md"
|
||||
label="刷新使用记录"
|
||||
spin-icon-on-loading
|
||||
:loading="refreshing"
|
||||
:disabled="loading || refreshing"
|
||||
@click="emit('refresh')"
|
||||
|
||||
@@ -1 +1 @@
|
||||
version: "3.2.1"
|
||||
version: "3.2.2"
|
||||
|
||||
Reference in New Issue
Block a user