Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
fad04ca9
Commit
fad04ca9
authored
Feb 18, 2026
by
yangjianbo
Browse files
Merge branch 'main' of
https://github.com/mt21625457/aicodex2api
parents
1cf51b14
074bd0df
Changes
31
Show whitespace changes
Inline
Side-by-side
backend/internal/service/usage_log.go
View file @
fad04ca9
...
...
@@ -46,6 +46,9 @@ type UsageLog struct {
UserAgent
*
string
IPAddress
*
string
// Cache TTL Override 标记(管理员强制替换了缓存 TTL 计费)
CacheTTLOverridden
bool
// 图片生成字段
ImageCount
int
ImageSize
*
string
...
...
backend/migrations/055_add_cache_ttl_overridden.sql
0 → 100644
View file @
fad04ca9
-- Add cache_ttl_overridden flag to usage_logs for tracking cache TTL override per account.
ALTER
TABLE
usage_logs
ADD
COLUMN
IF
NOT
EXISTS
cache_ttl_overridden
BOOLEAN
NOT
NULL
DEFAULT
FALSE
;
frontend/src/components/account/BulkEditAccountModal.vue
View file @
fad04ca9
...
...
@@ -708,6 +708,7 @@ const groupIds = ref<number[]>([])
// All models list (combined Anthropic + OpenAI)
const
allModels
=
[
{
value
:
'
claude-opus-4-6
'
,
label
:
'
Claude Opus 4.6
'
}
,
{
value
:
'
claude-sonnet-4-6
'
,
label
:
'
Claude Sonnet 4.6
'
}
,
{
value
:
'
claude-opus-4-5-20251101
'
,
label
:
'
Claude Opus 4.5
'
}
,
{
value
:
'
claude-sonnet-4-20250514
'
,
label
:
'
Claude Sonnet 4
'
}
,
{
value
:
'
claude-sonnet-4-5-20250929
'
,
label
:
'
Claude Sonnet 4.5
'
}
,
...
...
@@ -754,6 +755,13 @@ const presetMappings = [
color
:
'
bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400
'
}
,
{
label
:
'
Sonnet 4.6
'
,
from
:
'
claude-sonnet-4-6
'
,
to
:
'
claude-sonnet-4-6
'
,
color
:
'
bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400
'
}
,
{
label
:
'
Opus->Sonnet
'
,
from
:
'
claude-opus-4-5-20251101
'
,
...
...
frontend/src/components/account/CreateAccountModal.vue
View file @
fad04ca9
...
...
@@ -1527,6 +1527,46 @@
<
/button
>
<
/div
>
<
/div
>
<!--
Cache
TTL
Override
-->
<
div
class
=
"
rounded-lg border border-gray-200 p-4 dark:border-dark-600
"
>
<
div
class
=
"
flex items-center justify-between
"
>
<
div
>
<
label
class
=
"
input-label mb-0
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.label
'
)
}}
<
/label
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.hint
'
)
}}
<
/p
>
<
/div
>
<
button
type
=
"
button
"
@
click
=
"
cacheTTLOverrideEnabled = !cacheTTLOverrideEnabled
"
:
class
=
"
[
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
cacheTTLOverrideEnabled ? 'bg-primary-600' : 'bg-gray-200 dark:bg-dark-600'
]
"
>
<
span
:
class
=
"
[
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
cacheTTLOverrideEnabled ? 'translate-x-5' : 'translate-x-0'
]
"
/>
<
/button
>
<
/div
>
<
div
v
-
if
=
"
cacheTTLOverrideEnabled
"
class
=
"
mt-3
"
>
<
label
class
=
"
input-label text-xs
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.target
'
)
}}
<
/label
>
<
select
v
-
model
=
"
cacheTTLOverrideTarget
"
class
=
"
mt-1 block w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 dark:border-dark-500 dark:bg-dark-700 dark:text-white
"
>
<
option
value
=
"
5m
"
>
5
m
<
/option
>
<
option
value
=
"
1h
"
>
1
h
<
/option
>
<
/select
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.targetHint
'
)
}}
<
/p
>
<
/div
>
<
/div
>
<
/div
>
<
div
>
...
...
@@ -2146,6 +2186,8 @@ const maxSessions = ref<number | null>(null)
const
sessionIdleTimeout
=
ref
<
number
|
null
>
(
null
)
const
tlsFingerprintEnabled
=
ref
(
false
)
const
sessionIdMaskingEnabled
=
ref
(
false
)
const
cacheTTLOverrideEnabled
=
ref
(
false
)
const
cacheTTLOverrideTarget
=
ref
<
string
>
(
'
5m
'
)
// Gemini tier selection (used as fallback when auto-detection is unavailable/fails)
const
geminiTierGoogleOne
=
ref
<
'
google_one_free
'
|
'
google_ai_pro
'
|
'
google_ai_ultra
'
>
(
'
google_one_free
'
)
...
...
@@ -2597,6 +2639,8 @@ const resetForm = () => {
sessionIdleTimeout
.
value
=
null
tlsFingerprintEnabled
.
value
=
false
sessionIdMaskingEnabled
.
value
=
false
cacheTTLOverrideEnabled
.
value
=
false
cacheTTLOverrideTarget
.
value
=
'
5m
'
antigravityAccountType
.
value
=
'
oauth
'
upstreamBaseUrl
.
value
=
''
upstreamApiKey
.
value
=
''
...
...
@@ -3174,6 +3218,12 @@ const handleAnthropicExchange = async (authCode: string) => {
extra
.
session_id_masking_enabled
=
true
}
// Add cache TTL override settings
if
(
cacheTTLOverrideEnabled
.
value
)
{
extra
.
cache_ttl_override_enabled
=
true
extra
.
cache_ttl_override_target
=
cacheTTLOverrideTarget
.
value
}
const
credentials
=
{
...
tokenInfo
,
...(
interceptWarmupRequests
.
value
?
{
intercept_warmup_requests
:
true
}
:
{
}
)
...
...
@@ -3267,6 +3317,12 @@ const handleCookieAuth = async (sessionKey: string) => {
extra
.
session_id_masking_enabled
=
true
}
// Add cache TTL override settings
if
(
cacheTTLOverrideEnabled
.
value
)
{
extra
.
cache_ttl_override_enabled
=
true
extra
.
cache_ttl_override_target
=
cacheTTLOverrideTarget
.
value
}
const
accountName
=
keys
.
length
>
1
?
`${form.name
}
#${i + 1
}
`
:
form
.
name
// Merge interceptWarmupRequests into credentials
...
...
frontend/src/components/account/EditAccountModal.vue
View file @
fad04ca9
...
...
@@ -904,6 +904,46 @@
<
/button
>
<
/div
>
<
/div
>
<!--
Cache
TTL
Override
-->
<
div
class
=
"
rounded-lg border border-gray-200 p-4 dark:border-dark-600
"
>
<
div
class
=
"
flex items-center justify-between
"
>
<
div
>
<
label
class
=
"
input-label mb-0
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.label
'
)
}}
<
/label
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.hint
'
)
}}
<
/p
>
<
/div
>
<
button
type
=
"
button
"
@
click
=
"
cacheTTLOverrideEnabled = !cacheTTLOverrideEnabled
"
:
class
=
"
[
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
cacheTTLOverrideEnabled ? 'bg-primary-600' : 'bg-gray-200 dark:bg-dark-600'
]
"
>
<
span
:
class
=
"
[
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
cacheTTLOverrideEnabled ? 'translate-x-5' : 'translate-x-0'
]
"
/>
<
/button
>
<
/div
>
<
div
v
-
if
=
"
cacheTTLOverrideEnabled
"
class
=
"
mt-3
"
>
<
label
class
=
"
input-label text-xs
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.target
'
)
}}
<
/label
>
<
select
v
-
model
=
"
cacheTTLOverrideTarget
"
class
=
"
mt-1 block w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 dark:border-dark-500 dark:bg-dark-700 dark:text-white
"
>
<
option
value
=
"
5m
"
>
5
m
<
/option
>
<
option
value
=
"
1h
"
>
1
h
<
/option
>
<
/select
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.quotaControl.cacheTTLOverride.targetHint
'
)
}}
<
/p
>
<
/div
>
<
/div
>
<
/div
>
<
div
class
=
"
border-t border-gray-200 pt-4 dark:border-dark-600
"
>
...
...
@@ -1102,6 +1142,8 @@ const maxSessions = ref<number | null>(null)
const
sessionIdleTimeout
=
ref
<
number
|
null
>
(
null
)
const
tlsFingerprintEnabled
=
ref
(
false
)
const
sessionIdMaskingEnabled
=
ref
(
false
)
const
cacheTTLOverrideEnabled
=
ref
(
false
)
const
cacheTTLOverrideTarget
=
ref
<
string
>
(
'
5m
'
)
// Computed: current preset mappings based on platform
const
presetMappings
=
computed
(()
=>
getPresetMappingsByPlatform
(
props
.
account
?.
platform
||
'
anthropic
'
))
...
...
@@ -1489,6 +1531,8 @@ function loadQuotaControlSettings(account: Account) {
sessionIdleTimeout
.
value
=
null
tlsFingerprintEnabled
.
value
=
false
sessionIdMaskingEnabled
.
value
=
false
cacheTTLOverrideEnabled
.
value
=
false
cacheTTLOverrideTarget
.
value
=
'
5m
'
// Only applies to Anthropic OAuth/SetupToken accounts
if
(
account
.
platform
!==
'
anthropic
'
||
(
account
.
type
!==
'
oauth
'
&&
account
.
type
!==
'
setup-token
'
))
{
...
...
@@ -1517,6 +1561,12 @@ function loadQuotaControlSettings(account: Account) {
if
(
account
.
session_id_masking_enabled
===
true
)
{
sessionIdMaskingEnabled
.
value
=
true
}
// Load cache TTL override setting
if
(
account
.
cache_ttl_override_enabled
===
true
)
{
cacheTTLOverrideEnabled
.
value
=
true
cacheTTLOverrideTarget
.
value
=
account
.
cache_ttl_override_target
||
'
5m
'
}
}
function
formatTempUnschedKeywords
(
value
:
unknown
)
{
...
...
@@ -1723,6 +1773,15 @@ const handleSubmit = async () => {
delete
newExtra
.
session_id_masking_enabled
}
// Cache TTL override setting
if
(
cacheTTLOverrideEnabled
.
value
)
{
newExtra
.
cache_ttl_override_enabled
=
true
newExtra
.
cache_ttl_override_target
=
cacheTTLOverrideTarget
.
value
}
else
{
delete
newExtra
.
cache_ttl_override_enabled
delete
newExtra
.
cache_ttl_override_target
}
updatePayload
.
extra
=
newExtra
}
...
...
frontend/src/components/admin/usage/UsageTable.vue
View file @
fad04ca9
...
...
@@ -71,6 +71,7 @@
<svg
class=
"h-3.5 w-3.5 text-amber-500"
fill=
"none"
stroke=
"currentColor"
viewBox=
"0 0 24 24"
><path
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"2"
d=
"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/></svg>
<span
class=
"font-medium text-amber-600 dark:text-amber-400"
>
{{
formatCacheTokens
(
row
.
cache_creation_tokens
)
}}
</span>
<span
v-if=
"row.cache_creation_1h_tokens > 0"
class=
"inline-flex items-center rounded px-1 py-px text-[10px] font-medium leading-tight bg-orange-100 text-orange-600 ring-1 ring-inset ring-orange-200 dark:bg-orange-500/20 dark:text-orange-400 dark:ring-orange-500/30"
>
1h
</span>
<span
v-if=
"row.cache_ttl_overridden"
:title=
"t('usage.cacheTtlOverriddenHint')"
class=
"inline-flex items-center rounded px-1 py-px text-[10px] font-medium leading-tight bg-rose-100 text-rose-600 ring-1 ring-inset ring-rose-200 dark:bg-rose-500/20 dark:text-rose-400 dark:ring-rose-500/30 cursor-help"
>
R
</span>
</div>
</div>
</div>
...
...
@@ -182,6 +183,13 @@
<span
class=
"font-medium text-white"
>
{{ tokenTooltipData.cache_creation_tokens.toLocaleString() }}
</span>
</div>
</div>
<div
v-if=
"tokenTooltipData && tokenTooltipData.cache_ttl_overridden"
class=
"flex items-center justify-between gap-4"
>
<span
class=
"text-gray-400 flex items-center gap-1.5"
>
{{ t('usage.cacheTtlOverriddenLabel') }}
<span
class=
"inline-flex items-center rounded px-1 py-px text-[10px] font-medium leading-tight bg-rose-500/20 text-rose-400 ring-1 ring-inset ring-rose-500/30"
>
R-{{ tokenTooltipData.cache_creation_1h_tokens > 0 ? '5m' : '1H' }}
</span>
</span>
<span
class=
"font-medium text-rose-400"
>
{{ tokenTooltipData.cache_creation_1h_tokens > 0 ? t('usage.cacheTtlOverridden1h') : t('usage.cacheTtlOverridden5m') }}
</span>
</div>
<div
v-if=
"tokenTooltipData && tokenTooltipData.cache_read_tokens > 0"
class=
"flex items-center justify-between gap-4"
>
<span
class=
"text-gray-400"
>
{{ t('admin.usage.cacheReadTokens') }}
</span>
<span
class=
"font-medium text-white"
>
{{ tokenTooltipData.cache_read_tokens.toLocaleString() }}
</span>
...
...
frontend/src/composables/useModelWhitelist.ts
View file @
fad04ca9
...
...
@@ -39,6 +39,7 @@ export const claudeModels = [
'
claude-sonnet-4-5-20250929
'
,
'
claude-haiku-4-5-20251001
'
,
'
claude-opus-4-5-20251101
'
,
'
claude-opus-4-6
'
,
'
claude-sonnet-4-6
'
,
'
claude-2.1
'
,
'
claude-2.0
'
,
'
claude-instant-1.2
'
]
...
...
@@ -233,6 +234,7 @@ export const allModels = allModelsList.map(m => ({ value: m, label: m }))
const
anthropicPresetMappings
=
[
{
label
:
'
Sonnet 4
'
,
from
:
'
claude-sonnet-4-20250514
'
,
to
:
'
claude-sonnet-4-20250514
'
,
color
:
'
bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400
'
},
{
label
:
'
Sonnet 4.5
'
,
from
:
'
claude-sonnet-4-5-20250929
'
,
to
:
'
claude-sonnet-4-5-20250929
'
,
color
:
'
bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400
'
},
{
label
:
'
Sonnet 4.6
'
,
from
:
'
claude-sonnet-4-6
'
,
to
:
'
claude-sonnet-4-6
'
,
color
:
'
bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400
'
},
{
label
:
'
Opus 4.5
'
,
from
:
'
claude-opus-4-5-20251101
'
,
to
:
'
claude-opus-4-5-20251101
'
,
color
:
'
bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400
'
},
{
label
:
'
Opus 4.6
'
,
from
:
'
claude-opus-4-6
'
,
to
:
'
claude-opus-4-6
'
,
color
:
'
bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400
'
},
{
label
:
'
Haiku 3.5
'
,
from
:
'
claude-3-5-haiku-20241022
'
,
to
:
'
claude-3-5-haiku-20241022
'
,
color
:
'
bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400
'
},
...
...
frontend/src/i18n/locales/en.ts
View file @
fad04ca9
...
...
@@ -576,6 +576,10 @@ export default {
description
:
'
View and analyze your API usage history
'
,
costDetails
:
'
Cost Breakdown
'
,
tokenDetails
:
'
Token Breakdown
'
,
cacheTtlOverriddenHint
:
'
Cache TTL Override enabled
'
,
cacheTtlOverriddenLabel
:
'
TTL Override
'
,
cacheTtlOverridden5m
:
'
Billed as 5m
'
,
cacheTtlOverridden1h
:
'
Billed as 1h
'
,
totalRequests
:
'
Total Requests
'
,
totalTokens
:
'
Total Tokens
'
,
totalCost
:
'
Total Cost
'
,
...
...
@@ -1595,6 +1599,12 @@ export default {
sessionIdMasking
:
{
label
:
'
Session ID Masking
'
,
hint
:
'
When enabled, fixes the session ID in metadata.user_id for 15 minutes, making upstream think requests come from the same session
'
},
cacheTTLOverride
:
{
label
:
'
Cache TTL Override
'
,
hint
:
'
Force all cache creation tokens to be billed as the selected TTL tier (5m or 1h)
'
,
target
:
'
Target TTL
'
,
targetHint
:
'
Select the TTL tier for billing
'
}
},
expired
:
'
Expired
'
,
...
...
frontend/src/i18n/locales/zh.ts
View file @
fad04ca9
...
...
@@ -582,6 +582,10 @@ export default {
description
:
'
查看和分析您的 API 使用历史
'
,
costDetails
:
'
成本明细
'
,
tokenDetails
:
'
Token 明细
'
,
cacheTtlOverriddenHint
:
'
缓存 TTL Override 已启用
'
,
cacheTtlOverriddenLabel
:
'
TTL 替换
'
,
cacheTtlOverridden5m
:
'
按 5m 计费
'
,
cacheTtlOverridden1h
:
'
按 1h 计费
'
,
totalRequests
:
'
总请求数
'
,
totalTokens
:
'
总 Token
'
,
totalCost
:
'
总消费
'
,
...
...
@@ -1741,6 +1745,12 @@ export default {
sessionIdMasking
:
{
label
:
'
会话 ID 伪装
'
,
hint
:
'
启用后将在 15 分钟内固定 metadata.user_id 中的 session ID,使上游认为请求来自同一会话
'
},
cacheTTLOverride
:
{
label
:
'
缓存 TTL 强制替换
'
,
hint
:
'
将所有缓存创建 token 强制按指定的 TTL 类型(5分钟或1小时)计费
'
,
target
:
'
目标 TTL
'
,
targetHint
:
'
选择计费使用的 TTL 类型
'
}
},
expired
:
'
已过期
'
,
...
...
frontend/src/types/index.ts
View file @
fad04ca9
...
...
@@ -614,6 +614,10 @@ export interface Account {
// 启用后将在15分钟内固定 metadata.user_id 中的 session ID
session_id_masking_enabled
?:
boolean
|
null
// 缓存 TTL 强制替换(仅 Anthropic OAuth/SetupToken 账号有效)
cache_ttl_override_enabled
?:
boolean
|
null
cache_ttl_override_target
?:
string
|
null
// 运行时状态(仅当启用对应限制时返回)
current_window_cost
?:
number
|
null
// 当前窗口费用
active_sessions
?:
number
|
null
// 当前活跃会话数
...
...
@@ -827,6 +831,9 @@ export interface UsageLog {
// User-Agent
user_agent
:
string
|
null
// Cache TTL Override
cache_ttl_overridden
:
boolean
created_at
:
string
user
?:
User
...
...
frontend/src/views/user/UsageView.vue
View file @
fad04ca9
...
...
@@ -234,6 +234,7 @@
formatCacheTokens
(
row
.
cache_creation_tokens
)
}}
</span>
<span
v-if=
"row.cache_creation_1h_tokens > 0"
class=
"inline-flex items-center rounded px-1 py-px text-[10px] font-medium leading-tight bg-orange-100 text-orange-600 ring-1 ring-inset ring-orange-200 dark:bg-orange-500/20 dark:text-orange-400 dark:ring-orange-500/30"
>
1h
</span>
<span
v-if=
"row.cache_ttl_overridden"
:title=
"t('usage.cacheTtlOverriddenHint')"
class=
"inline-flex items-center rounded px-1 py-px text-[10px] font-medium leading-tight bg-rose-100 text-rose-600 ring-1 ring-inset ring-rose-200 dark:bg-rose-500/20 dark:text-rose-400 dark:ring-rose-500/30 cursor-help"
>
R
</span>
</div>
</div>
</div>
...
...
@@ -375,6 +376,13 @@
<span
class=
"font-medium text-white"
>
{{ tokenTooltipData.cache_creation_tokens.toLocaleString() }}
</span>
</div>
</div>
<div
v-if=
"tokenTooltipData && tokenTooltipData.cache_ttl_overridden"
class=
"flex items-center justify-between gap-4"
>
<span
class=
"text-gray-400 flex items-center gap-1.5"
>
{{ t('usage.cacheTtlOverriddenLabel') }}
<span
class=
"inline-flex items-center rounded px-1 py-px text-[10px] font-medium leading-tight bg-rose-500/20 text-rose-400 ring-1 ring-inset ring-rose-500/30"
>
R-{{ tokenTooltipData.cache_creation_1h_tokens > 0 ? '5m' : '1H' }}
</span>
</span>
<span
class=
"font-medium text-rose-400"
>
{{ tokenTooltipData.cache_creation_1h_tokens > 0 ? t('usage.cacheTtlOverridden1h') : t('usage.cacheTtlOverridden5m') }}
</span>
</div>
<div
v-if=
"tokenTooltipData && tokenTooltipData.cache_read_tokens > 0"
class=
"flex items-center justify-between gap-4"
>
<span
class=
"text-gray-400"
>
{{ t('admin.usage.cacheReadTokens') }}
</span>
<span
class=
"font-medium text-white"
>
{{ tokenTooltipData.cache_read_tokens.toLocaleString() }}
</span>
...
...
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment