Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
50288e6b
"backend/internal/git@web.lueluesay.top:chenxi/sub2api.git" did not exist on "dca85c86223e04ffd36472104cab99e6ac8c6b60"
Commit
50288e6b
authored
Mar 30, 2026
by
shaw
Browse files
fix: 修复模型定价文件更新url
parent
ab3e44e4
Changes
3
Hide whitespace changes
Inline
Side-by-side
backend/internal/config/config.go
View file @
50288e6b
...
@@ -1281,8 +1281,8 @@ func setDefaults() {
...
@@ -1281,8 +1281,8 @@ func setDefaults() {
viper
.
SetDefault
(
"rate_limit.oauth_401_cooldown_minutes"
,
10
)
viper
.
SetDefault
(
"rate_limit.oauth_401_cooldown_minutes"
,
10
)
// Pricing - 从 model-price-repo 同步模型定价和上下文窗口数据(固定到 commit,避免分支漂移)
// Pricing - 从 model-price-repo 同步模型定价和上下文窗口数据(固定到 commit,避免分支漂移)
viper
.
SetDefault
(
"pricing.remote_url"
,
"https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/
c7947e9871687e664180bc971d4837f1fc2784a9
/model_prices_and_context_window.json"
)
viper
.
SetDefault
(
"pricing.remote_url"
,
"https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/
main
/model_prices_and_context_window.json"
)
viper
.
SetDefault
(
"pricing.hash_url"
,
"https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/
c7947e9871687e664180bc971d4837f1fc2784a9
/model_prices_and_context_window.sha256"
)
viper
.
SetDefault
(
"pricing.hash_url"
,
"https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/
main
/model_prices_and_context_window.sha256"
)
viper
.
SetDefault
(
"pricing.data_dir"
,
"./data"
)
viper
.
SetDefault
(
"pricing.data_dir"
,
"./data"
)
viper
.
SetDefault
(
"pricing.fallback_file"
,
"./resources/model-pricing/model_prices_and_context_window.json"
)
viper
.
SetDefault
(
"pricing.fallback_file"
,
"./resources/model-pricing/model_prices_and_context_window.json"
)
viper
.
SetDefault
(
"pricing.update_interval_hours"
,
24
)
viper
.
SetDefault
(
"pricing.update_interval_hours"
,
24
)
...
...
backend/internal/service/pricing_service.go
View file @
50288e6b
...
@@ -189,10 +189,38 @@ func (s *PricingService) checkAndUpdatePricing() error {
...
@@ -189,10 +189,38 @@ func (s *PricingService) checkAndUpdatePricing() error {
return
s
.
downloadPricingData
()
return
s
.
downloadPricingData
()
}
}
// 检查文件是否过期
// 先加载本地文件(确保服务可用),再检查是否需要更新
if
err
:=
s
.
loadPricingData
(
pricingFile
);
err
!=
nil
{
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Failed to load local file, downloading: %v"
,
err
)
return
s
.
downloadPricingData
()
}
// 如果配置了哈希URL,通过远程哈希检查是否有更新
if
s
.
cfg
.
Pricing
.
HashURL
!=
""
{
remoteHash
,
err
:=
s
.
fetchRemoteHash
()
if
err
!=
nil
{
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Failed to fetch remote hash on startup: %v"
,
err
)
return
nil
// 已加载本地文件,哈希获取失败不影响启动
}
s
.
mu
.
RLock
()
localHash
:=
s
.
localHash
s
.
mu
.
RUnlock
()
if
localHash
==
""
||
remoteHash
!=
localHash
{
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Remote hash differs on startup (local=%s remote=%s), downloading..."
,
localHash
[
:
min
(
8
,
len
(
localHash
))],
remoteHash
[
:
min
(
8
,
len
(
remoteHash
))])
if
err
:=
s
.
downloadPricingData
();
err
!=
nil
{
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Download failed, using existing file: %v"
,
err
)
}
}
return
nil
}
// 没有哈希URL时,基于文件年龄检查
info
,
err
:=
os
.
Stat
(
pricingFile
)
info
,
err
:=
os
.
Stat
(
pricingFile
)
if
err
!=
nil
{
if
err
!=
nil
{
return
s
.
downloadPricingData
()
return
nil
// 已加载本地文件
}
}
fileAge
:=
time
.
Since
(
info
.
ModTime
())
fileAge
:=
time
.
Since
(
info
.
ModTime
())
...
@@ -205,21 +233,11 @@ func (s *PricingService) checkAndUpdatePricing() error {
...
@@ -205,21 +233,11 @@ func (s *PricingService) checkAndUpdatePricing() error {
}
}
}
}
// 加载本地文件
return
nil
return
s
.
loadPricingData
(
pricingFile
)
}
}
// syncWithRemote 与远程同步(基于哈希校验)
// syncWithRemote 与远程同步(基于哈希校验)
func
(
s
*
PricingService
)
syncWithRemote
()
error
{
func
(
s
*
PricingService
)
syncWithRemote
()
error
{
pricingFile
:=
s
.
getPricingFilePath
()
// 计算本地文件哈希
localHash
,
err
:=
s
.
computeFileHash
(
pricingFile
)
if
err
!=
nil
{
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Failed to compute local hash: %v"
,
err
)
return
s
.
downloadPricingData
()
}
// 如果配置了哈希URL,从远程获取哈希进行比对
// 如果配置了哈希URL,从远程获取哈希进行比对
if
s
.
cfg
.
Pricing
.
HashURL
!=
""
{
if
s
.
cfg
.
Pricing
.
HashURL
!=
""
{
remoteHash
,
err
:=
s
.
fetchRemoteHash
()
remoteHash
,
err
:=
s
.
fetchRemoteHash
()
...
@@ -228,8 +246,13 @@ func (s *PricingService) syncWithRemote() error {
...
@@ -228,8 +246,13 @@ func (s *PricingService) syncWithRemote() error {
return
nil
// 哈希获取失败不影响正常使用
return
nil
// 哈希获取失败不影响正常使用
}
}
if
remoteHash
!=
localHash
{
s
.
mu
.
RLock
()
logger
.
LegacyPrintf
(
"service.pricing"
,
"%s"
,
"[Pricing] Remote hash differs, downloading new version..."
)
localHash
:=
s
.
localHash
s
.
mu
.
RUnlock
()
if
localHash
==
""
||
remoteHash
!=
localHash
{
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Remote hash differs (local=%s remote=%s), downloading new version..."
,
localHash
[
:
min
(
8
,
len
(
localHash
))],
remoteHash
[
:
min
(
8
,
len
(
remoteHash
))])
return
s
.
downloadPricingData
()
return
s
.
downloadPricingData
()
}
}
logger
.
LegacyPrintf
(
"service.pricing"
,
"%s"
,
"[Pricing] Hash check passed, no update needed"
)
logger
.
LegacyPrintf
(
"service.pricing"
,
"%s"
,
"[Pricing] Hash check passed, no update needed"
)
...
@@ -237,6 +260,7 @@ func (s *PricingService) syncWithRemote() error {
...
@@ -237,6 +260,7 @@ func (s *PricingService) syncWithRemote() error {
}
}
// 没有哈希URL时,基于时间检查
// 没有哈希URL时,基于时间检查
pricingFile
:=
s
.
getPricingFilePath
()
info
,
err
:=
os
.
Stat
(
pricingFile
)
info
,
err
:=
os
.
Stat
(
pricingFile
)
if
err
!=
nil
{
if
err
!=
nil
{
return
s
.
downloadPricingData
()
return
s
.
downloadPricingData
()
...
@@ -264,11 +288,12 @@ func (s *PricingService) downloadPricingData() error {
...
@@ -264,11 +288,12 @@ func (s *PricingService) downloadPricingData() error {
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
30
*
time
.
Second
)
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
30
*
time
.
Second
)
defer
cancel
()
defer
cancel
()
var
expectedHash
string
// 获取远程哈希(用于同步锚点,不作为完整性校验)
var
remoteHash
string
if
strings
.
TrimSpace
(
s
.
cfg
.
Pricing
.
HashURL
)
!=
""
{
if
strings
.
TrimSpace
(
s
.
cfg
.
Pricing
.
HashURL
)
!=
""
{
expec
te
d
Hash
,
err
=
s
.
fetchRemoteHash
()
remo
teHash
,
err
=
s
.
fetchRemoteHash
()
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"
fetch remote hash: %
w
"
,
err
)
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Failed to
fetch remote hash
(continuing)
: %
v
"
,
err
)
}
}
}
}
...
@@ -277,11 +302,13 @@ func (s *PricingService) downloadPricingData() error {
...
@@ -277,11 +302,13 @@ func (s *PricingService) downloadPricingData() error {
return
fmt
.
Errorf
(
"download failed: %w"
,
err
)
return
fmt
.
Errorf
(
"download failed: %w"
,
err
)
}
}
if
expectedHash
!=
""
{
// 哈希校验:不匹配时仅告警,不阻止更新
actualHash
:=
sha256
.
Sum256
(
body
)
// 远程哈希文件可能与数据文件不同步(如维护者更新了数据但未更新哈希文件)
if
!
strings
.
EqualFold
(
expectedHash
,
hex
.
EncodeToString
(
actualHash
[
:
]))
{
dataHash
:=
sha256
.
Sum256
(
body
)
return
fmt
.
Errorf
(
"pricing hash mismatch"
)
dataHashStr
:=
hex
.
EncodeToString
(
dataHash
[
:
])
}
if
remoteHash
!=
""
&&
!
strings
.
EqualFold
(
remoteHash
,
dataHashStr
)
{
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Hash mismatch warning: remote=%s data=%s (hash file may be out of sync)"
,
remoteHash
[
:
min
(
8
,
len
(
remoteHash
))],
dataHashStr
[
:
8
])
}
}
// 解析JSON数据(使用灵活的解析方式)
// 解析JSON数据(使用灵活的解析方式)
...
@@ -296,11 +323,14 @@ func (s *PricingService) downloadPricingData() error {
...
@@ -296,11 +323,14 @@ func (s *PricingService) downloadPricingData() error {
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Failed to save file: %v"
,
err
)
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Failed to save file: %v"
,
err
)
}
}
// 保存哈希
// 使用远程哈希作为同步锚点,防止重复下载
hash
:=
sha256
.
Sum256
(
body
)
// 当远程哈希不可用时,回退到数据本身的哈希
hashStr
:=
hex
.
EncodeToString
(
hash
[
:
])
syncHash
:=
dataHashStr
if
remoteHash
!=
""
{
syncHash
=
remoteHash
}
hashFile
:=
s
.
getHashFilePath
()
hashFile
:=
s
.
getHashFilePath
()
if
err
:=
os
.
WriteFile
(
hashFile
,
[]
byte
(
hashStr
+
"
\n
"
),
0644
);
err
!=
nil
{
if
err
:=
os
.
WriteFile
(
hashFile
,
[]
byte
(
syncHash
+
"
\n
"
),
0644
);
err
!=
nil
{
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Failed to save hash: %v"
,
err
)
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Failed to save hash: %v"
,
err
)
}
}
...
@@ -308,7 +338,7 @@ func (s *PricingService) downloadPricingData() error {
...
@@ -308,7 +338,7 @@ func (s *PricingService) downloadPricingData() error {
s
.
mu
.
Lock
()
s
.
mu
.
Lock
()
s
.
pricingData
=
data
s
.
pricingData
=
data
s
.
lastUpdated
=
time
.
Now
()
s
.
lastUpdated
=
time
.
Now
()
s
.
localHash
=
hashStr
s
.
localHash
=
syncHash
s
.
mu
.
Unlock
()
s
.
mu
.
Unlock
()
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Downloaded %d models successfully"
,
len
(
data
))
logger
.
LegacyPrintf
(
"service.pricing"
,
"[Pricing] Downloaded %d models successfully"
,
len
(
data
))
...
@@ -486,16 +516,6 @@ func (s *PricingService) validatePricingURL(raw string) (string, error) {
...
@@ -486,16 +516,6 @@ func (s *PricingService) validatePricingURL(raw string) (string, error) {
return
normalized
,
nil
return
normalized
,
nil
}
}
// computeFileHash 计算文件哈希
func
(
s
*
PricingService
)
computeFileHash
(
filePath
string
)
(
string
,
error
)
{
data
,
err
:=
os
.
ReadFile
(
filePath
)
if
err
!=
nil
{
return
""
,
err
}
hash
:=
sha256
.
Sum256
(
data
)
return
hex
.
EncodeToString
(
hash
[
:
]),
nil
}
// GetModelPricing 获取模型价格(带模糊匹配)
// GetModelPricing 获取模型价格(带模糊匹配)
func
(
s
*
PricingService
)
GetModelPricing
(
modelName
string
)
*
LiteLLMModelPricing
{
func
(
s
*
PricingService
)
GetModelPricing
(
modelName
string
)
*
LiteLLMModelPricing
{
s
.
mu
.
RLock
()
s
.
mu
.
RLock
()
...
...
deploy/config.example.yaml
View file @
50288e6b
...
@@ -865,10 +865,10 @@ rate_limit:
...
@@ -865,10 +865,10 @@ rate_limit:
pricing
:
pricing
:
# URL to fetch model pricing data (default: pinned model-price-repo commit)
# URL to fetch model pricing data (default: pinned model-price-repo commit)
# 获取模型定价数据的 URL(默认:固定 commit 的 model-price-repo)
# 获取模型定价数据的 URL(默认:固定 commit 的 model-price-repo)
remote_url
:
"
https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/
c7947e9871687e664180bc971d4837f1fc2784a9
/model_prices_and_context_window.json"
remote_url
:
"
https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/
refs/heads/main/
/model_prices_and_context_window.json"
# Hash verification URL (optional)
# Hash verification URL (optional)
# 哈希校验 URL(可选)
# 哈希校验 URL(可选)
hash_url
:
"
https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/
c7947e9871687e664180bc971d4837f1fc2784a9
/model_prices_and_context_window.sha256"
hash_url
:
"
https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/
refs/heads/main/
/model_prices_and_context_window.sha256"
# Local data directory for caching
# Local data directory for caching
# 本地数据缓存目录
# 本地数据缓存目录
data_dir
:
"
./data"
data_dir
:
"
./data"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment