feat: add Cache TTL Override per account + bump VERSION to 0.1.83

- Account-level cache TTL override: rewrite Anthropic cache_creation
  token classification (5m↔1h) in streaming/non-streaming responses
- New DB field cache_ttl_overridden in usage_log for billing tracking
- Migration 055_add_cache_ttl_overridden
- Frontend: CacheTTL override toggle in account create/edit modals
- Ent schema regenerated for new usage_log fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John Doe
2026-02-17 11:22:08 +03:00
parent a817cafe3d
commit 3d1f03c286
25 changed files with 533 additions and 19 deletions

View File

@@ -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">5m</option>
<option value="1h">1h</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
}