mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
feat: AI cost tracking, pre-launch balance check, low balance alerts
- store/ai_charge.go: local AI cost tracking per call (SQLite) - wallet/usdc.go: shared USDC balance query (Base chain RPC) - Pre-launch: estimate daily cost + runway days - Low balance: warn <$1, error at $0 (every 10 cycles) - API: GET /api/ai-costs for cost history - Frontend: model cards show price per call - Frontend: wallet create + QR deposit + balance display
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"nofx/kernel"
|
||||
"nofx/logger"
|
||||
"nofx/store"
|
||||
"nofx/wallet"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -27,6 +28,11 @@ func (at *AutoTrader) runCycle() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check USDC balance periodically for claw402 users (every 10 cycles)
|
||||
if at.callCount%10 == 0 && store.IsClaw402Config(at.config.AIModel) {
|
||||
at.checkClaw402Balance()
|
||||
}
|
||||
|
||||
// Create decision record
|
||||
record := &store.DecisionRecord{
|
||||
ExecutionLog: []string{},
|
||||
@@ -110,6 +116,13 @@ func (at *AutoTrader) runCycle() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Record AI charge (track cost regardless of decision outcome)
|
||||
if aiDecision != nil && at.store != nil {
|
||||
if chargeErr := at.store.AICharge().Record(at.id, at.aiModel, at.config.AIModel); chargeErr != nil {
|
||||
logger.Warnf("⚠️ Failed to record AI charge: %v", chargeErr)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
record.Success = false
|
||||
record.ErrorMessage = fmt.Sprintf("Failed to get AI decision: %v", err)
|
||||
@@ -558,3 +571,36 @@ func sortDecisionsByPriority(decisions []kernel.Decision) []kernel.Decision {
|
||||
|
||||
return sorted
|
||||
}
|
||||
|
||||
// checkClaw402Balance checks USDC balance and logs warnings if low
|
||||
func (at *AutoTrader) checkClaw402Balance() {
|
||||
scanMinutes := int(at.config.ScanInterval.Minutes())
|
||||
if scanMinutes <= 0 {
|
||||
scanMinutes = 3
|
||||
}
|
||||
dailyCost, _ := store.EstimateRunway(1.0, at.config.CustomModelName, scanMinutes)
|
||||
logger.Infof("💰 [%s] Estimated daily AI cost: ~$%.2f (model: %s, interval: %dm)",
|
||||
at.name, dailyCost, at.config.CustomModelName, scanMinutes)
|
||||
|
||||
if at.claw402WalletAddr != "" {
|
||||
balance, err := wallet.QueryUSDCBalance(at.claw402WalletAddr)
|
||||
if err != nil {
|
||||
logger.Warnf("⚠️ [%s] Failed to query USDC balance: %v", at.name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if balance < 1.0 {
|
||||
logger.Warnf("⚠️ [%s] Low USDC balance: $%.2f — AI may stop soon!", at.name, balance)
|
||||
}
|
||||
if balance <= 0 {
|
||||
logger.Errorf("🚨 [%s] USDC balance is ZERO — AI calls will fail!", at.name)
|
||||
}
|
||||
|
||||
runway := float64(0)
|
||||
if dailyCost > 0 {
|
||||
runway = balance / dailyCost
|
||||
}
|
||||
logger.Infof("💰 [%s] USDC Balance: $%.2f | Daily AI cost: ~$%.2f | Runway: ~%.1f days",
|
||||
at.name, balance, dailyCost, runway)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user