Files
nofx/backtest/ai_client.go
tinkle-community 8e294a5eed refactor: restructure project directories for better modularity
- Delete llm/ dead code (3 files, zero references)
- Split mcp/ into sub-packages: mcp/provider/ (8 providers) and
  mcp/payment/ (4 payment clients) with registry pattern
- Export Client internal fields and ClientHooks interface for
  sub-package access
- Split api/server.go (3892 lines) into 8 domain-specific handler files
- Split trader/auto_trader.go (2296 lines) into 5 focused files
- Reorganize web/src/components/ flat files into auth/, charts/,
  trader/, common/, modals/, backtest/ subdirectories
- Update all consumer imports to use registry-based provider creation
2026-03-11 23:58:13 +08:00

73 lines
2.2 KiB
Go

package backtest
import (
"fmt"
"strings"
"nofx/mcp"
_ "nofx/mcp/payment"
_ "nofx/mcp/provider"
)
// configureMCPClient creates/clones an MCP client based on configuration (returns mcp.AIClient interface).
// Note: mcp.New() returns an interface type; here we convert to concrete implementation before copying to avoid concurrent shared state.
func configureMCPClient(cfg BacktestConfig, base mcp.AIClient) (mcp.AIClient, error) {
providerName := strings.ToLower(strings.TrimSpace(cfg.AICfg.Provider))
// Inherit base client
if providerName == "" || providerName == "inherit" || providerName == "default" {
client := cloneBaseClient(base)
if cfg.AICfg.APIKey != "" || cfg.AICfg.BaseURL != "" || cfg.AICfg.Model != "" {
client.SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
}
return client, nil
}
// Custom provider uses cloned base
if providerName == "custom" {
if cfg.AICfg.BaseURL == "" || cfg.AICfg.APIKey == "" || cfg.AICfg.Model == "" {
return nil, fmt.Errorf("custom provider requires base_url, api key and model")
}
client := cloneBaseClient(base)
client.SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
return client, nil
}
// Create client via registry
client := mcp.NewAIClientByProvider(providerName)
if client == nil {
return nil, fmt.Errorf("unsupported ai provider %s", cfg.AICfg.Provider)
}
if cfg.AICfg.APIKey == "" {
return nil, fmt.Errorf("%s provider requires api key", providerName)
}
// Payment providers ignore custom URL
switch providerName {
case "blockrun-base", "blockrun-sol", "claw402":
client.SetAPIKey(cfg.AICfg.APIKey, "", cfg.AICfg.Model)
default:
client.SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
}
return client, nil
}
// cloneBaseClient copies the base client to avoid shared mutable state.
// Uses the ClientEmbedder interface to extract the underlying *mcp.Client
// from any provider type that embeds it.
func cloneBaseClient(base mcp.AIClient) *mcp.Client {
if embedder, ok := base.(mcp.ClientEmbedder); ok {
if inner := embedder.BaseClient(); inner != nil {
cp := *inner
return &cp
}
}
if c, ok := base.(*mcp.Client); ok {
cp := *c
return &cp
}
// Fall back to a new default client
return mcp.NewClient().(*mcp.Client)
}