mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-05-05 13:40:44 +08:00
feat: WebSearch tri-state, account stats pricing fix, quota cache fix, usage tooltip
WebSearch tri-state switch: - Account-level web_search_emulation changed from bool to tri-state string: "default" (follow channel) / "enabled" / "disabled" - shouldEmulateWebSearch checks channel config when account is "default" - SQL migration converts old bool values - Frontend select replaces toggle in Edit/CreateAccountModal Account stats pricing: - resolveAccountStatsCost uses upstream model (post-mapping) for matching - Priority: custom rules → model pricing file (when toggle on) → default - Custom rules always configurable, independent of toggle - Account ID field changed to searchable selector filtered by platform - Description updated to reflect new behavior Quota notification cache fix: - CheckAccountQuotaAfterIncrement fetches real-time account from DB - Reconstructs pre-increment usage for accurate threshold crossing detection - New AccountQuotaReader interface (minimal: GetByID only) Usage tooltip: - Per-request/image billing shows per-request price instead of $0 token price - Token billing continues to show input/output price per million tokens
This commit is contained in:
@@ -1161,7 +1161,11 @@
|
||||
{{ t('admin.accounts.anthropic.webSearchEmulationDesc') }}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle v-model="webSearchEmulationEnabled" />
|
||||
<select v-model="webSearchEmulationMode" class="input w-32 text-sm">
|
||||
<option value="default">{{ t('admin.accounts.anthropic.webSearchDefault') }}</option>
|
||||
<option value="enabled">{{ t('admin.accounts.anthropic.webSearchEnabled') }}</option>
|
||||
<option value="disabled">{{ t('admin.accounts.anthropic.webSearchDisabled') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1844,7 +1848,6 @@ import ConfirmDialog from '@/components/common/ConfirmDialog.vue'
|
||||
import Select from '@/components/common/Select.vue'
|
||||
import Icon from '@/components/icons/Icon.vue'
|
||||
import ProxySelector from '@/components/common/ProxySelector.vue'
|
||||
import Toggle from '@/components/common/Toggle.vue'
|
||||
import GroupSelector from '@/components/common/GroupSelector.vue'
|
||||
import ModelWhitelistSelector from '@/components/account/ModelWhitelistSelector.vue'
|
||||
import QuotaLimitCard from '@/components/account/QuotaLimitCard.vue'
|
||||
@@ -1986,7 +1989,7 @@ const openaiOAuthResponsesWebSocketV2Mode = ref<OpenAIWSMode>(OPENAI_WS_MODE_OFF
|
||||
const openaiAPIKeyResponsesWebSocketV2Mode = ref<OpenAIWSMode>(OPENAI_WS_MODE_OFF)
|
||||
const codexCLIOnlyEnabled = ref(false)
|
||||
const anthropicPassthroughEnabled = ref(false)
|
||||
const webSearchEmulationEnabled = ref(false)
|
||||
const webSearchEmulationMode = ref('default')
|
||||
const webSearchGlobalEnabled = ref(false)
|
||||
|
||||
// Load web search global state once
|
||||
@@ -2171,7 +2174,7 @@ const syncFormFromAccount = (newAccount: Account | null) => {
|
||||
openaiAPIKeyResponsesWebSocketV2Mode.value = OPENAI_WS_MODE_OFF
|
||||
codexCLIOnlyEnabled.value = false
|
||||
anthropicPassthroughEnabled.value = false
|
||||
webSearchEmulationEnabled.value = false
|
||||
webSearchEmulationMode.value = 'default'
|
||||
if (newAccount.platform === 'openai' && (newAccount.type === 'oauth' || newAccount.type === 'apikey')) {
|
||||
openaiPassthroughEnabled.value = extra?.openai_passthrough === true || extra?.openai_oauth_passthrough === true
|
||||
openaiOAuthResponsesWebSocketV2Mode.value = resolveOpenAIWSModeFromExtra(extra, {
|
||||
@@ -2192,7 +2195,15 @@ const syncFormFromAccount = (newAccount: Account | null) => {
|
||||
}
|
||||
if (newAccount.platform === 'anthropic' && newAccount.type === 'apikey') {
|
||||
anthropicPassthroughEnabled.value = extra?.anthropic_passthrough === true
|
||||
webSearchEmulationEnabled.value = extra?.web_search_emulation === true
|
||||
// 三态:string "default"/"enabled"/"disabled",向后兼容旧 bool
|
||||
const wsVal = extra?.web_search_emulation
|
||||
if (wsVal === 'enabled' || wsVal === 'disabled') {
|
||||
webSearchEmulationMode.value = wsVal
|
||||
} else if (wsVal === true) {
|
||||
webSearchEmulationMode.value = 'enabled'
|
||||
} else {
|
||||
webSearchEmulationMode.value = 'default'
|
||||
}
|
||||
}
|
||||
|
||||
// Load quota limit for apikey/bedrock accounts (bedrock quota is also loaded in its own branch above)
|
||||
@@ -3180,10 +3191,10 @@ const handleSubmit = async () => {
|
||||
} else {
|
||||
delete newExtra.anthropic_passthrough
|
||||
}
|
||||
if (webSearchEmulationEnabled.value) {
|
||||
newExtra.web_search_emulation = true
|
||||
} else {
|
||||
if (webSearchEmulationMode.value === 'default') {
|
||||
delete newExtra.web_search_emulation
|
||||
} else {
|
||||
newExtra.web_search_emulation = webSearchEmulationMode.value
|
||||
}
|
||||
updatePayload.extra = newExtra
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user