mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-10 06:20:58 +08:00
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
This commit is contained in:
@@ -5,15 +5,17 @@ import (
|
||||
"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) {
|
||||
provider := strings.ToLower(strings.TrimSpace(cfg.AICfg.Provider))
|
||||
providerName := strings.ToLower(strings.TrimSpace(cfg.AICfg.Provider))
|
||||
|
||||
// DeepSeek
|
||||
if provider == "" || provider == "inherit" || provider == "default" {
|
||||
// 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)
|
||||
@@ -21,143 +23,49 @@ func configureMCPClient(cfg BacktestConfig, base mcp.AIClient) (mcp.AIClient, er
|
||||
return client, nil
|
||||
}
|
||||
|
||||
switch provider {
|
||||
case "deepseek":
|
||||
if cfg.AICfg.APIKey == "" {
|
||||
return nil, fmt.Errorf("deepseek provider requires api key")
|
||||
}
|
||||
ds := mcp.NewDeepSeekClientWithOptions()
|
||||
ds.(*mcp.DeepSeekClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
|
||||
return ds, nil
|
||||
case "qwen":
|
||||
if cfg.AICfg.APIKey == "" {
|
||||
return nil, fmt.Errorf("qwen provider requires api key")
|
||||
}
|
||||
qc := mcp.NewQwenClientWithOptions()
|
||||
qc.(*mcp.QwenClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
|
||||
return qc, nil
|
||||
case "claude":
|
||||
if cfg.AICfg.APIKey == "" {
|
||||
return nil, fmt.Errorf("claude provider requires api key")
|
||||
}
|
||||
cc := mcp.NewClaudeClientWithOptions()
|
||||
cc.(*mcp.ClaudeClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
|
||||
return cc, nil
|
||||
case "kimi":
|
||||
if cfg.AICfg.APIKey == "" {
|
||||
return nil, fmt.Errorf("kimi provider requires api key")
|
||||
}
|
||||
kc := mcp.NewKimiClientWithOptions()
|
||||
kc.(*mcp.KimiClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
|
||||
return kc, nil
|
||||
case "gemini":
|
||||
if cfg.AICfg.APIKey == "" {
|
||||
return nil, fmt.Errorf("gemini provider requires api key")
|
||||
}
|
||||
gc := mcp.NewGeminiClientWithOptions()
|
||||
gc.(*mcp.GeminiClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
|
||||
return gc, nil
|
||||
case "grok":
|
||||
if cfg.AICfg.APIKey == "" {
|
||||
return nil, fmt.Errorf("grok provider requires api key")
|
||||
}
|
||||
grokC := mcp.NewGrokClientWithOptions()
|
||||
grokC.(*mcp.GrokClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
|
||||
return grokC, nil
|
||||
case "openai":
|
||||
if cfg.AICfg.APIKey == "" {
|
||||
return nil, fmt.Errorf("openai provider requires api key")
|
||||
}
|
||||
oaiC := mcp.NewOpenAIClientWithOptions()
|
||||
oaiC.(*mcp.OpenAIClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
|
||||
return oaiC, nil
|
||||
case "minimax":
|
||||
if cfg.AICfg.APIKey == "" {
|
||||
return nil, fmt.Errorf("minimax provider requires api key")
|
||||
}
|
||||
mmC := mcp.NewMiniMaxClientWithOptions()
|
||||
mmC.(*mcp.MiniMaxClient).SetAPIKey(cfg.AICfg.APIKey, cfg.AICfg.BaseURL, cfg.AICfg.Model)
|
||||
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 "claw402":
|
||||
if cfg.AICfg.APIKey == "" {
|
||||
return nil, fmt.Errorf("claw402 provider requires wallet private key")
|
||||
}
|
||||
claw := mcp.NewClaw402Client()
|
||||
claw.SetAPIKey(cfg.AICfg.APIKey, "", cfg.AICfg.Model)
|
||||
return claw, nil
|
||||
case "custom":
|
||||
// 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
|
||||
default:
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// Prefer to reuse the passed-in base client (deep copy)
|
||||
switch c := base.(type) {
|
||||
case *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
|
||||
case *mcp.DeepSeekClient:
|
||||
if c != nil && c.Client != nil {
|
||||
cp := *c.Client
|
||||
return &cp
|
||||
}
|
||||
case *mcp.QwenClient:
|
||||
if c != nil && c.Client != nil {
|
||||
cp := *c.Client
|
||||
return &cp
|
||||
}
|
||||
case *mcp.ClaudeClient:
|
||||
if c != nil && c.Client != nil {
|
||||
cp := *c.Client
|
||||
return &cp
|
||||
}
|
||||
case *mcp.KimiClient:
|
||||
if c != nil && c.Client != nil {
|
||||
cp := *c.Client
|
||||
return &cp
|
||||
}
|
||||
case *mcp.GeminiClient:
|
||||
if c != nil && c.Client != nil {
|
||||
cp := *c.Client
|
||||
return &cp
|
||||
}
|
||||
case *mcp.GrokClient:
|
||||
if c != nil && c.Client != nil {
|
||||
cp := *c.Client
|
||||
return &cp
|
||||
}
|
||||
case *mcp.OpenAIClient:
|
||||
if c != nil && c.Client != nil {
|
||||
cp := *c.Client
|
||||
return &cp
|
||||
}
|
||||
case *mcp.MiniMaxClient:
|
||||
if c != nil && c.Client != nil {
|
||||
cp := *c.Client
|
||||
return &cp
|
||||
}
|
||||
}
|
||||
// Fall back to a new default client
|
||||
return mcp.NewClient().(*mcp.Client)
|
||||
|
||||
Reference in New Issue
Block a user