mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
f0d3352971
* feat: enforce strategy limits to prevent token overflow * fix: tune token limits after real-world testing - Relax kline max 20→30, timeframes 3→4 (tested ~41K tokens, safe under 131K) - Restore ranking limits to original [5,10,15,20] options (only ~1.5K token impact) - Add static coins limit (max 3) with toast notification - Add timeframe limit toast when exceeding 4 - Log SSE token usage (prompt/completion/total) from API response - Fix nil logger crash in claw402 data client (engine.go) * feat: add token estimation functionality for strategy configurations * feat: add discard changes button in Strategy Studio for unsaved modifications * feat: retain selected strategy after saving in Strategy Studio * feat: enhance strategy display in Strategy Studio with improved layout and sorting of token limits * refactor: improve layout and styling of stats display in CompetitionPage * refactor: replace select elements with NofxSelect component for improved consistency in strategy configuration forms * style: update NofxSelect component to use smaller text size for improved readability * feat: implement token overflow handling in strategy updates and UI --------- Co-authored-by: Dean <afei.wuhao@gmail.com>
113 lines
3.5 KiB
Go
113 lines
3.5 KiB
Go
package store
|
|
|
|
import "testing"
|
|
|
|
func TestEstimateTokens_DefaultConfig(t *testing.T) {
|
|
config := GetDefaultStrategyConfig("en")
|
|
est := config.EstimateTokens()
|
|
|
|
if est.Total <= 0 {
|
|
t.Errorf("expected positive token estimate, got %d", est.Total)
|
|
}
|
|
if est.Total > 200000 {
|
|
t.Errorf("token estimate %d seems unreasonably high for default config", est.Total)
|
|
}
|
|
|
|
// Breakdown should sum approximately to total (before 15% margin)
|
|
subtotal := est.Breakdown.SystemPrompt + est.Breakdown.MarketData +
|
|
est.Breakdown.RankingData + est.Breakdown.QuantData + est.Breakdown.FixedOverhead
|
|
expectedTotal := subtotal * 115 / 100
|
|
if est.Total != expectedTotal {
|
|
t.Errorf("total %d != breakdown subtotal %d * 1.15 = %d", est.Total, subtotal, expectedTotal)
|
|
}
|
|
|
|
// Should have model limits
|
|
if len(est.ModelLimits) == 0 {
|
|
t.Error("expected model limits to be populated")
|
|
}
|
|
|
|
// Default config should be ok for all models
|
|
for _, ml := range est.ModelLimits {
|
|
if ml.Level == "danger" {
|
|
t.Errorf("default config should not exceed %s limit, got %d%%", ml.Name, ml.UsagePct)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEstimateTokens_ZhVsEn(t *testing.T) {
|
|
enConfig := GetDefaultStrategyConfig("en")
|
|
zhConfig := GetDefaultStrategyConfig("zh")
|
|
|
|
enEst := enConfig.EstimateTokens()
|
|
zhEst := zhConfig.EstimateTokens()
|
|
|
|
// Chinese config should have more tokens for system prompt due to CJK encoding
|
|
// but total can vary — just ensure both are reasonable
|
|
if enEst.Total <= 0 || zhEst.Total <= 0 {
|
|
t.Errorf("both estimates should be positive: en=%d, zh=%d", enEst.Total, zhEst.Total)
|
|
}
|
|
}
|
|
|
|
func TestEstimateTokens_HighConfig(t *testing.T) {
|
|
config := GetDefaultStrategyConfig("en")
|
|
// Push config to extremes (beyond clamped limits)
|
|
config.CoinSource.SourceType = "static"
|
|
config.CoinSource.StaticCoins = []string{"BTCUSDT", "ETHUSDT", "SOLUSDT", "DOGEUSDT", "XRPUSDT"}
|
|
config.Indicators.Klines.SelectedTimeframes = []string{"1m", "3m", "5m", "15m", "1h", "4h"}
|
|
config.Indicators.Klines.PrimaryCount = 100
|
|
config.Indicators.EnableEMA = true
|
|
config.Indicators.EnableMACD = true
|
|
config.Indicators.EnableRSI = true
|
|
config.Indicators.EnableATR = true
|
|
config.Indicators.EnableBOLL = true
|
|
|
|
est := config.EstimateTokens()
|
|
|
|
// Should produce a higher estimate than default
|
|
defaultCfg := GetDefaultStrategyConfig("en")
|
|
defaultEst := defaultCfg.EstimateTokens()
|
|
if est.Total <= defaultEst.Total {
|
|
t.Errorf("high config estimate %d should be greater than default %d", est.Total, defaultEst.Total)
|
|
}
|
|
|
|
// Should have some models in warning/danger
|
|
hasDanger := false
|
|
for _, ml := range est.ModelLimits {
|
|
if ml.Level == "danger" || ml.Level == "warning" {
|
|
hasDanger = true
|
|
break
|
|
}
|
|
}
|
|
// With 5 coins * 6 timeframes * 100 klines, this should exceed small models
|
|
if !hasDanger {
|
|
t.Logf("high config estimate: %d tokens", est.Total)
|
|
}
|
|
}
|
|
|
|
func TestGetContextLimit(t *testing.T) {
|
|
if got := GetContextLimit("deepseek"); got != 131072 {
|
|
t.Errorf("deepseek limit = %d, want 131072", got)
|
|
}
|
|
if got := GetContextLimit("unknown_provider"); got != 131072 {
|
|
t.Errorf("unknown provider should return default 131072, got %d", got)
|
|
}
|
|
}
|
|
|
|
func TestGetEffectiveCoinCount(t *testing.T) {
|
|
config := StrategyConfig{
|
|
CoinSource: CoinSourceConfig{
|
|
SourceType: "static",
|
|
StaticCoins: []string{"BTCUSDT", "ETHUSDT"},
|
|
},
|
|
}
|
|
if got := config.getEffectiveCoinCount(); got != 2 {
|
|
t.Errorf("static coin count = %d, want 2", got)
|
|
}
|
|
|
|
config.CoinSource.SourceType = "ai500"
|
|
config.CoinSource.AI500Limit = 5
|
|
if got := config.getEffectiveCoinCount(); got != 5 {
|
|
t.Errorf("ai500 coin count = %d, want 5", got)
|
|
}
|
|
}
|