Merge branch 'alpha' into refactor_relay

# Conflicts:
#	dto/openai_image.go
This commit is contained in:
CaIon
2025-08-15 13:46:34 +08:00
8 changed files with 64 additions and 63 deletions

View File

@@ -9,19 +9,22 @@ import (
)
type ImageRequest struct {
Model string `json:"model"`
Prompt string `json:"prompt" binding:"required"`
N uint `json:"n,omitempty"`
Model string `json:"model"`
Prompt string `json:"prompt" binding:"required"`
N uint `json:"n,omitempty"`
Size string `json:"size,omitempty"`
Quality string `json:"quality,omitempty"`
ResponseFormat string `json:"response_format,omitempty"`
Style string `json:"style,omitempty"`
User string `json:"user,omitempty"`
ExtraFields json.RawMessage `json:"extra_fields,omitempty"`
Background string `json:"background,omitempty"`
Moderation string `json:"moderation,omitempty"`
OutputFormat string `json:"output_format,omitempty"`
Watermark *bool `json:"watermark,omitempty"`
Style json.RawMessage `json:"style,omitempty"`
User json.RawMessage `json:"user,omitempty"`
ExtraFields json.RawMessage `json:"extra_fields,omitempty"`
Background json.RawMessage `json:"background,omitempty"`
Moderation json.RawMessage `json:"moderation,omitempty"`
OutputFormat json.RawMessage `json:"output_format,omitempty"`
OutputCompression json.RawMessage `json:"output_compression,omitempty"`
PartialImages json.RawMessage `json:"partial_images,omitempty"`
// Stream bool `json:"stream,omitempty"`
Watermark *bool `json:"watermark,omitempty"`
}
func (i *ImageRequest) GetTokenCountMeta() *types.TokenCountMeta {

View File

@@ -197,8 +197,10 @@ func TokenAuth() func(c *gin.Context) {
// 或者是否 x-api-key 不为空且存在anthropic-version
// 谁知道有多少不符合规范没写anthropic-version的
// 所以就这样随它去吧(
if strings.Contains(c.Request.URL.Path, "/v1/messages") || (anthropicKey != "" && c.Request.Header.Get("anthropic-version") != "") {
c.Request.Header.Set("Authorization", "Bearer "+anthropicKey)
if strings.Contains(c.Request.URL.Path, "/v1/messages") {
if anthropicKey != "" {
c.Request.Header.Set("Authorization", "Bearer "+anthropicKey)
}
}
// gemini api 从query中获取key
if strings.HasPrefix(c.Request.URL.Path, "/v1beta/models") ||

View File

@@ -35,6 +35,7 @@ const PricingTopSection = ({
models,
filteredModels,
loading,
searchValue,
t
}) => {
const [showFilterModal, setShowFilterModal] = useState(false);
@@ -46,6 +47,7 @@ const PricingTopSection = ({
<Input
prefix={<IconSearch />}
placeholder={t('模糊搜索模型名称')}
value={searchValue}
onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd}
onChange={handleChange}
@@ -78,7 +80,7 @@ const PricingTopSection = ({
</Button>
)}
</div>
), [selectedRowKeys, t, handleCompositionStart, handleCompositionEnd, handleChange, copyText, isMobile]);
), [selectedRowKeys, t, handleCompositionStart, handleCompositionEnd, handleChange, copyText, isMobile, searchValue]);
return (
<>

View File

@@ -128,19 +128,6 @@ const PricingCardView = ({
return record.description || '';
};
// 渲染价格信息
const renderPriceInfo = (record) => {
const priceData = calculateModelPrice({
record,
selectedGroup,
groupRatio,
tokenUnit,
displayPrice,
currency,
});
return formatPriceInfo(priceData, t);
};
// 渲染标签
const renderTags = (record) => {
// 计费类型标签(左边)
@@ -221,6 +208,15 @@ const PricingCardView = ({
const modelKey = getModelKey(model);
const isSelected = selectedRowKeys.includes(modelKey);
const priceData = calculateModelPrice({
record: model,
selectedGroup,
groupRatio,
tokenUnit,
displayPrice,
currency,
});
return (
<Card
key={modelKey || index}
@@ -238,7 +234,7 @@ const PricingCardView = ({
{model.model_name}
</h3>
<div className="flex items-center gap-3 text-xs mt-1">
{renderPriceInfo(model)}
{formatPriceInfo(priceData, t)}
</div>
</div>
</div>
@@ -313,7 +309,7 @@ const PricingCardView = ({
{t('补全')}: {model.quota_type === 0 ? parseFloat(model.completion_ratio.toFixed(3)) : t('无')}
</div>
<div>
{t('分组')}: {priceData.usedGroupRatio}
{t('分组')}: {priceData?.usedGroupRatio ?? '-'}
</div>
</div>
</div>

View File

@@ -98,6 +98,25 @@ export const getPricingTableColumns = ({
displayPrice,
showRatio,
}) => {
const priceDataCache = new WeakMap();
const getPriceData = (record) => {
let cache = priceDataCache.get(record);
if (!cache) {
cache = calculateModelPrice({
record,
selectedGroup,
groupRatio,
tokenUnit,
displayPrice,
currency,
});
priceDataCache.set(record, cache);
}
return cache;
};
const endpointColumn = {
title: t('可用端点类型'),
dataIndex: 'supported_endpoint_types',
@@ -167,21 +186,21 @@ export const getPricingTableColumns = ({
dataIndex: 'model_ratio',
render: (text, record, index) => {
const completionRatio = parseFloat(record.completion_ratio.toFixed(3));
const content = (
const priceData = getPriceData(record);
return (
<div className="space-y-1">
<div className="text-gray-700">
{t('模型倍率')}{record.quota_type === 0 ? text : t('无')}
</div>
<div className="text-gray-700">
{t('补全倍率')}
{record.quota_type === 0 ? completionRatio : t('无')}
{t('补全倍率')}{record.quota_type === 0 ? completionRatio : t('无')}
</div>
<div className="text-gray-700">
{t('分组倍率')}{groupRatio[selectedGroup]}
{t('分组倍率')}{priceData?.usedGroupRatio ?? '-'}
</div>
</div>
);
return content;
},
};
@@ -190,14 +209,7 @@ export const getPricingTableColumns = ({
dataIndex: 'model_price',
fixed: 'right',
render: (text, record, index) => {
const priceData = calculateModelPrice({
record,
selectedGroup,
groupRatio,
tokenUnit,
displayPrice,
currency
});
const priceData = getPriceData(record);
if (priceData.isPerToken) {
return (

View File

@@ -305,6 +305,7 @@ const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit
node: 'item',
key: i,
name,
value: item[name],
onClick: () => onOpenLink(name, item[name], record),
});
}
@@ -326,11 +327,8 @@ const renderOperations = (text, record, onOpenLink, setEditingToken, setShowEdit
if (chatsArray.length === 0) {
showError(t('请联系管理员配置聊天链接'));
} else {
onOpenLink(
'default',
chatsArray[0].name ? (parsed => parsed)(localStorage.getItem('chats')) : '',
record,
);
const first = chatsArray[0];
onOpenLink(first.name, first.value, record);
}
}}
>

View File

@@ -211,9 +211,6 @@ export const useModelPricingData = () => {
};
const handleChange = (value) => {
if (compositionRef.current.isComposition) {
return;
}
const newSearchValue = value ? value : '';
setSearchValue(newSearchValue);
};
@@ -231,9 +228,7 @@ export const useModelPricingData = () => {
const handleGroupClick = (group) => {
setSelectedGroup(group);
// 同时将分组过滤设置为该分组
setFilterGroup(group);
if (group === 'all') {
showInfo(t('已切换至最优倍率视图,每个模型使用其最低倍率分组'));
} else {

View File

@@ -104,34 +104,27 @@ export const usePricingFilterCounts = ({
// 生成不同视图所需的模型集合
const quotaTypeModels = useMemo(
() => allModels.filter((m) => matchesFilters(m, ['quota'])),
[allModels, filterGroup, filterEndpointType, filterVendor, filterTag]
[allModels, filterGroup, filterEndpointType, filterVendor, filterTag, searchValue]
);
const endpointTypeModels = useMemo(
() => allModels.filter((m) => matchesFilters(m, ['endpoint'])),
[allModels, filterGroup, filterQuotaType, filterVendor, filterTag]
[allModels, filterGroup, filterQuotaType, filterVendor, filterTag, searchValue]
);
const vendorModels = useMemo(
() => allModels.filter((m) => matchesFilters(m, ['vendor'])),
[allModels, filterGroup, filterQuotaType, filterEndpointType, filterTag]
[allModels, filterGroup, filterQuotaType, filterEndpointType, filterTag, searchValue]
);
const tagModels = useMemo(
() => allModels.filter((m) => matchesFilters(m, ['tag'])),
[allModels, filterGroup, filterQuotaType, filterEndpointType, filterVendor]
[allModels, filterGroup, filterQuotaType, filterEndpointType, filterVendor, searchValue]
);
const groupCountModels = useMemo(
() => allModels.filter((m) => matchesFilters(m, ['group'])),
[
allModels,
filterQuotaType,
filterEndpointType,
filterVendor,
filterTag,
searchValue,
]
[allModels, filterQuotaType, filterEndpointType, filterVendor, filterTag, searchValue]
);
return {