ai去特征增加开关

This commit is contained in:
2026-07-10 00:13:09 +08:00
parent 6d6bd2a638
commit 6a3588b35b
4 changed files with 40 additions and 8 deletions
+7
View File
@@ -51,6 +51,7 @@ type CreditSettings struct {
// DeAISettings is the per-tier surcharge (积分) for the 去AI特征 option on the // DeAISettings is the per-tier surcharge (积分) for the 去AI特征 option on the
// 画图台 — charged on top of the model's image price when the toggle is on. // 画图台 — charged on top of the model's image price when the toggle is on.
type DeAISettings struct { type DeAISettings struct {
Enabled bool `json:"enabled"`
Price1K int `json:"price_1k"` Price1K int `json:"price_1k"`
Price2K int `json:"price_2k"` Price2K int `json:"price_2k"`
Price4K int `json:"price_4k"` Price4K int `json:"price_4k"`
@@ -343,6 +344,10 @@ func (s *AppSettingsService) TestProxy(ctx context.Context, proxy string) (map[s
} }
func (s *AppSettingsService) DeAI(ctx context.Context) (*DeAISettings, error) { func (s *AppSettingsService) DeAI(ctx context.Context) (*DeAISettings, error) {
enabledRaw, err := s.settings.GetValue(ctx, "deai.enabled")
if err != nil {
return nil, err
}
p1Raw, err := s.settings.GetValue(ctx, "deai.price_1k") p1Raw, err := s.settings.GetValue(ctx, "deai.price_1k")
if err != nil { if err != nil {
return nil, err return nil, err
@@ -356,6 +361,7 @@ func (s *AppSettingsService) DeAI(ctx context.Context) (*DeAISettings, error) {
return nil, err return nil, err
} }
return &DeAISettings{ return &DeAISettings{
Enabled: parseBoolSetting(enabledRaw, false),
Price1K: clampNonNegative(parseIntSetting(p1Raw, 1)), Price1K: clampNonNegative(parseIntSetting(p1Raw, 1)),
Price2K: clampNonNegative(parseIntSetting(p2Raw, 2)), Price2K: clampNonNegative(parseIntSetting(p2Raw, 2)),
Price4K: clampNonNegative(parseIntSetting(p4Raw, 3)), Price4K: clampNonNegative(parseIntSetting(p4Raw, 3)),
@@ -364,6 +370,7 @@ func (s *AppSettingsService) DeAI(ctx context.Context) (*DeAISettings, error) {
func (s *AppSettingsService) SaveDeAI(ctx context.Context, in DeAISettings) (*DeAISettings, error) { func (s *AppSettingsService) SaveDeAI(ctx context.Context, in DeAISettings) (*DeAISettings, error) {
if err := s.settings.UpsertValues(ctx, map[string]string{ if err := s.settings.UpsertValues(ctx, map[string]string{
"deai.enabled": strconv.FormatBool(in.Enabled),
"deai.price_1k": strconv.Itoa(clampNonNegative(in.Price1K)), "deai.price_1k": strconv.Itoa(clampNonNegative(in.Price1K)),
"deai.price_2k": strconv.Itoa(clampNonNegative(in.Price2K)), "deai.price_2k": strconv.Itoa(clampNonNegative(in.Price2K)),
"deai.price_4k": strconv.Itoa(clampNonNegative(in.Price4K)), "deai.price_4k": strconv.Itoa(clampNonNegative(in.Price4K)),
+18
View File
@@ -418,6 +418,11 @@ func (s *V1Service) prepareImageExecution(ctx context.Context, principal *APIPri
return nil, err return nil, err
} }
} }
// 去AI特征 is gated by a system-settings switch (default off) — drop the
// flag when disabled so no surcharge is charged and no processing runs.
if in.DeAI && !s.deaiEnabled(ctx) {
in.DeAI = false
}
genCtx, cancel := context.WithTimeout(ctx, 8*time.Minute) genCtx, cancel := context.WithTimeout(ctx, 8*time.Minute)
defer cancel() defer cancel()
@@ -2879,6 +2884,19 @@ func guessRatio(w, h int) string {
// firstPricedResolution returns the model's lowest priced image tier (1K/2K/4K // firstPricedResolution returns the model's lowest priced image tier (1K/2K/4K
// order), or "" if none is priced. Used to rescue a request whose resolution // order), or "" if none is priced. Used to rescue a request whose resolution
// the model doesn't support. // the model doesn't support.
// deaiEnabled reports whether the 去AI特征 feature is switched on in system
// settings (default off). When off, an incoming deai flag is ignored entirely.
func (s *V1Service) deaiEnabled(ctx context.Context) bool {
if s.settings == nil {
return false
}
raw, err := s.settings.GetValue(ctx, "deai.enabled")
if err != nil {
return false
}
return parseBoolSetting(raw, false)
}
// deaiSurcharge returns the 去AI特征 surcharge (积分) for an image resolution // deaiSurcharge returns the 去AI特征 surcharge (积分) for an image resolution
// tier, from site settings (defaults: 1K=1, 2K=2, 4K=3). // tier, from site settings (defaults: 1K=1, 2K=2, 4K=3).
func (s *V1Service) deaiSurcharge(ctx context.Context, resolution string) float64 { func (s *V1Service) deaiSurcharge(ctx context.Context, resolution string) float64 {
+10 -4
View File
@@ -121,7 +121,7 @@ const credits = reactive({ checkin_enabled: true, checkin_reward: 3, invite_enab
const credBusy = ref(false); const credSaved = ref(false) const credBusy = ref(false); const credSaved = ref(false)
// ---- deai (去AI特征 附加价格) ---- // ---- deai (去AI特征 附加价格) ----
const deaiCfg = reactive({ price_1k: 1, price_2k: 2, price_4k: 3 }) const deaiCfg = reactive({ enabled: false, price_1k: 1, price_2k: 2, price_4k: 3 })
const deaiBusy = ref(false); const deaiSaved = ref(false) const deaiBusy = ref(false); const deaiSaved = ref(false)
async function loadDeai() { async function loadDeai() {
const r = await api('/settings/deai') const r = await api('/settings/deai')
@@ -130,6 +130,7 @@ async function loadDeai() {
async function saveDeai() { async function saveDeai() {
deaiBusy.value = true; deaiSaved.value = false deaiBusy.value = true; deaiSaved.value = false
const r = await api('/settings/deai', jsonBody('PUT', { const r = await api('/settings/deai', jsonBody('PUT', {
enabled: !!deaiCfg.enabled,
price_1k: Number(deaiCfg.price_1k) || 0, price_1k: Number(deaiCfg.price_1k) || 0,
price_2k: Number(deaiCfg.price_2k) || 0, price_2k: Number(deaiCfg.price_2k) || 0,
price_4k: Number(deaiCfg.price_4k) || 0, price_4k: Number(deaiCfg.price_4k) || 0,
@@ -489,14 +490,18 @@ onMounted(() => { loadSite(); loadReg(); loadSmtp(); loadCredits(); loadAnnounce
<p class="text-xs text-slate-400 mb-4">画图台开启去AI特征,按画质档位在模型价格之上额外扣除的积分仅对图片生成生效</p> <p class="text-xs text-slate-400 mb-4">画图台开启去AI特征,按画质档位在模型价格之上额外扣除的积分仅对图片生成生效</p>
<div class="space-y-3"> <div class="space-y-3">
<label class="row"> <label class="row">
<span><span class="lbl">开启去AI特征</span><span class="hint">关闭后画图台不显示该选项,也不会加价默认关闭</span></span>
<input type="checkbox" v-model="deaiCfg.enabled" class="sw" />
</label>
<label class="row" :class="!deaiCfg.enabled && 'opacity-50'">
<span><span class="lbl">1K 附加价格</span><span class="hint">默认 1 积分</span></span> <span><span class="lbl">1K 附加价格</span><span class="hint">默认 1 积分</span></span>
<input type="number" min="0" v-model.number="deaiCfg.price_1k" class="num" /> <input type="number" min="0" v-model.number="deaiCfg.price_1k" class="num" />
</label> </label>
<label class="row"> <label class="row" :class="!deaiCfg.enabled && 'opacity-50'">
<span><span class="lbl">2K 附加价格</span><span class="hint">默认 2 积分</span></span> <span><span class="lbl">2K 附加价格</span><span class="hint">默认 2 积分</span></span>
<input type="number" min="0" v-model.number="deaiCfg.price_2k" class="num" /> <input type="number" min="0" v-model.number="deaiCfg.price_2k" class="num" />
</label> </label>
<label class="row"> <label class="row" :class="!deaiCfg.enabled && 'opacity-50'">
<span><span class="lbl">4K 附加价格</span><span class="hint">默认 3 积分</span></span> <span><span class="lbl">4K 附加价格</span><span class="hint">默认 3 积分</span></span>
<input type="number" min="0" v-model.number="deaiCfg.price_4k" class="num" /> <input type="number" min="0" v-model.number="deaiCfg.price_4k" class="num" />
</label> </label>
@@ -678,7 +683,8 @@ html.dark .num, html.dark .txt, html.dark .field {
box-shadow: 0 0 0 3px rgb(167 139 250 / 0.15); box-shadow: 0 0 0 3px rgb(167 139 250 / 0.15);
} }
.num:disabled, .txt:disabled, .field:disabled { opacity: 0.45; cursor: not-allowed; } .num:disabled, .txt:disabled, .field:disabled { opacity: 0.45; cursor: not-allowed; }
.num { width: 6rem; padding: 0.4rem 0.55rem; font-size: 0.8rem; text-align: right; } .num { width: 6rem; padding: 0.4rem 0.55rem; font-size: 0.8rem; text-align: right; -moz-appearance: textfield; appearance: textfield; }
.num::-webkit-outer-spin-button, .num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.txt { width: 16rem; max-width: 60%; padding: 0.4rem 0.65rem; font-size: 0.8rem; } .txt { width: 16rem; max-width: 60%; padding: 0.4rem 0.65rem; font-size: 0.8rem; }
.field { width: 100%; padding: 0.55rem 0.75rem; font-size: 0.85rem; } .field { width: 100%; padding: 0.55rem 0.75rem; font-size: 0.85rem; }
+5 -4
View File
@@ -139,7 +139,8 @@ function tierPrice(normalMap, agentMap, key) {
return Number(n) return Number(n)
} }
// 去AI特征 per-tier surcharge (loaded from /deai-pricing; defaults 1/2/3 分). // 去AI特征 per-tier surcharge (loaded from /deai-pricing; defaults 1/2/3 分).
const deaiPricing = ref({ price_1k: 1, price_2k: 2, price_4k: 3 }) const deaiPricing = ref({ enabled: false, price_1k: 1, price_2k: 2, price_4k: 3 })
const deaiEnabled = computed(() => !!deaiPricing.value.enabled)
const deaiSurcharge = computed(() => { const deaiSurcharge = computed(() => {
const key = { '1K': 'price_1k', '2K': 'price_2k', '4K': 'price_4k' }[resolution.value] || 'price_1k' const key = { '1K': 'price_1k', '2K': 'price_2k', '4K': 'price_4k' }[resolution.value] || 'price_1k'
return Number(deaiPricing.value[key] ?? 0) return Number(deaiPricing.value[key] ?? 0)
@@ -155,7 +156,7 @@ const price = computed(() => {
} }
const base = tierPrice(m.prices, m.prices_agent, resolution.value) const base = tierPrice(m.prices, m.prices_agent, resolution.value)
if (base == null) return null if (base == null) return null
return base + (deai.value ? deaiSurcharge.value : 0) return base + (deaiEnabled.value && deai.value ? deaiSurcharge.value : 0)
}) })
const priceLabel = computed(() => price.value == null ? '—' : pointsLabel(price.value)) const priceLabel = computed(() => price.value == null ? '—' : pointsLabel(price.value))
const canAfford = computed(() => price.value == null || credits.value >= price.value) const canAfford = computed(() => price.value == null || credits.value >= price.value)
@@ -402,7 +403,7 @@ async function fireOne() {
ratio: ratio.value, ratio: ratio.value,
resolution: resolution.value, resolution: resolution.value,
duration: mode.value === 'video' ? duration.value : '', duration: mode.value === 'video' ? duration.value : '',
deai: mode.value === 'image' ? deai.value : false, deai: mode.value === 'image' && deaiEnabled.value ? deai.value : false,
status: 'pending', status: 'pending',
url: '', url: '',
error: '', error: '',
@@ -697,7 +698,7 @@ onUnmounted(() => {
</div> </div>
<!-- 去AI特征 (image only): opt-in post-processing with a per-tier surcharge --> <!-- 去AI特征 (image only): opt-in post-processing with a per-tier surcharge -->
<div v-if="mode === 'image'" class="flex items-center justify-between"> <div v-if="mode === 'image' && deaiEnabled" class="flex items-center justify-between">
<label class="text-xs font-medium text-slate-500"> <label class="text-xs font-medium text-slate-500">
去AI特征 去AI特征
<span class="text-slate-400 font-normal">(+{{ deaiSurcharge }} 积分)</span> <span class="text-slate-400 font-normal">(+{{ deaiSurcharge }} 积分)</span>