feat: add blockRun (x402 USDC) support to all AI model consumers

- telegram/bot.go: add blockrun-base, blockrun-sol, minimax to
  clientForProvider; fix newLLMClient to prefer TelegramConfig.ModelID
  over GetDefault; log USDC payment provider usage
- debate/engine.go: add blockrun-base, blockrun-sol to InitializeClients
- api/strategy.go: add blockrun-base, blockrun-sol to runRealAITest
- backtest/ai_client.go: add blockrun-base, blockrun-sol to configureMCPClient
This commit is contained in:
tinkle-community
2026-03-10 17:47:19 +08:00
parent c5c5ed2a4d
commit af250825e7
4 changed files with 60 additions and 1 deletions

View File

@@ -668,6 +668,12 @@ func (s *Server) runRealAITest(userID, modelID, systemPrompt, userPrompt string)
case "minimax": case "minimax":
aiClient = mcp.NewMiniMaxClient() aiClient = mcp.NewMiniMaxClient()
aiClient.SetAPIKey(apiKey, model.CustomAPIURL, model.CustomModelName) aiClient.SetAPIKey(apiKey, model.CustomAPIURL, model.CustomModelName)
case "blockrun-base":
aiClient = mcp.NewBlockRunBaseClient()
aiClient.SetAPIKey(apiKey, "", model.CustomModelName)
case "blockrun-sol":
aiClient = mcp.NewBlockRunSolClient()
aiClient.SetAPIKey(apiKey, "", model.CustomModelName)
default: default:
// Use generic client // Use generic client
aiClient = mcp.NewClient() aiClient = mcp.NewClient()

View File

@@ -78,6 +78,20 @@ func configureMCPClient(cfg BacktestConfig, base mcp.AIClient) (mcp.AIClient, er
mmC := mcp.NewMiniMaxClientWithOptions() mmC := mcp.NewMiniMaxClientWithOptions()
mmC.(*mcp.MiniMaxClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model) mmC.(*mcp.MiniMaxClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
return mmC, nil return mmC, nil
case "blockrun-base":
if cfg.AICfg.APIKey == "" {
return nil, fmt.Errorf("blockrun-base provider requires wallet private key")
}
brBase := mcp.NewBlockRunBaseClient()
brBase.SetAPIKey(cfg.AICfg.APIKey, "", cfg.AICfg.Model)
return brBase, nil
case "blockrun-sol":
if cfg.AICfg.APIKey == "" {
return nil, fmt.Errorf("blockrun-sol provider requires wallet keypair")
}
brSol := mcp.NewBlockRunSolClient()
brSol.SetAPIKey(cfg.AICfg.APIKey, "", cfg.AICfg.Model)
return brSol, nil
case "custom": case "custom":
if cfg.AICfg.BaseURL == "" || cfg.AICfg.APIKey == "" || cfg.AICfg.Model == "" { if cfg.AICfg.BaseURL == "" || cfg.AICfg.APIKey == "" || cfg.AICfg.Model == "" {
return nil, fmt.Errorf("custom provider requires base_url, api key and model") return nil, fmt.Errorf("custom provider requires base_url, api key and model")

View File

@@ -99,6 +99,10 @@ func (e *DebateEngine) InitializeClients(participants []*store.DebateParticipant
client = mcp.NewKimiClient() client = mcp.NewKimiClient()
case "minimax": case "minimax":
client = mcp.NewMiniMaxClient() client = mcp.NewMiniMaxClient()
case "blockrun-base":
client = mcp.NewBlockRunBaseClient()
case "blockrun-sol":
client = mcp.NewBlockRunSolClient()
default: default:
client = mcp.New() client = mcp.New()
} }

View File

@@ -266,15 +266,39 @@ func sendMarkdownMsg(bot *tgbotapi.BotAPI, chatID int64, text string) {
// ── LLM client ─────────────────────────────────────────────────────────────── // ── LLM client ───────────────────────────────────────────────────────────────
func newLLMClient(st *store.Store, userID string) mcp.AIClient { func newLLMClient(st *store.Store, userID string) mcp.AIClient {
// 1. Prefer the model explicitly configured for Telegram (Settings → Telegram → AI Model)
if tgCfg, err := st.TelegramConfig().Get(); err == nil && tgCfg.ModelID != "" {
if model, err := st.AIModel().Get(userID, tgCfg.ModelID); err == nil && model.Enabled {
apiKey := string(model.APIKey)
if apiKey != "" {
client := clientForProvider(model.Provider)
client.SetAPIKey(apiKey, model.CustomAPIURL, model.CustomModelName)
if isUSDCProvider(model.Provider) {
logger.Infof("Telegram agent: provider=%s (USDC payment) user=%s", model.Provider, userID)
} else {
logger.Infof("Telegram agent: provider=%s user=%s", model.Provider, userID)
}
return client
}
}
}
// 2. Fall back to first enabled model
if model, err := st.AIModel().GetDefault(userID); err == nil { if model, err := st.AIModel().GetDefault(userID); err == nil {
apiKey := string(model.APIKey) apiKey := string(model.APIKey)
if apiKey != "" { if apiKey != "" {
client := clientForProvider(model.Provider) client := clientForProvider(model.Provider)
client.SetAPIKey(apiKey, model.CustomAPIURL, model.CustomModelName) client.SetAPIKey(apiKey, model.CustomAPIURL, model.CustomModelName)
logger.Infof("Telegram agent: provider=%s user=%s", model.Provider, userID) if isUSDCProvider(model.Provider) {
logger.Infof("Telegram agent: provider=%s (USDC payment) user=%s", model.Provider, userID)
} else {
logger.Infof("Telegram agent: provider=%s user=%s", model.Provider, userID)
}
return client return client
} }
} }
// 3. Environment variable fallback
for _, pair := range []struct{ provider, key, url string }{ for _, pair := range []struct{ provider, key, url string }{
{"deepseek", os.Getenv("DEEPSEEK_API_KEY"), mcp.DefaultDeepSeekBaseURL}, {"deepseek", os.Getenv("DEEPSEEK_API_KEY"), mcp.DefaultDeepSeekBaseURL},
{"openai", os.Getenv("OPENAI_API_KEY"), ""}, {"openai", os.Getenv("OPENAI_API_KEY"), ""},
@@ -289,6 +313,11 @@ func newLLMClient(st *store.Store, userID string) mcp.AIClient {
return nil return nil
} }
// isUSDCProvider returns true for providers that pay per call with USDC (x402 protocol).
func isUSDCProvider(provider string) bool {
return provider == "blockrun-base" || provider == "blockrun-sol"
}
func clientForProvider(provider string) mcp.AIClient { func clientForProvider(provider string) mcp.AIClient {
switch provider { switch provider {
case "openai": case "openai":
@@ -305,6 +334,12 @@ func clientForProvider(provider string) mcp.AIClient {
return mcp.NewGrokClient() return mcp.NewGrokClient()
case "gemini": case "gemini":
return mcp.NewGeminiClient() return mcp.NewGeminiClient()
case "minimax":
return mcp.NewMiniMaxClient()
case "blockrun-base":
return mcp.NewBlockRunBaseClient()
case "blockrun-sol":
return mcp.NewBlockRunSolClient()
default: default:
return mcp.NewDeepSeekClient() return mcp.NewDeepSeekClient()
} }