mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-11-02 08:31:35 +00:00
feat(billing): Codex alpha/search 网页搜索按次计费
- alpha/search 成功请求(上游 2xx)按次计费落 usage_logs(billing_mode=per_request), 上游错误透传/failover 不计费;使用 mandatory 池提交,池满同步兜底不丢扣费 - 单价默认 0.01 USD/次(官方 $10/1000 次),分组新增 web_search_price_per_call 覆盖价(0=免费,负数/留空=默认价),实际扣费叠加分组费率倍数(与 token 口径一致) - 分组字段全链路:ent schema + 迁移 174 + 端口快照 v15 + admin 创建/更新 + DTO - 前端分组表单(openai 平台)新增单次价格配置,实时预览应用当前倍率后的单次价格 - 该端点鉴权维持仅 OpenAI 分组(非 OpenAI 分组 404)
This commit is contained in:
@@ -1315,6 +1315,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Codex 网页搜索按次计费(仅 openai 平台) -->
|
||||
<div
|
||||
v-if="createForm.platform === 'openai'"
|
||||
class="border-t border-gray-200 dark:border-dark-400 pt-4 mt-4"
|
||||
>
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
|
||||
{{ t("admin.groups.webSearchPricing.title") }}
|
||||
</h4>
|
||||
<div>
|
||||
<label class="input-label">{{
|
||||
t("admin.groups.webSearchPricing.pricePerCall")
|
||||
}}</label>
|
||||
<input
|
||||
v-model.number="createForm.web_search_price_per_call"
|
||||
type="number"
|
||||
step="0.001"
|
||||
min="0"
|
||||
placeholder="0.01"
|
||||
class="input"
|
||||
/>
|
||||
<p class="input-hint">
|
||||
{{ t("admin.groups.webSearchPricing.pricePerCallHint") }}
|
||||
</p>
|
||||
<div
|
||||
class="mt-2 rounded-lg bg-gray-50 p-3 text-xs text-gray-600 dark:bg-dark-700 dark:text-gray-300"
|
||||
>
|
||||
{{
|
||||
t("admin.groups.webSearchPricing.finalPricePreview", {
|
||||
price: createWebSearchFinalPricePreview,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OpenAI Messages 调度配置(仅 openai 平台) -->
|
||||
<div
|
||||
v-if="createForm.platform === 'openai'"
|
||||
@@ -2790,6 +2825,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Codex 网页搜索按次计费(仅 openai 平台) -->
|
||||
<div
|
||||
v-if="editForm.platform === 'openai'"
|
||||
class="border-t border-gray-200 dark:border-dark-400 pt-4 mt-4"
|
||||
>
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
|
||||
{{ t("admin.groups.webSearchPricing.title") }}
|
||||
</h4>
|
||||
<div>
|
||||
<label class="input-label">{{
|
||||
t("admin.groups.webSearchPricing.pricePerCall")
|
||||
}}</label>
|
||||
<input
|
||||
v-model.number="editForm.web_search_price_per_call"
|
||||
type="number"
|
||||
step="0.001"
|
||||
min="0"
|
||||
placeholder="0.01"
|
||||
class="input"
|
||||
/>
|
||||
<p class="input-hint">
|
||||
{{ t("admin.groups.webSearchPricing.pricePerCallHint") }}
|
||||
</p>
|
||||
<div
|
||||
class="mt-2 rounded-lg bg-gray-50 p-3 text-xs text-gray-600 dark:bg-dark-700 dark:text-gray-300"
|
||||
>
|
||||
{{
|
||||
t("admin.groups.webSearchPricing.finalPricePreview", {
|
||||
price: editWebSearchFinalPricePreview,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OpenAI Messages 调度配置(仅 openai 平台) -->
|
||||
<div
|
||||
v-if="editForm.platform === 'openai'"
|
||||
@@ -3888,6 +3958,8 @@ const createForm = reactive({
|
||||
video_price_480p: null as number | null,
|
||||
video_price_720p: null as number | null,
|
||||
video_price_1080p: null as number | null,
|
||||
// Codex 网页搜索按次计费(仅 openai 平台使用);null = 使用默认价 0.01
|
||||
web_search_price_per_call: null as number | null,
|
||||
// 高峰时段倍率配置
|
||||
peak_rate_enabled: false,
|
||||
peak_start: "",
|
||||
@@ -4233,6 +4305,8 @@ const editForm = reactive({
|
||||
video_price_480p: null as number | null,
|
||||
video_price_720p: null as number | null,
|
||||
video_price_1080p: null as number | null,
|
||||
// Codex 网页搜索按次计费(仅 openai 平台使用);null = 使用默认价 0.01
|
||||
web_search_price_per_call: null as number | null,
|
||||
// 高峰时段倍率配置
|
||||
peak_rate_enabled: false,
|
||||
peak_start: "",
|
||||
@@ -4390,6 +4464,27 @@ const editVideoFinalPricePreview = computed(() =>
|
||||
buildVideoFinalPricePreview(editForm),
|
||||
);
|
||||
|
||||
// Codex 网页搜索单次默认价(与后端 defaultWebSearchPricePerCall 一致,官方 $10/1000 次)
|
||||
const DEFAULT_WEB_SEARCH_PRICE_PER_CALL = 0.01;
|
||||
|
||||
const buildWebSearchFinalPricePreview = (form: {
|
||||
web_search_price_per_call: number | string | null;
|
||||
rate_multiplier: number | string | null;
|
||||
}) => {
|
||||
const basePrice =
|
||||
parsePreviewPrice(form.web_search_price_per_call) ??
|
||||
DEFAULT_WEB_SEARCH_PRICE_PER_CALL;
|
||||
const multiplier = normalizePreviewNumber(form.rate_multiplier, 1);
|
||||
return formatImagePricePreview(basePrice * multiplier);
|
||||
};
|
||||
|
||||
const createWebSearchFinalPricePreview = computed(() =>
|
||||
buildWebSearchFinalPricePreview(createForm),
|
||||
);
|
||||
const editWebSearchFinalPricePreview = computed(() =>
|
||||
buildWebSearchFinalPricePreview(editForm),
|
||||
);
|
||||
|
||||
const resetDisabledBatchImagePricing = (
|
||||
form: Pick<
|
||||
ImagePricingFormState,
|
||||
@@ -4615,6 +4710,7 @@ const closeCreateModal = () => {
|
||||
createForm.video_price_480p = null;
|
||||
createForm.video_price_720p = null;
|
||||
createForm.video_price_1080p = null;
|
||||
createForm.web_search_price_per_call = null;
|
||||
createForm.peak_rate_enabled = false;
|
||||
createForm.peak_start = "";
|
||||
createForm.peak_end = "";
|
||||
@@ -4726,6 +4822,9 @@ const handleCreateGroup = async () => {
|
||||
requestData.video_price_480p = emptyToNull(requestData.video_price_480p);
|
||||
requestData.video_price_720p = emptyToNull(requestData.video_price_720p);
|
||||
requestData.video_price_1080p = emptyToNull(requestData.video_price_1080p);
|
||||
requestData.web_search_price_per_call = emptyToNull(
|
||||
requestData.web_search_price_per_call,
|
||||
);
|
||||
requestData.peak_rate_enabled = createForm.peak_rate_enabled;
|
||||
requestData.peak_start = createForm.peak_start;
|
||||
requestData.peak_end = createForm.peak_end;
|
||||
@@ -4779,6 +4878,7 @@ const handleEdit = async (group: AdminGroup) => {
|
||||
editForm.video_price_480p = group.video_price_480p;
|
||||
editForm.video_price_720p = group.video_price_720p;
|
||||
editForm.video_price_1080p = group.video_price_1080p;
|
||||
editForm.web_search_price_per_call = group.web_search_price_per_call ?? null;
|
||||
editForm.peak_rate_enabled = group.peak_rate_enabled ?? false;
|
||||
editForm.peak_start = group.peak_start ?? "";
|
||||
editForm.peak_end = group.peak_end ?? "";
|
||||
@@ -4836,6 +4936,7 @@ const closeEditModal = () => {
|
||||
editForm.video_price_480p = null;
|
||||
editForm.video_price_720p = null;
|
||||
editForm.video_price_1080p = null;
|
||||
editForm.web_search_price_per_call = null;
|
||||
resetMessagesDispatchFormState(editForm);
|
||||
resetModelsListState(editModelsListState);
|
||||
};
|
||||
@@ -4914,6 +5015,9 @@ const handleUpdateGroup = async () => {
|
||||
payload.video_price_480p = emptyPriceToClear(payload.video_price_480p);
|
||||
payload.video_price_720p = emptyPriceToClear(payload.video_price_720p);
|
||||
payload.video_price_1080p = emptyPriceToClear(payload.video_price_1080p);
|
||||
payload.web_search_price_per_call = emptyPriceToClear(
|
||||
payload.web_search_price_per_call,
|
||||
);
|
||||
payload.peak_rate_enabled = editForm.peak_rate_enabled;
|
||||
payload.peak_start = editForm.peak_start;
|
||||
payload.peak_end = editForm.peak_end;
|
||||
|
||||
Reference in New Issue
Block a user