Files

1 line
102 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "carayev/nofxaios", "files": {"mcp/client.go": {"324cb9d1f4aad8ec759fd769dea155eb30aee449": {"t": 1785746058.6641197, "n": 7162, "h": "b822836ea86e20c2", "c": "package mcp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Provider AI提供商类型\ntype Provider string\n\nconst (\n\tProviderDeepSeek Provider = \"deepseek\"\n\tProviderQwen Provider = \"qwen\"\n\tProviderCustom Provider = \"custom\"\n)\n\n// Client AI API配置\ntype Client struct {\n\tProvider Provider\n\tAPIKey string\n\tBaseURL string\n\tModel string\n\tTimeout time.Duration\n\tUseFullURL bool // 是否使用完整URL(不添加/chat/completions)\n}\n\nfunc New() *Client {\n\t// 默认配置\n\treturn &Client{\n\t\tProvider: ProviderDeepSeek,\n\t\tBaseURL: \"https://api.deepseek.com/v1\",\n\t\tModel: \"deepseek-chat\",\n\t\tTimeout: 120 * time.Second, // 增加到120秒,因为AI需要分析大量数据\n\t}\n}\n\n// SetDeepSeekAPIKey 设置DeepSeek API密钥\n// customURL 为空时使用默认URL,customModel 为空时使用默认模型\nfunc (client *Client) SetDeepSeekAPIKey(apiKey string, customURL string, customModel string) {\n\tclient.Provider = ProviderDeepSeek\n\tclient.APIKey = apiKey\n\tif customURL != \"\" {\n\t\tclient.BaseURL = customURL\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek 使用自定义 BaseURL: %s\", customURL)\n\t} else {\n\t\tclient.BaseURL = \"https://api.deepseek.com/v1\"\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek 使用默认 BaseURL: %s\", client.BaseURL)\n\t}\n\tif customModel != \"\" {\n\t\tclient.Model = customModel\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek 使用自定义 Model: %s\", customModel)\n\t} else {\n\t\tclient.Model = \"deepseek-chat\"\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek 使用默认 Model: %s\", client.Model)\n\t}\n\t// 打印 API Key 的前后各4位用于验证\n\tif len(apiKey) > 8 {\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek API Key: %s...%s\", apiKey[:4], apiKey[len(apiKey)-4:])\n\t}\n}\n\n// SetQwenAPIKey 设置阿里云Qwen API密钥\n// customURL 为空时使用默认URL,customModel 为空时使用默认模型\nfunc (client *Client) SetQwenAPIKey(apiKey string, customURL string, customModel string) {\n\tclient.Provider = ProviderQwen\n\tclient.APIKey = apiKey\n\tif customURL != \"\" {\n\t\tclient.BaseURL = customURL\n\t\tlog.Printf(\"🔧 [MCP] Qwen 使用自定义 BaseURL: %s\", customURL)\n\t} else {\n\t\tclient.BaseURL = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n\t\tlog.Printf(\"🔧 [MCP] Qwen 使用默认 BaseURL: %s\", client.BaseURL)\n\t}\n\tif customModel != \"\" {\n\t\tclient.Model = customModel\n\t\tlog.Printf(\"🔧 [MCP] Qwen 使用自定义 Model: %s\", customModel)\n\t} else {\n\t\tclient.Model = \"qwen-plus\" // 可选: qwen-turbo, qwen-plus, qwen-max\n\t\tlog.Printf(\"🔧 [MCP] Qwen 使用默认 Model: %s\", client.Model)\n\t}\n\t// 打印 API Key 的前后各4位用于验证\n\tif len(apiKey) > 8 {\n\t\tlog.Printf(\"🔧 [MCP] Qwen API Key: %s...%s\", apiKey[:4], apiKey[len(apiKey)-4:])\n\t}\n}\n\n// SetCustomAPI 设置自定义OpenAI兼容API\nfunc (client *Client) SetCustomAPI(apiURL, apiKey, modelName string) {\n\tclient.Provider = ProviderCustom\n\tclient.APIKey = apiKey\n\n\t// 检查URL是否以#结尾,如果是则使用完整URL(不添加/chat/completions)\n\tif strings.HasSuffix(apiURL, \"#\") {\n\t\tclient.BaseURL = strings.TrimSuffix(apiURL, \"#\")\n\t\tclient.UseFullURL = true\n\t} else {\n\t\tclient.BaseURL = apiURL\n\t\tclient.UseFullURL = false\n\t}\n\n\tclient.Model = modelName\n\tclient.Timeout = 120 * time.Second\n}\n\n// SetClient 设置完整的AI配置(高级用户)\nfunc (client *Client) SetClient(Client Client) {\n\tif Client.Timeout == 0 {\n\t\tClient.Timeout = 30 * time.Second\n\t}\n\tclient = &Client\n}\n\n// CallWithMessages 使用 system + user prompt 调用AI API(推荐)\nfunc (client *Client) CallWithMessages(systemPrompt, userPrompt string) (string, error) {\n\tif client.APIKey == \"\" {\n\t\treturn \"\", fmt.Errorf(\"AI API密钥未设置,请先调用 SetDeepSeekAPIKey() 或 SetQwenAPIKey()\")\n\t}\n\n\t// 重试配置\n\tmaxRetries := 3\n\tvar lastErr error\n\n\tfor attempt := 1; attempt <= maxRetries; attempt++ {\n\t\tif attempt > 1 {\n\t\t\tfmt.Printf(\"⚠️ AI API调用失败,正在重试 (%d/%d)...\\n\", attempt, maxRetries)\n\t\t}\n\n\t\tresult, err := client.callOnce(systemPrompt, userPrompt)\n\t\tif err == nil {\n\t\t\tif attempt > 1 {\n\t\t\t\tfmt.Printf(\"✓ AI API重试成功\\n\")\n\t\t\t}\n\t\t\treturn result, nil\n\t\t}\n\n\t\tlastErr = err\n\t\t// 如果不是网络错误,不重试\n\t\tif !isRetryableError(err) {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\t// 重试前等待\n\t\tif attempt < maxRetries {\n\t\t\twaitTime := time.Duration(attempt) * 2 * time.Second\n\t\t\tfmt.Printf(\"⏳ 等待%v后重试...\\n\", waitTime)\n\t\t\ttime.Sleep(waitTime)\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"重试%d次后仍然失败: %w\", maxRetries, lastErr)\n}\n\n// callOnce 单次调用AI API(内部使用)\nfunc (client *Client) callOnce(systemPrompt, userPrompt string) (string, error) {\n\t// 打印当前 AI 配置\n\tlog.Printf(\"📡 [MCP] AI 请求配置:\")\n\tlog.Printf(\" Provider: %s\", client.Provider)\n\tlog.Printf(\" BaseURL: %s\", client.BaseURL)\n\tlog.Printf(\" Model: %s\", client.Model)\n\tlog.Printf(\" UseFullURL: %v\", client.UseFullURL)\n\tif len(client.APIKey) > 8 {\n\t\tlog.Printf(\" API Key: %s...%s\", client.APIKey[:4], client.APIKey[len(client.APIKey)-4:])\n\t}\n\n\t// 构建 messages 数组\n\tmessages := []map[string]string{}\n\n\t// 如果有 system prompt,添加 system message\n\tif systemPrompt != \"\" {\n\t\tmessages = append(messages, map[string]string{\n\t\t\t\"role\": \"system\",\n\t\t\t\"content\": systemPrompt,\n\t\t})\n\t}\n\n\t// 添加 user message\n\tmessages = append(messages, map[string]string{\n\t\t\"role\": \"user\",\n\t\t\"content\": userPrompt,\n\t})\n\n\t// 构建请求体\n\trequestBody := map[string]interface{}{\n\t\t\"model\": client.Model,\n\t\t\"messages\": messages,\n\t\t\"temperature\": 0.5, // 降低temperature以提高JSON格式稳定性\n\t\t\"max_tokens\": 2000,\n\t}\n\n\t// 注意:response_format 参数仅 OpenAI 支持,DeepSeek/Qwen 不支持\n\t// 我们通过强化 prompt 和后处理来确保 JSON 格式正确\n\n\tjsonData, err := json.Marshal(requestBody)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"序列化请求失败: %w\", err)\n\t}\n\n\t// 创建HTTP请求\n\tvar url string\n\tif client.UseFullURL {\n\t\t// 使用完整URL,不添加/chat/completions\n\t\turl = client.BaseURL\n\t} else {\n\t\t// 默认行为:添加/chat/completions\n\t\turl = fmt.Sprintf(\"%s/chat/completions\", client.BaseURL)\n\t}\n\tlog.Printf(\"📡 [MCP] 请求 URL: %s\", url)\n\n\treq, err := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"创建请求失败: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// 根据不同的Provider设置认证方式\n\tswitch client.Provider {\n\tcase ProviderDeepSeek:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.APIKey))\n\tcase ProviderQwen:\n\t\t// 阿里云Qwen使用API-Key认证\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.APIKey))\n\t\t// 注意:如果使用的不是兼容模式,可能需要不同的认证方式\n\tdefault:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.APIKey))\n\t}\n\n\t// 发送请求\n\thttpClient := &http.Client{Timeout: client.Timeout}\n\tresp, err := httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"发送请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 读取响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"API返回错误 (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\t// 解析响应\n\tvar result struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tContent string `json:\"content\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\n\tif len(result.Choices) == 0 {\n\t\treturn \"\", fmt.Errorf(\"API返回空响应\")\n\t}\n\n\treturn result.Choices[0].Message.Content, nil\n}\n\n// isRetryableError 判断错误是否可重试\nfunc isRetryableError(err error) bool {\n\terrStr := err.Error()\n\t// 网络错误、超时、EOF等可以重试\n\tretryableErrors := []string{\n\t\t\"EOF\",\n\t\t\"timeout\",\n\t\t\"connection reset\",\n\t\t\"connection refused\",\n\t\t\"temporary failure\",\n\t\t\"no such host\",\n\t}\n\tfor _, retryable := range retryableErrors {\n\t\tif strings.Contains(errStr, retryable) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"}, "48d13202094e81062970196311a61d2e388a2e94": {"t": 1785759335.2758763, "n": 7162, "h": "b822836ea86e20c2", "c": "package mcp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Provider AI提供商类型\ntype Provider string\n\nconst (\n\tProviderDeepSeek Provider = \"deepseek\"\n\tProviderQwen Provider = \"qwen\"\n\tProviderCustom Provider = \"custom\"\n)\n\n// Client AI API配置\ntype Client struct {\n\tProvider Provider\n\tAPIKey string\n\tBaseURL string\n\tModel string\n\tTimeout time.Duration\n\tUseFullURL bool // 是否使用完整URL(不添加/chat/completions)\n}\n\nfunc New() *Client {\n\t// 默认配置\n\treturn &Client{\n\t\tProvider: ProviderDeepSeek,\n\t\tBaseURL: \"https://api.deepseek.com/v1\",\n\t\tModel: \"deepseek-chat\",\n\t\tTimeout: 120 * time.Second, // 增加到120秒,因为AI需要分析大量数据\n\t}\n}\n\n// SetDeepSeekAPIKey 设置DeepSeek API密钥\n// customURL 为空时使用默认URL,customModel 为空时使用默认模型\nfunc (client *Client) SetDeepSeekAPIKey(apiKey string, customURL string, customModel string) {\n\tclient.Provider = ProviderDeepSeek\n\tclient.APIKey = apiKey\n\tif customURL != \"\" {\n\t\tclient.BaseURL = customURL\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek 使用自定义 BaseURL: %s\", customURL)\n\t} else {\n\t\tclient.BaseURL = \"https://api.deepseek.com/v1\"\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek 使用默认 BaseURL: %s\", client.BaseURL)\n\t}\n\tif customModel != \"\" {\n\t\tclient.Model = customModel\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek 使用自定义 Model: %s\", customModel)\n\t} else {\n\t\tclient.Model = \"deepseek-chat\"\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek 使用默认 Model: %s\", client.Model)\n\t}\n\t// 打印 API Key 的前后各4位用于验证\n\tif len(apiKey) > 8 {\n\t\tlog.Printf(\"🔧 [MCP] DeepSeek API Key: %s...%s\", apiKey[:4], apiKey[len(apiKey)-4:])\n\t}\n}\n\n// SetQwenAPIKey 设置阿里云Qwen API密钥\n// customURL 为空时使用默认URL,customModel 为空时使用默认模型\nfunc (client *Client) SetQwenAPIKey(apiKey string, customURL string, customModel string) {\n\tclient.Provider = ProviderQwen\n\tclient.APIKey = apiKey\n\tif customURL != \"\" {\n\t\tclient.BaseURL = customURL\n\t\tlog.Printf(\"🔧 [MCP] Qwen 使用自定义 BaseURL: %s\", customURL)\n\t} else {\n\t\tclient.BaseURL = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n\t\tlog.Printf(\"🔧 [MCP] Qwen 使用默认 BaseURL: %s\", client.BaseURL)\n\t}\n\tif customModel != \"\" {\n\t\tclient.Model = customModel\n\t\tlog.Printf(\"🔧 [MCP] Qwen 使用自定义 Model: %s\", customModel)\n\t} else {\n\t\tclient.Model = \"qwen-plus\" // 可选: qwen-turbo, qwen-plus, qwen-max\n\t\tlog.Printf(\"🔧 [MCP] Qwen 使用默认 Model: %s\", client.Model)\n\t}\n\t// 打印 API Key 的前后各4位用于验证\n\tif len(apiKey) > 8 {\n\t\tlog.Printf(\"🔧 [MCP] Qwen API Key: %s...%s\", apiKey[:4], apiKey[len(apiKey)-4:])\n\t}\n}\n\n// SetCustomAPI 设置自定义OpenAI兼容API\nfunc (client *Client) SetCustomAPI(apiURL, apiKey, modelName string) {\n\tclient.Provider = ProviderCustom\n\tclient.APIKey = apiKey\n\n\t// 检查URL是否以#结尾,如果是则使用完整URL(不添加/chat/completions)\n\tif strings.HasSuffix(apiURL, \"#\") {\n\t\tclient.BaseURL = strings.TrimSuffix(apiURL, \"#\")\n\t\tclient.UseFullURL = true\n\t} else {\n\t\tclient.BaseURL = apiURL\n\t\tclient.UseFullURL = false\n\t}\n\n\tclient.Model = modelName\n\tclient.Timeout = 120 * time.Second\n}\n\n// SetClient 设置完整的AI配置(高级用户)\nfunc (client *Client) SetClient(Client Client) {\n\tif Client.Timeout == 0 {\n\t\tClient.Timeout = 30 * time.Second\n\t}\n\tclient = &Client\n}\n\n// CallWithMessages 使用 system + user prompt 调用AI API(推荐)\nfunc (client *Client) CallWithMessages(systemPrompt, userPrompt string) (string, error) {\n\tif client.APIKey == \"\" {\n\t\treturn \"\", fmt.Errorf(\"AI API密钥未设置,请先调用 SetDeepSeekAPIKey() 或 SetQwenAPIKey()\")\n\t}\n\n\t// 重试配置\n\tmaxRetries := 3\n\tvar lastErr error\n\n\tfor attempt := 1; attempt <= maxRetries; attempt++ {\n\t\tif attempt > 1 {\n\t\t\tfmt.Printf(\"⚠️ AI API调用失败,正在重试 (%d/%d)...\\n\", attempt, maxRetries)\n\t\t}\n\n\t\tresult, err := client.callOnce(systemPrompt, userPrompt)\n\t\tif err == nil {\n\t\t\tif attempt > 1 {\n\t\t\t\tfmt.Printf(\"✓ AI API重试成功\\n\")\n\t\t\t}\n\t\t\treturn result, nil\n\t\t}\n\n\t\tlastErr = err\n\t\t// 如果不是网络错误,不重试\n\t\tif !isRetryableError(err) {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\t// 重试前等待\n\t\tif attempt < maxRetries {\n\t\t\twaitTime := time.Duration(attempt) * 2 * time.Second\n\t\t\tfmt.Printf(\"⏳ 等待%v后重试...\\n\", waitTime)\n\t\t\ttime.Sleep(waitTime)\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"重试%d次后仍然失败: %w\", maxRetries, lastErr)\n}\n\n// callOnce 单次调用AI API(内部使用)\nfunc (client *Client) callOnce(systemPrompt, userPrompt string) (string, error) {\n\t// 打印当前 AI 配置\n\tlog.Printf(\"📡 [MCP] AI 请求配置:\")\n\tlog.Printf(\" Provider: %s\", client.Provider)\n\tlog.Printf(\" BaseURL: %s\", client.BaseURL)\n\tlog.Printf(\" Model: %s\", client.Model)\n\tlog.Printf(\" UseFullURL: %v\", client.UseFullURL)\n\tif len(client.APIKey) > 8 {\n\t\tlog.Printf(\" API Key: %s...%s\", client.APIKey[:4], client.APIKey[len(client.APIKey)-4:])\n\t}\n\n\t// 构建 messages 数组\n\tmessages := []map[string]string{}\n\n\t// 如果有 system prompt,添加 system message\n\tif systemPrompt != \"\" {\n\t\tmessages = append(messages, map[string]string{\n\t\t\t\"role\": \"system\",\n\t\t\t\"content\": systemPrompt,\n\t\t})\n\t}\n\n\t// 添加 user message\n\tmessages = append(messages, map[string]string{\n\t\t\"role\": \"user\",\n\t\t\"content\": userPrompt,\n\t})\n\n\t// 构建请求体\n\trequestBody := map[string]interface{}{\n\t\t\"model\": client.Model,\n\t\t\"messages\": messages,\n\t\t\"temperature\": 0.5, // 降低temperature以提高JSON格式稳定性\n\t\t\"max_tokens\": 2000,\n\t}\n\n\t// 注意:response_format 参数仅 OpenAI 支持,DeepSeek/Qwen 不支持\n\t// 我们通过强化 prompt 和后处理来确保 JSON 格式正确\n\n\tjsonData, err := json.Marshal(requestBody)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"序列化请求失败: %w\", err)\n\t}\n\n\t// 创建HTTP请求\n\tvar url string\n\tif client.UseFullURL {\n\t\t// 使用完整URL,不添加/chat/completions\n\t\turl = client.BaseURL\n\t} else {\n\t\t// 默认行为:添加/chat/completions\n\t\turl = fmt.Sprintf(\"%s/chat/completions\", client.BaseURL)\n\t}\n\tlog.Printf(\"📡 [MCP] 请求 URL: %s\", url)\n\n\treq, err := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"创建请求失败: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// 根据不同的Provider设置认证方式\n\tswitch client.Provider {\n\tcase ProviderDeepSeek:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.APIKey))\n\tcase ProviderQwen:\n\t\t// 阿里云Qwen使用API-Key认证\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.APIKey))\n\t\t// 注意:如果使用的不是兼容模式,可能需要不同的认证方式\n\tdefault:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.APIKey))\n\t}\n\n\t// 发送请求\n\thttpClient := &http.Client{Timeout: client.Timeout}\n\tresp, err := httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"发送请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 读取响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"API返回错误 (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\t// 解析响应\n\tvar result struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tContent string `json:\"content\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\n\tif len(result.Choices) == 0 {\n\t\treturn \"\", fmt.Errorf(\"API返回空响应\")\n\t}\n\n\treturn result.Choices[0].Message.Content, nil\n}\n\n// isRetryableError 判断错误是否可重试\nfunc isRetryableError(err error) bool {\n\terrStr := err.Error()\n\t// 网络错误、超时、EOF等可以重试\n\tretryableErrors := []string{\n\t\t\"EOF\",\n\t\t\"timeout\",\n\t\t\"connection reset\",\n\t\t\"connection refused\",\n\t\t\"temporary failure\",\n\t\t\"no such host\",\n\t}\n\tfor _, retryable := range retryableErrors {\n\t\tif strings.Contains(errStr, retryable) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"}, "69445594e3f4b57b63d264b1cda48dc982feb443": {"t": 1785759337.2208965, "n": 5633, "h": "b1bdcde2213b4949", "c": "package mcp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Provider AI提供商类型\ntype Provider string\n\nconst (\n\tProviderDeepSeek Provider = \"deepseek\"\n\tProviderQwen Provider = \"qwen\"\n\tProviderCustom Provider = \"custom\"\n)\n\n// Client AI API配置\ntype Client struct {\n\tProvider Provider\n\tAPIKey string\n\tSecretKey string // 阿里云需要\n\tBaseURL string\n\tModel string\n\tTimeout time.Duration\n\tUseFullURL bool // 是否使用完整URL(不添加/chat/completions)\n}\n\nfunc New() *Client {\n\t// 默认配置\n\treturn &Client{\n\t\tProvider: ProviderDeepSeek,\n\t\tBaseURL: \"https://api.deepseek.com/v1\",\n\t\tModel: \"deepseek-chat\",\n\t\tTimeout: 120 * time.Second, // 增加到120秒,因为AI需要分析大量数据\n\t}\n}\n\n// SetDeepSeekAPIKey 设置DeepSeek API密钥\nfunc (client *Client) SetDeepSeekAPIKey(apiKey string) {\n\tclient.Provider = ProviderDeepSeek\n\tclient.APIKey = apiKey\n\tclient.BaseURL = \"https://api.deepseek.com/v1\"\n\tclient.Model = \"deepseek-chat\"\n}\n\n// SetQwenAPIKey 设置阿里云Qwen API密钥\nfunc (client *Client) SetQwenAPIKey(apiKey, secretKey string) {\n\tclient.Provider = ProviderQwen\n\tclient.APIKey = apiKey\n\tclient.SecretKey = secretKey\n\tclient.BaseURL = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n\tclient.Model = \"qwen-plus\" // 可选: qwen-turbo, qwen-plus, qwen-max\n}\n\n// SetCustomAPI 设置自定义OpenAI兼容API\nfunc (client *Client) SetCustomAPI(apiURL, apiKey, modelName string) {\n\tclient.Provider = ProviderCustom\n\tclient.APIKey = apiKey\n\n\t// 检查URL是否以#结尾,如果是则使用完整URL(不添加/chat/completions)\n\tif strings.HasSuffix(apiURL, \"#\") {\n\t\tclient.BaseURL = strings.TrimSuffix(apiURL, \"#\")\n\t\tclient.UseFullURL = true\n\t} else {\n\t\tclient.BaseURL = apiURL\n\t\tclient.UseFullURL = false\n\t}\n\n\tclient.Model = modelName\n\tclient.Timeout = 120 * time.Second\n}\n\n// SetClient 设置完整的AI配置(高级用户)\nfunc (client *Client) SetClient(Client Client) {\n\tif Client.Timeout == 0 {\n\t\tClient.Timeout = 30 * time.Second\n\t}\n\tclient = &Client\n}\n\n// CallWithMessages 使用 system + user prompt 调用AI API(推荐)\nfunc (client *Client) CallWithMessages(systemPrompt, userPrompt string) (string, error) {\n\tif client.APIKey == \"\" {\n\t\treturn \"\", fmt.Errorf(\"AI API密钥未设置,请先调用 SetDeepSeekAPIKey() 或 SetQwenAPIKey()\")\n\t}\n\n\t// 重试配置\n\tmaxRetries := 3\n\tvar lastErr error\n\n\tfor attempt := 1; attempt <= maxRetries; attempt++ {\n\t\tif attempt > 1 {\n\t\t\tfmt.Printf(\"⚠️ AI API调用失败,正在重试 (%d/%d)...\\n\", attempt, maxRetries)\n\t\t}\n\n\t\tresult, err := client.callOnce(systemPrompt, userPrompt)\n\t\tif err == nil {\n\t\t\tif attempt > 1 {\n\t\t\t\tfmt.Printf(\"✓ AI API重试成功\\n\")\n\t\t\t}\n\t\t\treturn result, nil\n\t\t}\n\n\t\tlastErr = err\n\t\t// 如果不是网络错误,不重试\n\t\tif !isRetryableError(err) {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\t// 重试前等待\n\t\tif attempt < maxRetries {\n\t\t\twaitTime := time.Duration(attempt) * 2 * time.Second\n\t\t\tfmt.Printf(\"⏳ 等待%v后重试...\\n\", waitTime)\n\t\t\ttime.Sleep(waitTime)\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"重试%d次后仍然失败: %w\", maxRetries, lastErr)\n}\n\n// callOnce 单次调用AI API(内部使用)\nfunc (client *Client) callOnce(systemPrompt, userPrompt string) (string, error) {\n\t// 构建 messages 数组\n\tmessages := []map[string]string{}\n\n\t// 如果有 system prompt,添加 system message\n\tif systemPrompt != \"\" {\n\t\tmessages = append(messages, map[string]string{\n\t\t\t\"role\": \"system\",\n\t\t\t\"content\": systemPrompt,\n\t\t})\n\t}\n\n\t// 添加 user message\n\tmessages = append(messages, map[string]string{\n\t\t\"role\": \"user\",\n\t\t\"content\": userPrompt,\n\t})\n\n\t// 构建请求体\n\trequestBody := map[string]interface{}{\n\t\t\"model\": client.Model,\n\t\t\"messages\": messages,\n\t\t\"temperature\": 0.5, // 降低temperature以提高JSON格式稳定性\n\t\t\"max_tokens\": 2000,\n\t}\n\n\t// 注意:response_format 参数仅 OpenAI 支持,DeepSeek/Qwen 不支持\n\t// 我们通过强化 prompt 和后处理来确保 JSON 格式正确\n\n\tjsonData, err := json.Marshal(requestBody)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"序列化请求失败: %w\", err)\n\t}\n\n\t// 创建HTTP请求\n\tvar url string\n\tif client.UseFullURL {\n\t\t// 使用完整URL,不添加/chat/completions\n\t\turl = client.BaseURL\n\t} else {\n\t\t// 默认行为:添加/chat/completions\n\t\turl = fmt.Sprintf(\"%s/chat/completions\", client.BaseURL)\n\t}\n\treq, err := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"创建请求失败: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// 根据不同的Provider设置认证方式\n\tswitch client.Provider {\n\tcase ProviderDeepSeek:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.APIKey))\n\tcase ProviderQwen:\n\t\t// 阿里云Qwen使用API-Key认证\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.APIKey))\n\t\t// 注意:如果使用的不是兼容模式,可能需要不同的认证方式\n\tdefault:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.APIKey))\n\t}\n\n\t// 发送请求\n\thttpClient := &http.Client{Timeout: client.Timeout}\n\tresp, err := httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"发送请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 读取响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"API返回错误 (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\t// 解析响应\n\tvar result struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tContent string `json:\"content\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\n\tif len(result.Choices) == 0 {\n\t\treturn \"\", fmt.Errorf(\"API返回空响应\")\n\t}\n\n\treturn result.Choices[0].Message.Content, nil\n}\n\n// isRetryableError 判断错误是否可重试\nfunc isRetryableError(err error) bool {\n\terrStr := err.Error()\n\t// 网络错误、超时、EOF等可以重试\n\tretryableErrors := []string{\n\t\t\"EOF\",\n\t\t\"timeout\",\n\t\t\"connection reset\",\n\t\t\"connection refused\",\n\t\t\"temporary failure\",\n\t\t\"no such host\",\n\t}\n\tfor _, retryable := range retryableErrors {\n\t\tif strings.Contains(errStr, retryable) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"}, "1083c06d1fb76b0f9a65256ffa7b9fea5bc0f66e": {"t": 1785759338.9016144, "n": 5555, "h": "8ac9d63cd4557d24", "c": "package mcp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Provider AI提供商类型\ntype Provider string\n\nconst (\n\tProviderDeepSeek Provider = \"deepseek\"\n\tProviderQwen Provider = \"qwen\"\n\tProviderCustom Provider = \"custom\"\n)\n\n// Client AI API配置\ntype Client struct {\n\tProvider Provider\n\tAPIKey string\n\tSecretKey string // 阿里云需要\n\tBaseURL string\n\tModel string\n\tTimeout time.Duration\n\tUseFullURL bool // 是否使用完整URL(不添加/chat/completions)\n}\n\nfunc New() *Client {\n\t// 默认配置\n\tvar defaultClient = Client{\n\t\tProvider: ProviderDeepSeek,\n\t\tBaseURL: \"https://api.deepseek.com/v1\",\n\t\tModel: \"deepseek-chat\",\n\t\tTimeout: 120 * time.Second, // 增加到120秒,因为AI需要分析大量数据\n\t}\n\treturn &defaultClient\n}\n\n// SetDeepSeekAPIKey 设置DeepSeek API密钥\nfunc (cfg *Client) SetDeepSeekAPIKey(apiKey string) {\n\tcfg.Provider = ProviderDeepSeek\n\tcfg.APIKey = apiKey\n\tcfg.BaseURL = \"https://api.deepseek.com/v1\"\n\tcfg.Model = \"deepseek-chat\"\n}\n\n// SetQwenAPIKey 设置阿里云Qwen API密钥\nfunc (cfg *Client) SetQwenAPIKey(apiKey, secretKey string) {\n\tcfg.Provider = ProviderQwen\n\tcfg.APIKey = apiKey\n\tcfg.SecretKey = secretKey\n\tcfg.BaseURL = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n\tcfg.Model = \"qwen-plus\" // 可选: qwen-turbo, qwen-plus, qwen-max\n}\n\n// SetCustomAPI 设置自定义OpenAI兼容API\nfunc (cfg *Client) SetCustomAPI(apiURL, apiKey, modelName string) {\n\tcfg.Provider = ProviderCustom\n\tcfg.APIKey = apiKey\n\n\t// 检查URL是否以#结尾,如果是则使用完整URL(不添加/chat/completions)\n\tif strings.HasSuffix(apiURL, \"#\") {\n\t\tcfg.BaseURL = strings.TrimSuffix(apiURL, \"#\")\n\t\tcfg.UseFullURL = true\n\t} else {\n\t\tcfg.BaseURL = apiURL\n\t\tcfg.UseFullURL = false\n\t}\n\n\tcfg.Model = modelName\n\tcfg.Timeout = 120 * time.Second\n}\n\n// SetClient 设置完整的AI配置(高级用户)\nfunc (cfg *Client) SetClient(Client Client) {\n\tif Client.Timeout == 0 {\n\t\tClient.Timeout = 30 * time.Second\n\t}\n\tcfg = &Client\n}\n\n// CallWithMessages 使用 system + user prompt 调用AI API(推荐)\nfunc (cfg *Client) CallWithMessages(systemPrompt, userPrompt string) (string, error) {\n\tif cfg.APIKey == \"\" {\n\t\treturn \"\", fmt.Errorf(\"AI API密钥未设置,请先调用 SetDeepSeekAPIKey() 或 SetQwenAPIKey()\")\n\t}\n\n\t// 重试配置\n\tmaxRetries := 3\n\tvar lastErr error\n\n\tfor attempt := 1; attempt <= maxRetries; attempt++ {\n\t\tif attempt > 1 {\n\t\t\tfmt.Printf(\"⚠️ AI API调用失败,正在重试 (%d/%d)...\\n\", attempt, maxRetries)\n\t\t}\n\n\t\tresult, err := cfg.callOnce(systemPrompt, userPrompt)\n\t\tif err == nil {\n\t\t\tif attempt > 1 {\n\t\t\t\tfmt.Printf(\"✓ AI API重试成功\\n\")\n\t\t\t}\n\t\t\treturn result, nil\n\t\t}\n\n\t\tlastErr = err\n\t\t// 如果不是网络错误,不重试\n\t\tif !isRetryableError(err) {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\t// 重试前等待\n\t\tif attempt < maxRetries {\n\t\t\twaitTime := time.Duration(attempt) * 2 * time.Second\n\t\t\tfmt.Printf(\"⏳ 等待%v后重试...\\n\", waitTime)\n\t\t\ttime.Sleep(waitTime)\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"重试%d次后仍然失败: %w\", maxRetries, lastErr)\n}\n\n// callOnce 单次调用AI API(内部使用)\nfunc (cfg *Client) callOnce(systemPrompt, userPrompt string) (string, error) {\n\t// 构建 messages 数组\n\tmessages := []map[string]string{}\n\n\t// 如果有 system prompt,添加 system message\n\tif systemPrompt != \"\" {\n\t\tmessages = append(messages, map[string]string{\n\t\t\t\"role\": \"system\",\n\t\t\t\"content\": systemPrompt,\n\t\t})\n\t}\n\n\t// 添加 user message\n\tmessages = append(messages, map[string]string{\n\t\t\"role\": \"user\",\n\t\t\"content\": userPrompt,\n\t})\n\n\t// 构建请求体\n\trequestBody := map[string]interface{}{\n\t\t\"model\": cfg.Model,\n\t\t\"messages\": messages,\n\t\t\"temperature\": 0.5, // 降低temperature以提高JSON格式稳定性\n\t\t\"max_tokens\": 2000,\n\t}\n\n\t// 注意:response_format 参数仅 OpenAI 支持,DeepSeek/Qwen 不支持\n\t// 我们通过强化 prompt 和后处理来确保 JSON 格式正确\n\n\tjsonData, err := json.Marshal(requestBody)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"序列化请求失败: %w\", err)\n\t}\n\n\t// 创建HTTP请求\n\tvar url string\n\tif cfg.UseFullURL {\n\t\t// 使用完整URL,不添加/chat/completions\n\t\turl = cfg.BaseURL\n\t} else {\n\t\t// 默认行为:添加/chat/completions\n\t\turl = fmt.Sprintf(\"%s/chat/completions\", cfg.BaseURL)\n\t}\n\treq, err := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"创建请求失败: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// 根据不同的Provider设置认证方式\n\tswitch cfg.Provider {\n\tcase ProviderDeepSeek:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", cfg.APIKey))\n\tcase ProviderQwen:\n\t\t// 阿里云Qwen使用API-Key认证\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", cfg.APIKey))\n\t\t// 注意:如果使用的不是兼容模式,可能需要不同的认证方式\n\tdefault:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", cfg.APIKey))\n\t}\n\n\t// 发送请求\n\tclient := &http.Client{Timeout: cfg.Timeout}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"发送请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 读取响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"API返回错误 (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\t// 解析响应\n\tvar result struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tContent string `json:\"content\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\n\tif len(result.Choices) == 0 {\n\t\treturn \"\", fmt.Errorf(\"API返回空响应\")\n\t}\n\n\treturn result.Choices[0].Message.Content, nil\n}\n\n// isRetryableError 判断错误是否可重试\nfunc isRetryableError(err error) bool {\n\terrStr := err.Error()\n\t// 网络错误、超时、EOF等可以重试\n\tretryableErrors := []string{\n\t\t\"EOF\",\n\t\t\"timeout\",\n\t\t\"connection reset\",\n\t\t\"connection refused\",\n\t\t\"temporary failure\",\n\t\t\"no such host\",\n\t}\n\tfor _, retryable := range retryableErrors {\n\t\tif strings.Contains(errStr, retryable) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"}, "9886c61ea05b84eaf41341921dd730a7357c3785": {"t": 1785759341.261068, "n": 5687, "h": "2b2898a7a8689f6f", "c": "package mcp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Provider AI提供商类型\ntype Provider string\n\nconst (\n\tProviderDeepSeek Provider = \"deepseek\"\n\tProviderQwen Provider = \"qwen\"\n\tProviderCustom Provider = \"custom\"\n)\n\n// Config AI API配置\ntype Config struct {\n\tProvider Provider\n\tAPIKey string\n\tSecretKey string // 阿里云需要\n\tBaseURL string\n\tModel string\n\tTimeout time.Duration\n\tUseFullURL bool // 是否使用完整URL(不添加/chat/completions)\n}\n\n// 默认配置\nvar defaultConfig = Config{\n\tProvider: ProviderDeepSeek,\n\tBaseURL: \"https://api.deepseek.com/v1\",\n\tModel: \"deepseek-chat\",\n\tTimeout: 120 * time.Second, // 增加到120秒,因为AI需要分析大量数据\n}\n\n// SetDeepSeekAPIKey 设置DeepSeek API密钥\nfunc SetDeepSeekAPIKey(apiKey string) {\n\tdefaultConfig.Provider = ProviderDeepSeek\n\tdefaultConfig.APIKey = apiKey\n\tdefaultConfig.BaseURL = \"https://api.deepseek.com/v1\"\n\tdefaultConfig.Model = \"deepseek-chat\"\n}\n\n// SetQwenAPIKey 设置阿里云Qwen API密钥\nfunc SetQwenAPIKey(apiKey, secretKey string) {\n\tdefaultConfig.Provider = ProviderQwen\n\tdefaultConfig.APIKey = apiKey\n\tdefaultConfig.SecretKey = secretKey\n\tdefaultConfig.BaseURL = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n\tdefaultConfig.Model = \"qwen-plus\" // 可选: qwen-turbo, qwen-plus, qwen-max\n}\n\n// SetCustomAPI 设置自定义OpenAI兼容API\nfunc SetCustomAPI(apiURL, apiKey, modelName string) {\n\tdefaultConfig.Provider = ProviderCustom\n\tdefaultConfig.APIKey = apiKey\n\n\t// 检查URL是否以#结尾,如果是则使用完整URL(不添加/chat/completions)\n\tif strings.HasSuffix(apiURL, \"#\") {\n\t\tdefaultConfig.BaseURL = strings.TrimSuffix(apiURL, \"#\")\n\t\tdefaultConfig.UseFullURL = true\n\t} else {\n\t\tdefaultConfig.BaseURL = apiURL\n\t\tdefaultConfig.UseFullURL = false\n\t}\n\n\tdefaultConfig.Model = modelName\n\tdefaultConfig.Timeout = 120 * time.Second\n}\n\n// SetConfig 设置完整的AI配置(高级用户)\nfunc SetConfig(config Config) {\n\tif config.Timeout == 0 {\n\t\tconfig.Timeout = 30 * time.Second\n\t}\n\tdefaultConfig = config\n}\n\n// CallWithMessages 使用 system + user prompt 调用AI API(推荐)\nfunc CallWithMessages(systemPrompt, userPrompt string) (string, error) {\n\tif defaultConfig.APIKey == \"\" {\n\t\treturn \"\", fmt.Errorf(\"AI API密钥未设置,请先调用 SetDeepSeekAPIKey() 或 SetQwenAPIKey()\")\n\t}\n\n\t// 重试配置\n\tmaxRetries := 3\n\tvar lastErr error\n\n\tfor attempt := 1; attempt <= maxRetries; attempt++ {\n\t\tif attempt > 1 {\n\t\t\tfmt.Printf(\"⚠️ AI API调用失败,正在重试 (%d/%d)...\\n\", attempt, maxRetries)\n\t\t}\n\n\t\tresult, err := callOnce(systemPrompt, userPrompt)\n\t\tif err == nil {\n\t\t\tif attempt > 1 {\n\t\t\t\tfmt.Printf(\"✓ AI API重试成功\\n\")\n\t\t\t}\n\t\t\treturn result, nil\n\t\t}\n\n\t\tlastErr = err\n\t\t// 如果不是网络错误,不重试\n\t\tif !isRetryableError(err) {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\t// 重试前等待\n\t\tif attempt < maxRetries {\n\t\t\twaitTime := time.Duration(attempt) * 2 * time.Second\n\t\t\tfmt.Printf(\"⏳ 等待%v后重试...\\n\", waitTime)\n\t\t\ttime.Sleep(waitTime)\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"重试%d次后仍然失败: %w\", maxRetries, lastErr)\n}\n\n// callOnce 单次调用AI API(内部使用)\nfunc callOnce(systemPrompt, userPrompt string) (string, error) {\n\t// 构建 messages 数组\n\tmessages := []map[string]string{}\n\n\t// 如果有 system prompt,添加 system message\n\tif systemPrompt != \"\" {\n\t\tmessages = append(messages, map[string]string{\n\t\t\t\"role\": \"system\",\n\t\t\t\"content\": systemPrompt,\n\t\t})\n\t}\n\n\t// 添加 user message\n\tmessages = append(messages, map[string]string{\n\t\t\"role\": \"user\",\n\t\t\"content\": userPrompt,\n\t})\n\n\t// 构建请求体\n\trequestBody := map[string]interface{}{\n\t\t\"model\": defaultConfig.Model,\n\t\t\"messages\": messages,\n\t\t\"temperature\": 0.5, // 降低temperature以提高JSON格式稳定性\n\t\t\"max_tokens\": 2000,\n\t}\n\n\t// 注意:response_format 参数仅 OpenAI 支持,DeepSeek/Qwen 不支持\n\t// 我们通过强化 prompt 和后处理来确保 JSON 格式正确\n\n\tjsonData, err := json.Marshal(requestBody)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"序列化请求失败: %w\", err)\n\t}\n\n\t// 创建HTTP请求\n\tvar url string\n\tif defaultConfig.UseFullURL {\n\t\t// 使用完整URL,不添加/chat/completions\n\t\turl = defaultConfig.BaseURL\n\t} else {\n\t\t// 默认行为:添加/chat/completions\n\t\turl = fmt.Sprintf(\"%s/chat/completions\", defaultConfig.BaseURL)\n\t}\n\treq, err := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"创建请求失败: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// 根据不同的Provider设置认证方式\n\tswitch defaultConfig.Provider {\n\tcase ProviderDeepSeek:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", defaultConfig.APIKey))\n\tcase ProviderQwen:\n\t\t// 阿里云Qwen使用API-Key认证\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", defaultConfig.APIKey))\n\t\t// 注意:如果使用的不是兼容模式,可能需要不同的认证方式\n\tdefault:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", defaultConfig.APIKey))\n\t}\n\n\t// 发送请求\n\tclient := &http.Client{Timeout: defaultConfig.Timeout}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"发送请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 读取响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"API返回错误 (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\t// 解析响应\n\tvar result struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tContent string `json:\"content\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\n\tif len(result.Choices) == 0 {\n\t\treturn \"\", fmt.Errorf(\"API返回空响应\")\n\t}\n\n\treturn result.Choices[0].Message.Content, nil\n}\n\n// isRetryableError 判断错误是否可重试\nfunc isRetryableError(err error) bool {\n\terrStr := err.Error()\n\t// 网络错误、超时、EOF等可以重试\n\tretryableErrors := []string{\n\t\t\"EOF\",\n\t\t\"timeout\",\n\t\t\"connection reset\",\n\t\t\"connection refused\",\n\t\t\"temporary failure\",\n\t\t\"no such host\",\n\t}\n\tfor _, retryable := range retryableErrors {\n\t\tif strings.Contains(errStr, retryable) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"}, "2cdfb87144f2585915fafaad4636327ca6016393": {"t": 1785759342.8836699, "n": 5251, "h": "8e47e6f30091ca38", "c": "package mcp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Provider AI提供商类型\ntype Provider string\n\nconst (\n\tProviderDeepSeek Provider = \"deepseek\"\n\tProviderQwen Provider = \"qwen\"\n\tProviderCustom Provider = \"custom\"\n)\n\n// Config AI API配置\ntype Config struct {\n\tProvider Provider\n\tAPIKey string\n\tSecretKey string // 阿里云需要\n\tBaseURL string\n\tModel string\n\tTimeout time.Duration\n}\n\n// 默认配置\nvar defaultConfig = Config{\n\tProvider: ProviderDeepSeek,\n\tBaseURL: \"https://api.deepseek.com/v1\",\n\tModel: \"deepseek-chat\",\n\tTimeout: 120 * time.Second, // 增加到120秒,因为AI需要分析大量数据\n}\n\n// SetDeepSeekAPIKey 设置DeepSeek API密钥\nfunc SetDeepSeekAPIKey(apiKey string) {\n\tdefaultConfig.Provider = ProviderDeepSeek\n\tdefaultConfig.APIKey = apiKey\n\tdefaultConfig.BaseURL = \"https://api.deepseek.com/v1\"\n\tdefaultConfig.Model = \"deepseek-chat\"\n}\n\n// SetQwenAPIKey 设置阿里云Qwen API密钥\nfunc SetQwenAPIKey(apiKey, secretKey string) {\n\tdefaultConfig.Provider = ProviderQwen\n\tdefaultConfig.APIKey = apiKey\n\tdefaultConfig.SecretKey = secretKey\n\tdefaultConfig.BaseURL = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n\tdefaultConfig.Model = \"qwen-plus\" // 可选: qwen-turbo, qwen-plus, qwen-max\n}\n\n// SetCustomAPI 设置自定义OpenAI兼容API\nfunc SetCustomAPI(apiURL, apiKey, modelName string) {\n\tdefaultConfig.Provider = ProviderCustom\n\tdefaultConfig.APIKey = apiKey\n\tdefaultConfig.BaseURL = apiURL\n\tdefaultConfig.Model = modelName\n\tdefaultConfig.Timeout = 120 * time.Second\n}\n\n// SetConfig 设置完整的AI配置(高级用户)\nfunc SetConfig(config Config) {\n\tif config.Timeout == 0 {\n\t\tconfig.Timeout = 30 * time.Second\n\t}\n\tdefaultConfig = config\n}\n\n// CallWithMessages 使用 system + user prompt 调用AI API(推荐)\nfunc CallWithMessages(systemPrompt, userPrompt string) (string, error) {\n\tif defaultConfig.APIKey == \"\" {\n\t\treturn \"\", fmt.Errorf(\"AI API密钥未设置,请先调用 SetDeepSeekAPIKey() 或 SetQwenAPIKey()\")\n\t}\n\n\t// 重试配置\n\tmaxRetries := 3\n\tvar lastErr error\n\n\tfor attempt := 1; attempt <= maxRetries; attempt++ {\n\t\tif attempt > 1 {\n\t\t\tfmt.Printf(\"⚠️ AI API调用失败,正在重试 (%d/%d)...\\n\", attempt, maxRetries)\n\t\t}\n\n\t\tresult, err := callOnce(systemPrompt, userPrompt)\n\t\tif err == nil {\n\t\t\tif attempt > 1 {\n\t\t\t\tfmt.Printf(\"✓ AI API重试成功\\n\")\n\t\t\t}\n\t\t\treturn result, nil\n\t\t}\n\n\t\tlastErr = err\n\t\t// 如果不是网络错误,不重试\n\t\tif !isRetryableError(err) {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\t// 重试前等待\n\t\tif attempt < maxRetries {\n\t\t\twaitTime := time.Duration(attempt) * 2 * time.Second\n\t\t\tfmt.Printf(\"⏳ 等待%v后重试...\\n\", waitTime)\n\t\t\ttime.Sleep(waitTime)\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"重试%d次后仍然失败: %w\", maxRetries, lastErr)\n}\n\n// callOnce 单次调用AI API(内部使用)\nfunc callOnce(systemPrompt, userPrompt string) (string, error) {\n\t// 构建 messages 数组\n\tmessages := []map[string]string{}\n\n\t// 如果有 system prompt,添加 system message\n\tif systemPrompt != \"\" {\n\t\tmessages = append(messages, map[string]string{\n\t\t\t\"role\": \"system\",\n\t\t\t\"content\": systemPrompt,\n\t\t})\n\t}\n\n\t// 添加 user message\n\tmessages = append(messages, map[string]string{\n\t\t\"role\": \"user\",\n\t\t\"content\": userPrompt,\n\t})\n\n\t// 构建请求体\n\trequestBody := map[string]interface{}{\n\t\t\"model\": defaultConfig.Model,\n\t\t\"messages\": messages,\n\t\t\"temperature\": 0.5, // 降低temperature以提高JSON格式稳定性\n\t\t\"max_tokens\": 2000,\n\t}\n\n\t// 注意:response_format 参数仅 OpenAI 支持,DeepSeek/Qwen 不支持\n\t// 我们通过强化 prompt 和后处理来确保 JSON 格式正确\n\n\tjsonData, err := json.Marshal(requestBody)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"序列化请求失败: %w\", err)\n\t}\n\n\t// 创建HTTP请求\n\turl := fmt.Sprintf(\"%s/chat/completions\", defaultConfig.BaseURL)\n\treq, err := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"创建请求失败: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// 根据不同的Provider设置认证方式\n\tswitch defaultConfig.Provider {\n\tcase ProviderDeepSeek:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", defaultConfig.APIKey))\n\tcase ProviderQwen:\n\t\t// 阿里云Qwen使用API-Key认证\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", defaultConfig.APIKey))\n\t\t// 注意:如果使用的不是兼容模式,可能需要不同的认证方式\n\tdefault:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", defaultConfig.APIKey))\n\t}\n\n\t// 发送请求\n\tclient := &http.Client{Timeout: defaultConfig.Timeout}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"发送请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 读取响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"API返回错误 (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\t// 解析响应\n\tvar result struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tContent string `json:\"content\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\n\tif len(result.Choices) == 0 {\n\t\treturn \"\", fmt.Errorf(\"API返回空响应\")\n\t}\n\n\treturn result.Choices[0].Message.Content, nil\n}\n\n// isRetryableError 判断错误是否可重试\nfunc isRetryableError(err error) bool {\n\terrStr := err.Error()\n\t// 网络错误、超时、EOF等可以重试\n\tretryableErrors := []string{\n\t\t\"EOF\",\n\t\t\"timeout\",\n\t\t\"connection reset\",\n\t\t\"connection refused\",\n\t\t\"temporary failure\",\n\t\t\"no such host\",\n\t}\n\tfor _, retryable := range retryableErrors {\n\t\tif strings.Contains(errStr, retryable) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"}, "f3a87b5a7a19b5df37ef9501486fc05e0270f99e": {"t": 1785759344.46248, "n": 4943, "h": "2030970e80291d95", "c": "package mcp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Provider AI提供商类型\ntype Provider string\n\nconst (\n\tProviderDeepSeek Provider = \"deepseek\"\n\tProviderQwen Provider = \"qwen\"\n)\n\n// Config AI API配置\ntype Config struct {\n\tProvider Provider\n\tAPIKey string\n\tSecretKey string // 阿里云需要\n\tBaseURL string\n\tModel string\n\tTimeout time.Duration\n}\n\n// 默认配置\nvar defaultConfig = Config{\n\tProvider: ProviderDeepSeek,\n\tBaseURL: \"https://api.deepseek.com/v1\",\n\tModel: \"deepseek-chat\",\n\tTimeout: 120 * time.Second, // 增加到120秒,因为AI需要分析大量数据\n}\n\n// SetDeepSeekAPIKey 设置DeepSeek API密钥\nfunc SetDeepSeekAPIKey(apiKey string) {\n\tdefaultConfig.Provider = ProviderDeepSeek\n\tdefaultConfig.APIKey = apiKey\n\tdefaultConfig.BaseURL = \"https://api.deepseek.com/v1\"\n\tdefaultConfig.Model = \"deepseek-chat\"\n}\n\n// SetQwenAPIKey 设置阿里云Qwen API密钥\nfunc SetQwenAPIKey(apiKey, secretKey string) {\n\tdefaultConfig.Provider = ProviderQwen\n\tdefaultConfig.APIKey = apiKey\n\tdefaultConfig.SecretKey = secretKey\n\tdefaultConfig.BaseURL = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n\tdefaultConfig.Model = \"qwen-plus\" // 可选: qwen-turbo, qwen-plus, qwen-max\n}\n\n// SetConfig 设置完整的AI配置(高级用户)\nfunc SetConfig(config Config) {\n\tif config.Timeout == 0 {\n\t\tconfig.Timeout = 30 * time.Second\n\t}\n\tdefaultConfig = config\n}\n\n// CallWithMessages 使用 system + user prompt 调用AI API(推荐)\nfunc CallWithMessages(systemPrompt, userPrompt string) (string, error) {\n\tif defaultConfig.APIKey == \"\" {\n\t\treturn \"\", fmt.Errorf(\"AI API密钥未设置,请先调用 SetDeepSeekAPIKey() 或 SetQwenAPIKey()\")\n\t}\n\n\t// 重试配置\n\tmaxRetries := 3\n\tvar lastErr error\n\n\tfor attempt := 1; attempt <= maxRetries; attempt++ {\n\t\tif attempt > 1 {\n\t\t\tfmt.Printf(\"⚠️ AI API调用失败,正在重试 (%d/%d)...\\n\", attempt, maxRetries)\n\t\t}\n\n\t\tresult, err := callOnce(systemPrompt, userPrompt)\n\t\tif err == nil {\n\t\t\tif attempt > 1 {\n\t\t\t\tfmt.Printf(\"✓ AI API重试成功\\n\")\n\t\t\t}\n\t\t\treturn result, nil\n\t\t}\n\n\t\tlastErr = err\n\t\t// 如果不是网络错误,不重试\n\t\tif !isRetryableError(err) {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\t// 重试前等待\n\t\tif attempt < maxRetries {\n\t\t\twaitTime := time.Duration(attempt) * 2 * time.Second\n\t\t\tfmt.Printf(\"⏳ 等待%v后重试...\\n\", waitTime)\n\t\t\ttime.Sleep(waitTime)\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"重试%d次后仍然失败: %w\", maxRetries, lastErr)\n}\n\n// callOnce 单次调用AI API(内部使用)\nfunc callOnce(systemPrompt, userPrompt string) (string, error) {\n\t// 构建 messages 数组\n\tmessages := []map[string]string{}\n\n\t// 如果有 system prompt,添加 system message\n\tif systemPrompt != \"\" {\n\t\tmessages = append(messages, map[string]string{\n\t\t\t\"role\": \"system\",\n\t\t\t\"content\": systemPrompt,\n\t\t})\n\t}\n\n\t// 添加 user message\n\tmessages = append(messages, map[string]string{\n\t\t\"role\": \"user\",\n\t\t\"content\": userPrompt,\n\t})\n\n\t// 构建请求体\n\trequestBody := map[string]interface{}{\n\t\t\"model\": defaultConfig.Model,\n\t\t\"messages\": messages,\n\t\t\"temperature\": 0.5, // 降低temperature以提高JSON格式稳定性\n\t\t\"max_tokens\": 2000,\n\t}\n\n\t// 注意:response_format 参数仅 OpenAI 支持,DeepSeek/Qwen 不支持\n\t// 我们通过强化 prompt 和后处理来确保 JSON 格式正确\n\n\tjsonData, err := json.Marshal(requestBody)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"序列化请求失败: %w\", err)\n\t}\n\n\t// 创建HTTP请求\n\turl := fmt.Sprintf(\"%s/chat/completions\", defaultConfig.BaseURL)\n\treq, err := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"创建请求失败: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// 根据不同的Provider设置认证方式\n\tswitch defaultConfig.Provider {\n\tcase ProviderDeepSeek:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", defaultConfig.APIKey))\n\tcase ProviderQwen:\n\t\t// 阿里云Qwen使用API-Key认证\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", defaultConfig.APIKey))\n\t\t// 注意:如果使用的不是兼容模式,可能需要不同的认证方式\n\tdefault:\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", defaultConfig.APIKey))\n\t}\n\n\t// 发送请求\n\tclient := &http.Client{Timeout: defaultConfig.Timeout}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"发送请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 读取响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"API返回错误 (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\t// 解析响应\n\tvar result struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tContent string `json:\"content\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\n\tif len(result.Choices) == 0 {\n\t\treturn \"\", fmt.Errorf(\"API返回空响应\")\n\t}\n\n\treturn result.Choices[0].Message.Content, nil\n}\n\n// isRetryableError 判断错误是否可重试\nfunc isRetryableError(err error) bool {\n\terrStr := err.Error()\n\t// 网络错误、超时、EOF等可以重试\n\tretryableErrors := []string{\n\t\t\"EOF\",\n\t\t\"timeout\",\n\t\t\"connection reset\",\n\t\t\"connection refused\",\n\t\t\"temporary failure\",\n\t\t\"no such host\",\n\t}\n\tfor _, retryable := range retryableErrors {\n\t\tif strings.Contains(errStr, retryable) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"}}, "docs/architecture/README.md": {"324cb9d1f4aad8ec759fd769dea155eb30aee449": {"t": 1785747248.7755442, "n": 14364, "h": "58d70f8ec30eae86", "c": "# 🏗️ NOFX Architecture Documentation\n\n**Language:** [English](README.md) | [中文](README.zh-CN.md)\n\nTechnical documentation for developers who want to understand NOFX internals.\n\n---\n\n## 📋 Overview\n\nNOFX is a full-stack AI trading platform with:\n- **Backend:** Go (Gin framework, SQLite)\n- **Frontend:** React/TypeScript (Vite, TailwindCSS)\n- **Architecture:** Microservice-inspired modular design\n\n---\n\n## 📁 Project Structure\n\n```\nnofx/\n├── main.go # Program entry (multi-trader manager)\n├── config.json # ~~Multi-trader config~~ (Now via web interface)\n├── trading.db # SQLite database (traders, models, exchanges)\n│\n├── api/ # HTTP API service\n│ └── server.go # Gin framework, RESTful API\n│\n├── trader/ # Trading core\n│ ├── auto_trader.go # Auto trading main controller\n│ ├── interface.go # Unified trader interface\n│ ├── binance_futures.go # Binance API wrapper\n│ ├── hyperliquid_trader.go # Hyperliquid DEX wrapper\n│ └── aster_trader.go # Aster DEX wrapper\n│\n├── manager/ # Multi-trader management\n│ └── trader_manager.go # Manages multiple trader instances\n│\n├── config/ # Configuration & database\n│ └── database.go # SQLite operations and schema\n│\n├── auth/ # Authentication\n│ └── jwt.go # JWT token management & 2FA\n│\n├── mcp/ # Model Context Protocol - AI communication\n│ └── client.go # AI API client (DeepSeek/Qwen/Custom)\n│\n├── decision/ # AI decision engine\n│ ├── engine.go # Decision logic with historical feedback\n│ └── prompt_manager.go # Prompt template system\n│\n├── market/ # Market data fetching\n│ └── data.go # Market data & technical indicators (TA-Lib)\n│ └── api_client.go # Market data acquisition API\n│ └── websocket_client.go # Market data acquisition WebSocket interface\n│ └── combined_streams.go # Market data acquisition: Combined streaming (single link to subscribe to multiple cryptocurrencies)\n│ └── monitor.go # Market data cache\n│ └── types.go # market structure\n\n├── pool/ # Coin pool management\n│ └── coin_pool.go # AI500 + OI Top merged pool\n│\n├── logger/ # Logging system\n│ └── decision_logger.go # Decision recording + performance analysis\n│\n├── decision_logs/ # Decision log storage (JSON files)\n│ ├── {trader_id}/ # Per-trader logs\n│ └── {timestamp}.json # Individual decisions\n│\n└── web/ # React frontend\n ├── src/\n │ ├── components/ # React components\n │ │ ├── EquityChart.tsx # Equity curve chart\n │ │ ├── ComparisonChart.tsx # Multi-AI comparison chart\n │ │ └── CompetitionPage.tsx # Competition leaderboard\n │ ├── lib/api.ts # API call wrapper\n │ ├── types/index.ts # TypeScript types\n │ ├── stores/ # Zustand state management\n │ ├── index.css # Binance-style CSS\n │ └── App.tsx # Main app\n ├── package.json # Frontend dependencies\n └── vite.config.ts # Vite configuration\n```\n\n---\n\n## 🔧 Core Dependencies\n\n### Backend (Go)\n\n| Package | Purpose | Version |\n|---------|---------|---------|\n| `github.com/gin-gonic/gin` | HTTP API framework | v1.9+ |\n| `github.com/adshao/go-binance/v2` | Binance API client | v2.4+ |\n| `github.com/markcheno/go-talib` | Technical indicators (TA-Lib) | Latest |\n| `github.com/mattn/go-sqlite3` | SQLite database driver | v1.14+ |\n| `github.com/golang-jwt/jwt/v5` | JWT authentication | v5.0+ |\n| `github.com/pquerna/otp` | 2FA/TOTP support | v1.4+ |\n| `golang.org/x/crypto` | Password hashing (bcrypt) | Latest |\n\n### Frontend (React + TypeScript)\n\n| Package | Purpose | Version |\n|---------|---------|---------|\n| `react` + `react-dom` | UI framework | 18.3+ |\n| `typescript` | Type safety | 5.8+ |\n| `vite` | Build tool | 6.0+ |\n| `recharts` | Charts (equity, comparison) | 2.15+ |\n| `swr` | Data fetching & caching | 2.2+ |\n| `zustand` | State management | 5.0+ |\n| `tailwindcss` | CSS framework | 3.4+ |\n| `lucide-react` | Icon library | Latest |\n\n---\n\n## 🗂️ System Architecture\n\n### High-Level Overview\n\n```\n┌──────────────────────────────────────────────────────────────────┐\n│ PRESENTATION LAYER │\n│ React SPA (Vite + TypeScript + TailwindCSS) │\n│ - Competition dashboard, trader management UI │\n│ - Real-time charts (Recharts), authentication pages │\n└──────────────────────────────────────────────────────────────────┘\n ↓ HTTP/JSON API\n┌──────────────────────────────────────────────────────────────────┐\n│ API LAYER (Gin Router) │\n│ /api/traders, /api/status, /api/positions, /api/decisions │\n│ Authentication middleware (JWT), CORS handling │\n└──────────────────────────────────────────────────────────────────┘\n ↓\n┌──────────────────────────────────────────────────────────────────┐\n│ BUSINESS LOGIC LAYER │\n│ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐ │\n│ │ TraderManager │ │ DecisionEngine │ │ MarketData │ │\n│ │ - Multi-trader │ │ - AI reasoning │ │ - K-lines │ │\n│ │ orchestration │ │ - Risk control │ │ - Indicators │ │\n│ └──────────────────┘ └──────────────────┘ └────────────────┘ │\n└──────────────────────────────────────────────────────────────────┘\n ↓\n┌──────────────────────────────────────────────────────────────────┐\n│ DATA ACCESS LAYER │\n│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │\n│ │ SQLite DB │ │ File Logger │ │ External APIs │ │\n│ │ - Traders │ │ - Decisions │ │ - Binance │ │\n│ │ - Models │ │ - Performance│ │ - Hyperliquid │ │\n│ │ - Exchanges │ │ analysis │ │ - Aster │ │\n│ └──────────────┘ └──────────────┘ └────────────────────┘ │\n└──────────────────────────────────────────────────────────────────┘\n```\n\n### Component Diagram\n\n*(Coming soon: detailed component interaction diagram)*\n\n---\n\n## 📚 Core Modules\n\n### 1. Trader System (`trader/`)\n\n**Purpose:** Trading execution layer with multi-exchange support\n\n**Key Files:**\n- `auto_trader.go` - Main trading orchestrator (100+ lines)\n- `interface.go` - Unified trader interface\n- `binance_futures.go` - Binance API wrapper\n- `hyperliquid_trader.go` - Hyperliquid DEX wrapper\n- `aster_trader.go` - Aster DEX wrapper\n\n**Design Pattern:** Strategy pattern with interface-based abstraction\n\n**Example:**\n```go\ntype ExchangeClient interface {\n GetAccount() (*AccountInfo, error)\n GetPositions() ([]*Position, error)\n CreateOrder(*OrderParams) (*Order, error)\n // ... more methods\n}\n```\n\n---\n\n### 2. Decision Engine (`decision/`)\n\n**Purpose:** AI-powered trading decision making\n\n**Key Files:**\n- `engine.go` - Decision logic with historical feedback\n- `prompt_manager.go` - Template system for AI prompts\n\n**Features:**\n- Chain-of-Thought reasoning\n- Historical performance analysis\n- Risk-aware decision making\n- Multi-model support (DeepSeek, Qwen, custom)\n\n**Flow:**\n```\nHistorical Data → Prompt Generation → AI API Call →\nDecision Parsing → Risk Validation → Execution\n```\n\n---\n\n### 3. Market Data System (`market/`)\n\n**Purpose:** Fetch and analyze market data\n\n**Key Files:**\n- `data.go` - Market data fetching and technical indicators\n\n**Features:**\n- Multi-timeframe K-line data (3min, 4hour)\n- Technical indicators via TA-Lib:\n - EMA (20, 50)\n - MACD\n - RSI (7, 14)\n - ATR (volatility)\n- Open Interest tracking\n\n---\n\n### 4. Manager (`manager/`)\n\n**Purpose:** Multi-trader orchestration\n\n**Key Files:**\n- `trader_manager.go` - Manages multiple trader instances\n\n**Responsibilities:**\n- Trader lifecycle (start, stop, restart)\n- Resource allocation\n- Concurrent execution coordination\n\n---\n\n### 5. API Server (`api/`)\n\n**Purpose:** HTTP API for frontend communication\n\n**Key Files:**\n- `server.go` - Gin framework RESTful API\n\n**Endpoints:**\n```\nGET /api/traders # List all traders\nPOST /api/traders # Create trader\nPOST /api/traders/:id/start # Start trader\nGET /api/status # System status\nGET /api/positions # Current positions\nGET /api/decisions/latest # Recent decisions\n```\n\n---\n\n### 6. Database Layer (`config/`)\n\n**Purpose:** SQLite data persistence\n\n**Key Files:**\n- `database.go` - Database operations and schema\n\n**Tables:**\n- `users` - User accounts (with 2FA support)\n- `ai_models` - AI model configurations\n- `exchanges` - Exchange credentials\n- `traders` - Trader instances\n- `equity_history` - Performance tracking\n- `system_config` - Application settings\n\n---\n\n### 7. Authentication (`auth/`)\n\n**Purpose:** User authentication and authorization\n\n**Features:**\n- JWT token-based auth\n- 2FA with TOTP (Google Authenticator)\n- Bcrypt password hashing\n- Admin mode (simplified single-user)\n\n---\n\n## 🔄 Request Flow Examples\n\n### Example 1: Create New Trader\n\n```\nUser Action (Frontend)\n ↓\nPOST /api/traders\n ↓\nAPI Server (auth middleware)\n ↓\nDatabase.CreateTrader()\n ↓\nTraderManager.StartTrader()\n ↓\nAutoTrader.Run() → goroutine\n ↓\nResponse: {trader_id, status}\n```\n\n### Example 2: Trading Decision Cycle\n\n```\nAutoTrader (every 3-5 min)\n ↓\n1. FetchAccountStatus()\n ↓\n2. GetOpenPositions()\n ↓\n3. FetchMarketData() → TA-Lib indicators\n ↓\n4. AnalyzeHistory() → last 20 trades\n ↓\n5. GeneratePrompt() → full context\n ↓\n6. CallAI() → DeepSeek/Qwen\n ↓\n7. ParseDecision() → structured output\n ↓\n8. ValidateRisk() → position limits, margin\n ↓\n9. ExecuteOrders() → exchange API\n ↓\n10. LogDecision() → JSON file + database\n```\n\n---\n\n## 📊 Data Flow\n\n### Market Data Flow\n\n```\nExchange API\n ↓\nmarket.FetchKlines()\n ↓\nTA-Lib.Calculate(EMA, MACD, RSI)\n ↓\nDecisionEngine (as context)\n ↓\nAI Model (reasoning)\n```\n\n### Decision Logging Flow\n\n```\nAI Response\n ↓\ndecision_logger.go\n ↓\nJSON file: decision_logs/{trader_id}/{timestamp}.json\n ↓\nDatabase: performance tracking\n ↓\nFrontend: /api/decisions/latest\n```\n\n---\n\n## 🗄️ Database Schema\n\n### Core Tables\n\n**users**\n```sql\n- id (INTEGER PRIMARY KEY)\n- username (TEXT UNIQUE)\n- password_hash (TEXT)\n- totp_secret (TEXT)\n- is_admin (BOOLEAN)\n- created_at (DATETIME)\n```\n\n**ai_models**\n```sql\n- id (INTEGER PRIMARY KEY)\n- name (TEXT)\n- model_type (TEXT) -- deepseek, qwen, custom\n- api_key (TEXT)\n- api_url (TEXT)\n- enabled (BOOLEAN)\n```\n\n**traders**\n```sql\n- id (TEXT PRIMARY KEY)\n- name (TEXT)\n- ai_model_id (INTEGER FK)\n- exchange_id (INTEGER FK)\n- initial_balance (REAL)\n- current_equity (REAL)\n- status (TEXT) -- running, stopped\n- created_at (DATETIME)\n```\n\n*(More details: database-schema.md - coming soon)*\n\n---\n\n## 🔌 API Reference\n\n### Authentication\n\n**POST /api/auth/login**\n```json\nRequest: {\n \"username\": \"string\",\n \"password\": \"string\",\n \"totp_code\": \"string\" // optional\n}\n\nResponse: {\n \"token\": \"jwt_token\",\n \"user\": {...}\n}\n```\n\n### Trader Management\n\n**GET /api/traders**\n```json\nResponse: {\n \"traders\": [\n {\n \"id\": \"string\",\n \"name\": \"string\",\n \"status\": \"running|stopped\",\n \"balance\": 1000.0,\n \"roi\": 5.2\n }\n ]\n}\n```\n\n*(Full API reference: api-reference.md - coming soon)*\n\n---\n\n## 🧪 Testing Architecture\n\n### Current State\n- ⚠️ No unit tests yet\n- ⚠️ Manual testing only\n- ⚠️ Testnet verification\n\n### Planned Testing Strategy\n\n**Unit Tests (Priority 1)**\n```\ntrader/binance_futures_test.go\n- Mock API responses\n- Test precision handling\n- Validate order construction\n```\n\n**Integration Tests (Priority 2)**\n```\n- End-to-end trading flow (testnet)\n- Multi-trader scenarios\n- Database operations\n```\n\n**Frontend Tests (Priority 3)**\n```\n- Component tests (Vitest + React Testing Library)\n- API integration tests\n- E2E tests (Playwright)\n```\n\n*(Testing guide: testing-guide.md - coming soon)*\n\n---\n\n## 🔧 Development Tools\n\n### Build & Run\n\n```bash\n# Backend\ngo build -o nofx\n./nofx\n\n# Frontend\ncd web\nnpm run dev\n\n# Docker\ndocker compose up --build\n```\n\n### Code Quality\n\n```bash\n# Format Go code\ngo fmt ./...\n\n# Lint (if configured)\ngolangci-lint run\n\n# Type check TypeScript\ncd web && npm run build\n```\n\n---\n\n## 📈 Performance Considerations\n\n### Backend\n- **Concurrency:** Each trader runs in separate goroutine\n- **Database:** SQLite (good for <100 traders)\n- **API Rate Limits:** Handled per exchange\n- **Memory:** ~50-100MB per trader\n\n### Frontend\n- **Data Fetching:** SWR with 5-10s polling\n- **State:** Zustand (lightweight)\n- **Bundle Size:** ~500KB (gzipped)\n\n---\n\n## 🔮 Future Architecture Plans\n\n### Planned Improvements\n\n1. **Microservices Split** (if scaling needed)\n - Separate decision engine service\n - Market data service\n - Execution service\n\n2. **Database Migration**\n - Mysql for production (>100 traders)\n - Redis for caching\n\n3. **Event-Driven Architecture**\n - WebSocket for real-time updates\n - Message queue (RabbitMQ/NATS)\n\n4. **Kubernetes Deployment**\n - Helm charts\n - Auto-scaling\n - High availability\n\n---\n\n## 🆘 For Developers\n\n**Want to contribute?**\n- Read [Contributing Guide](../../CONTRIBUTING.md)\n- Check [Open Issues](https://github.com/tinkle-community/nofx/issues)\n- Join [Telegram Community](https://t.me/nofx_dev_community)\n\n**Need clarification?**\n- Open a [GitHub Discussion](https://github.com/tinkle-community/nofx/discussions)\n- Ask in Telegram\n\n---\n\n## 📚 Related Documentation\n\n- [Getting Started](../getting-started/README.md) - Setup and deployment\n- [Contributing](../../CONTRIBUTING.md) - How to contribute\n- [Community](../community/README.md) - Bounties and recognition\n\n---\n\n[← Back to Documentation Home](../README.md)\n"}, "95c32fcb2e1a5ef6d08005478d376f66fc6a1591": {"t": 1785759334.7673624, "n": 14364, "h": "58d70f8ec30eae86", "c": "# 🏗️ NOFX Architecture Documentation\n\n**Language:** [English](README.md) | [中文](README.zh-CN.md)\n\nTechnical documentation for developers who want to understand NOFX internals.\n\n---\n\n## 📋 Overview\n\nNOFX is a full-stack AI trading platform with:\n- **Backend:** Go (Gin framework, SQLite)\n- **Frontend:** React/TypeScript (Vite, TailwindCSS)\n- **Architecture:** Microservice-inspired modular design\n\n---\n\n## 📁 Project Structure\n\n```\nnofx/\n├── main.go # Program entry (multi-trader manager)\n├── config.json # ~~Multi-trader config~~ (Now via web interface)\n├── trading.db # SQLite database (traders, models, exchanges)\n│\n├── api/ # HTTP API service\n│ └── server.go # Gin framework, RESTful API\n│\n├── trader/ # Trading core\n│ ├── auto_trader.go # Auto trading main controller\n│ ├── interface.go # Unified trader interface\n│ ├── binance_futures.go # Binance API wrapper\n│ ├── hyperliquid_trader.go # Hyperliquid DEX wrapper\n│ └── aster_trader.go # Aster DEX wrapper\n│\n├── manager/ # Multi-trader management\n│ └── trader_manager.go # Manages multiple trader instances\n│\n├── config/ # Configuration & database\n│ └── database.go # SQLite operations and schema\n│\n├── auth/ # Authentication\n│ └── jwt.go # JWT token management & 2FA\n│\n├── mcp/ # Model Context Protocol - AI communication\n│ └── client.go # AI API client (DeepSeek/Qwen/Custom)\n│\n├── decision/ # AI decision engine\n│ ├── engine.go # Decision logic with historical feedback\n│ └── prompt_manager.go # Prompt template system\n│\n├── market/ # Market data fetching\n│ └── data.go # Market data & technical indicators (TA-Lib)\n│ └── api_client.go # Market data acquisition API\n│ └── websocket_client.go # Market data acquisition WebSocket interface\n│ └── combined_streams.go # Market data acquisition: Combined streaming (single link to subscribe to multiple cryptocurrencies)\n│ └── monitor.go # Market data cache\n│ └── types.go # market structure\n\n├── pool/ # Coin pool management\n│ └── coin_pool.go # AI500 + OI Top merged pool\n│\n├── logger/ # Logging system\n│ └── decision_logger.go # Decision recording + performance analysis\n│\n├── decision_logs/ # Decision log storage (JSON files)\n│ ├── {trader_id}/ # Per-trader logs\n│ └── {timestamp}.json # Individual decisions\n│\n└── web/ # React frontend\n ├── src/\n │ ├── components/ # React components\n │ │ ├── EquityChart.tsx # Equity curve chart\n │ │ ├── ComparisonChart.tsx # Multi-AI comparison chart\n │ │ └── CompetitionPage.tsx # Competition leaderboard\n │ ├── lib/api.ts # API call wrapper\n │ ├── types/index.ts # TypeScript types\n │ ├── stores/ # Zustand state management\n │ ├── index.css # Binance-style CSS\n │ └── App.tsx # Main app\n ├── package.json # Frontend dependencies\n └── vite.config.ts # Vite configuration\n```\n\n---\n\n## 🔧 Core Dependencies\n\n### Backend (Go)\n\n| Package | Purpose | Version |\n|---------|---------|---------|\n| `github.com/gin-gonic/gin` | HTTP API framework | v1.9+ |\n| `github.com/adshao/go-binance/v2` | Binance API client | v2.4+ |\n| `github.com/markcheno/go-talib` | Technical indicators (TA-Lib) | Latest |\n| `github.com/mattn/go-sqlite3` | SQLite database driver | v1.14+ |\n| `github.com/golang-jwt/jwt/v5` | JWT authentication | v5.0+ |\n| `github.com/pquerna/otp` | 2FA/TOTP support | v1.4+ |\n| `golang.org/x/crypto` | Password hashing (bcrypt) | Latest |\n\n### Frontend (React + TypeScript)\n\n| Package | Purpose | Version |\n|---------|---------|---------|\n| `react` + `react-dom` | UI framework | 18.3+ |\n| `typescript` | Type safety | 5.8+ |\n| `vite` | Build tool | 6.0+ |\n| `recharts` | Charts (equity, comparison) | 2.15+ |\n| `swr` | Data fetching & caching | 2.2+ |\n| `zustand` | State management | 5.0+ |\n| `tailwindcss` | CSS framework | 3.4+ |\n| `lucide-react` | Icon library | Latest |\n\n---\n\n## 🗂️ System Architecture\n\n### High-Level Overview\n\n```\n┌──────────────────────────────────────────────────────────────────┐\n│ PRESENTATION LAYER │\n│ React SPA (Vite + TypeScript + TailwindCSS) │\n│ - Competition dashboard, trader management UI │\n│ - Real-time charts (Recharts), authentication pages │\n└──────────────────────────────────────────────────────────────────┘\n ↓ HTTP/JSON API\n┌──────────────────────────────────────────────────────────────────┐\n│ API LAYER (Gin Router) │\n│ /api/traders, /api/status, /api/positions, /api/decisions │\n│ Authentication middleware (JWT), CORS handling │\n└──────────────────────────────────────────────────────────────────┘\n ↓\n┌──────────────────────────────────────────────────────────────────┐\n│ BUSINESS LOGIC LAYER │\n│ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐ │\n│ │ TraderManager │ │ DecisionEngine │ │ MarketData │ │\n│ │ - Multi-trader │ │ - AI reasoning │ │ - K-lines │ │\n│ │ orchestration │ │ - Risk control │ │ - Indicators │ │\n│ └──────────────────┘ └──────────────────┘ └────────────────┘ │\n└──────────────────────────────────────────────────────────────────┘\n ↓\n┌──────────────────────────────────────────────────────────────────┐\n│ DATA ACCESS LAYER │\n│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │\n│ │ SQLite DB │ │ File Logger │ │ External APIs │ │\n│ │ - Traders │ │ - Decisions │ │ - Binance │ │\n│ │ - Models │ │ - Performance│ │ - Hyperliquid │ │\n│ │ - Exchanges │ │ analysis │ │ - Aster │ │\n│ └──────────────┘ └──────────────┘ └────────────────────┘ │\n└──────────────────────────────────────────────────────────────────┘\n```\n\n### Component Diagram\n\n*(Coming soon: detailed component interaction diagram)*\n\n---\n\n## 📚 Core Modules\n\n### 1. Trader System (`trader/`)\n\n**Purpose:** Trading execution layer with multi-exchange support\n\n**Key Files:**\n- `auto_trader.go` - Main trading orchestrator (100+ lines)\n- `interface.go` - Unified trader interface\n- `binance_futures.go` - Binance API wrapper\n- `hyperliquid_trader.go` - Hyperliquid DEX wrapper\n- `aster_trader.go` - Aster DEX wrapper\n\n**Design Pattern:** Strategy pattern with interface-based abstraction\n\n**Example:**\n```go\ntype ExchangeClient interface {\n GetAccount() (*AccountInfo, error)\n GetPositions() ([]*Position, error)\n CreateOrder(*OrderParams) (*Order, error)\n // ... more methods\n}\n```\n\n---\n\n### 2. Decision Engine (`decision/`)\n\n**Purpose:** AI-powered trading decision making\n\n**Key Files:**\n- `engine.go` - Decision logic with historical feedback\n- `prompt_manager.go` - Template system for AI prompts\n\n**Features:**\n- Chain-of-Thought reasoning\n- Historical performance analysis\n- Risk-aware decision making\n- Multi-model support (DeepSeek, Qwen, custom)\n\n**Flow:**\n```\nHistorical Data → Prompt Generation → AI API Call →\nDecision Parsing → Risk Validation → Execution\n```\n\n---\n\n### 3. Market Data System (`market/`)\n\n**Purpose:** Fetch and analyze market data\n\n**Key Files:**\n- `data.go` - Market data fetching and technical indicators\n\n**Features:**\n- Multi-timeframe K-line data (3min, 4hour)\n- Technical indicators via TA-Lib:\n - EMA (20, 50)\n - MACD\n - RSI (7, 14)\n - ATR (volatility)\n- Open Interest tracking\n\n---\n\n### 4. Manager (`manager/`)\n\n**Purpose:** Multi-trader orchestration\n\n**Key Files:**\n- `trader_manager.go` - Manages multiple trader instances\n\n**Responsibilities:**\n- Trader lifecycle (start, stop, restart)\n- Resource allocation\n- Concurrent execution coordination\n\n---\n\n### 5. API Server (`api/`)\n\n**Purpose:** HTTP API for frontend communication\n\n**Key Files:**\n- `server.go` - Gin framework RESTful API\n\n**Endpoints:**\n```\nGET /api/traders # List all traders\nPOST /api/traders # Create trader\nPOST /api/traders/:id/start # Start trader\nGET /api/status # System status\nGET /api/positions # Current positions\nGET /api/decisions/latest # Recent decisions\n```\n\n---\n\n### 6. Database Layer (`config/`)\n\n**Purpose:** SQLite data persistence\n\n**Key Files:**\n- `database.go` - Database operations and schema\n\n**Tables:**\n- `users` - User accounts (with 2FA support)\n- `ai_models` - AI model configurations\n- `exchanges` - Exchange credentials\n- `traders` - Trader instances\n- `equity_history` - Performance tracking\n- `system_config` - Application settings\n\n---\n\n### 7. Authentication (`auth/`)\n\n**Purpose:** User authentication and authorization\n\n**Features:**\n- JWT token-based auth\n- 2FA with TOTP (Google Authenticator)\n- Bcrypt password hashing\n- Admin mode (simplified single-user)\n\n---\n\n## 🔄 Request Flow Examples\n\n### Example 1: Create New Trader\n\n```\nUser Action (Frontend)\n ↓\nPOST /api/traders\n ↓\nAPI Server (auth middleware)\n ↓\nDatabase.CreateTrader()\n ↓\nTraderManager.StartTrader()\n ↓\nAutoTrader.Run() → goroutine\n ↓\nResponse: {trader_id, status}\n```\n\n### Example 2: Trading Decision Cycle\n\n```\nAutoTrader (every 3-5 min)\n ↓\n1. FetchAccountStatus()\n ↓\n2. GetOpenPositions()\n ↓\n3. FetchMarketData() → TA-Lib indicators\n ↓\n4. AnalyzeHistory() → last 20 trades\n ↓\n5. GeneratePrompt() → full context\n ↓\n6. CallAI() → DeepSeek/Qwen\n ↓\n7. ParseDecision() → structured output\n ↓\n8. ValidateRisk() → position limits, margin\n ↓\n9. ExecuteOrders() → exchange API\n ↓\n10. LogDecision() → JSON file + database\n```\n\n---\n\n## 📊 Data Flow\n\n### Market Data Flow\n\n```\nExchange API\n ↓\nmarket.FetchKlines()\n ↓\nTA-Lib.Calculate(EMA, MACD, RSI)\n ↓\nDecisionEngine (as context)\n ↓\nAI Model (reasoning)\n```\n\n### Decision Logging Flow\n\n```\nAI Response\n ↓\ndecision_logger.go\n ↓\nJSON file: decision_logs/{trader_id}/{timestamp}.json\n ↓\nDatabase: performance tracking\n ↓\nFrontend: /api/decisions/latest\n```\n\n---\n\n## 🗄️ Database Schema\n\n### Core Tables\n\n**users**\n```sql\n- id (INTEGER PRIMARY KEY)\n- username (TEXT UNIQUE)\n- password_hash (TEXT)\n- totp_secret (TEXT)\n- is_admin (BOOLEAN)\n- created_at (DATETIME)\n```\n\n**ai_models**\n```sql\n- id (INTEGER PRIMARY KEY)\n- name (TEXT)\n- model_type (TEXT) -- deepseek, qwen, custom\n- api_key (TEXT)\n- api_url (TEXT)\n- enabled (BOOLEAN)\n```\n\n**traders**\n```sql\n- id (TEXT PRIMARY KEY)\n- name (TEXT)\n- ai_model_id (INTEGER FK)\n- exchange_id (INTEGER FK)\n- initial_balance (REAL)\n- current_equity (REAL)\n- status (TEXT) -- running, stopped\n- created_at (DATETIME)\n```\n\n*(More details: database-schema.md - coming soon)*\n\n---\n\n## 🔌 API Reference\n\n### Authentication\n\n**POST /api/auth/login**\n```json\nRequest: {\n \"username\": \"string\",\n \"password\": \"string\",\n \"totp_code\": \"string\" // optional\n}\n\nResponse: {\n \"token\": \"jwt_token\",\n \"user\": {...}\n}\n```\n\n### Trader Management\n\n**GET /api/traders**\n```json\nResponse: {\n \"traders\": [\n {\n \"id\": \"string\",\n \"name\": \"string\",\n \"status\": \"running|stopped\",\n \"balance\": 1000.0,\n \"roi\": 5.2\n }\n ]\n}\n```\n\n*(Full API reference: api-reference.md - coming soon)*\n\n---\n\n## 🧪 Testing Architecture\n\n### Current State\n- ⚠️ No unit tests yet\n- ⚠️ Manual testing only\n- ⚠️ Testnet verification\n\n### Planned Testing Strategy\n\n**Unit Tests (Priority 1)**\n```\ntrader/binance_futures_test.go\n- Mock API responses\n- Test precision handling\n- Validate order construction\n```\n\n**Integration Tests (Priority 2)**\n```\n- End-to-end trading flow (testnet)\n- Multi-trader scenarios\n- Database operations\n```\n\n**Frontend Tests (Priority 3)**\n```\n- Component tests (Vitest + React Testing Library)\n- API integration tests\n- E2E tests (Playwright)\n```\n\n*(Testing guide: testing-guide.md - coming soon)*\n\n---\n\n## 🔧 Development Tools\n\n### Build & Run\n\n```bash\n# Backend\ngo build -o nofx\n./nofx\n\n# Frontend\ncd web\nnpm run dev\n\n# Docker\ndocker compose up --build\n```\n\n### Code Quality\n\n```bash\n# Format Go code\ngo fmt ./...\n\n# Lint (if configured)\ngolangci-lint run\n\n# Type check TypeScript\ncd web && npm run build\n```\n\n---\n\n## 📈 Performance Considerations\n\n### Backend\n- **Concurrency:** Each trader runs in separate goroutine\n- **Database:** SQLite (good for <100 traders)\n- **API Rate Limits:** Handled per exchange\n- **Memory:** ~50-100MB per trader\n\n### Frontend\n- **Data Fetching:** SWR with 5-10s polling\n- **State:** Zustand (lightweight)\n- **Bundle Size:** ~500KB (gzipped)\n\n---\n\n## 🔮 Future Architecture Plans\n\n### Planned Improvements\n\n1. **Microservices Split** (if scaling needed)\n - Separate decision engine service\n - Market data service\n - Execution service\n\n2. **Database Migration**\n - Mysql for production (>100 traders)\n - Redis for caching\n\n3. **Event-Driven Architecture**\n - WebSocket for real-time updates\n - Message queue (RabbitMQ/NATS)\n\n4. **Kubernetes Deployment**\n - Helm charts\n - Auto-scaling\n - High availability\n\n---\n\n## 🆘 For Developers\n\n**Want to contribute?**\n- Read [Contributing Guide](../../CONTRIBUTING.md)\n- Check [Open Issues](https://github.com/tinkle-community/nofx/issues)\n- Join [Telegram Community](https://t.me/nofx_dev_community)\n\n**Need clarification?**\n- Open a [GitHub Discussion](https://github.com/tinkle-community/nofx/discussions)\n- Ask in Telegram\n\n---\n\n## 📚 Related Documentation\n\n- [Getting Started](../getting-started/README.md) - Setup and deployment\n- [Contributing](../../CONTRIBUTING.md) - How to contribute\n- [Community](../community/README.md) - Bounties and recognition\n\n---\n\n[← Back to Documentation Home](../README.md)\n"}, "18820fc319c8e7546dba2bb6ce2d0042c4d59345": {"t": 1785759336.385803, "n": 13968, "h": "6ce74d604dee2322", "c": "# 🏗️ NOFX Architecture Documentation\n\n**Language:** [English](README.md) | [中文](README.zh-CN.md)\n\nTechnical documentation for developers who want to understand NOFX internals.\n\n---\n\n## 📋 Overview\n\nNOFX is a full-stack AI trading platform with:\n- **Backend:** Go (Gin framework, SQLite)\n- **Frontend:** React/TypeScript (Vite, TailwindCSS)\n- **Architecture:** Microservice-inspired modular design\n\n---\n\n## 📁 Project Structure\n\n```\nnofx/\n├── main.go # Program entry (multi-trader manager)\n├── config.json # ~~Multi-trader config~~ (Now via web interface)\n├── trading.db # SQLite database (traders, models, exchanges)\n│\n├── api/ # HTTP API service\n│ └── server.go # Gin framework, RESTful API\n│\n├── trader/ # Trading core\n│ ├── auto_trader.go # Auto trading main controller\n│ ├── interface.go # Unified trader interface\n│ ├── binance_futures.go # Binance API wrapper\n│ ├── hyperliquid_trader.go # Hyperliquid DEX wrapper\n│ └── aster_trader.go # Aster DEX wrapper\n│\n├── manager/ # Multi-trader management\n│ └── trader_manager.go # Manages multiple trader instances\n│\n├── config/ # Configuration & database\n│ └── database.go # SQLite operations and schema\n│\n├── auth/ # Authentication\n│ └── jwt.go # JWT token management & 2FA\n│\n├── mcp/ # Model Context Protocol - AI communication\n│ └── client.go # AI API client (DeepSeek/Qwen/Custom)\n│\n├── decision/ # AI decision engine\n│ ├── engine.go # Decision logic with historical feedback\n│ └── prompt_manager.go # Prompt template system\n│\n├── market/ # Market data fetching\n│ └── data.go # Market data & technical indicators (TA-Lib)\n│\n├── pool/ # Coin pool management\n│ └── coin_pool.go # AI500 + OI Top merged pool\n│\n├── logger/ # Logging system\n│ └── decision_logger.go # Decision recording + performance analysis\n│\n├── decision_logs/ # Decision log storage (JSON files)\n│ ├── {trader_id}/ # Per-trader logs\n│ └── {timestamp}.json # Individual decisions\n│\n└── web/ # React frontend\n ├── src/\n │ ├── components/ # React components\n │ │ ├── EquityChart.tsx # Equity curve chart\n │ │ ├── ComparisonChart.tsx # Multi-AI comparison chart\n │ │ └── CompetitionPage.tsx # Competition leaderboard\n │ ├── lib/api.ts # API call wrapper\n │ ├── types/index.ts # TypeScript types\n │ ├── stores/ # Zustand state management\n │ ├── index.css # Binance-style CSS\n │ └── App.tsx # Main app\n ├── package.json # Frontend dependencies\n └── vite.config.ts # Vite configuration\n```\n\n---\n\n## 🔧 Core Dependencies\n\n### Backend (Go)\n\n| Package | Purpose | Version |\n|---------|---------|---------|\n| `github.com/gin-gonic/gin` | HTTP API framework | v1.9+ |\n| `github.com/adshao/go-binance/v2` | Binance API client | v2.4+ |\n| `github.com/markcheno/go-talib` | Technical indicators (TA-Lib) | Latest |\n| `github.com/mattn/go-sqlite3` | SQLite database driver | v1.14+ |\n| `github.com/golang-jwt/jwt/v5` | JWT authentication | v5.0+ |\n| `github.com/pquerna/otp` | 2FA/TOTP support | v1.4+ |\n| `golang.org/x/crypto` | Password hashing (bcrypt) | Latest |\n\n### Frontend (React + TypeScript)\n\n| Package | Purpose | Version |\n|---------|---------|---------|\n| `react` + `react-dom` | UI framework | 18.3+ |\n| `typescript` | Type safety | 5.8+ |\n| `vite` | Build tool | 6.0+ |\n| `recharts` | Charts (equity, comparison) | 2.15+ |\n| `swr` | Data fetching & caching | 2.2+ |\n| `zustand` | State management | 5.0+ |\n| `tailwindcss` | CSS framework | 3.4+ |\n| `lucide-react` | Icon library | Latest |\n\n---\n\n## 🗂️ System Architecture\n\n### High-Level Overview\n\n```\n┌──────────────────────────────────────────────────────────────────┐\n│ PRESENTATION LAYER │\n│ React SPA (Vite + TypeScript + TailwindCSS) │\n│ - Competition dashboard, trader management UI │\n│ - Real-time charts (Recharts), authentication pages │\n└──────────────────────────────────────────────────────────────────┘\n ↓ HTTP/JSON API\n┌──────────────────────────────────────────────────────────────────┐\n│ API LAYER (Gin Router) │\n│ /api/traders, /api/status, /api/positions, /api/decisions │\n│ Authentication middleware (JWT), CORS handling │\n└──────────────────────────────────────────────────────────────────┘\n ↓\n┌──────────────────────────────────────────────────────────────────┐\n│ BUSINESS LOGIC LAYER │\n│ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐ │\n│ │ TraderManager │ │ DecisionEngine │ │ MarketData │ │\n│ │ - Multi-trader │ │ - AI reasoning │ │ - K-lines │ │\n│ │ orchestration │ │ - Risk control │ │ - Indicators │ │\n│ └──────────────────┘ └──────────────────┘ └────────────────┘ │\n└──────────────────────────────────────────────────────────────────┘\n ↓\n┌──────────────────────────────────────────────────────────────────┐\n│ DATA ACCESS LAYER │\n│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │\n│ │ SQLite DB │ │ File Logger │ │ External APIs │ │\n│ │ - Traders │ │ - Decisions │ │ - Binance │ │\n│ │ - Models │ │ - Performance│ │ - Hyperliquid │ │\n│ │ - Exchanges │ │ analysis │ │ - Aster │ │\n│ └──────────────┘ └──────────────┘ └────────────────────┘ │\n└──────────────────────────────────────────────────────────────────┘\n```\n\n### Component Diagram\n\n*(Coming soon: detailed component interaction diagram)*\n\n---\n\n## 📚 Core Modules\n\n### 1. Trader System (`trader/`)\n\n**Purpose:** Trading execution layer with multi-exchange support\n\n**Key Files:**\n- `auto_trader.go` - Main trading orchestrator (100+ lines)\n- `interface.go` - Unified trader interface\n- `binance_futures.go` - Binance API wrapper\n- `hyperliquid_trader.go` - Hyperliquid DEX wrapper\n- `aster_trader.go` - Aster DEX wrapper\n\n**Design Pattern:** Strategy pattern with interface-based abstraction\n\n**Example:**\n```go\ntype ExchangeClient interface {\n GetAccount() (*AccountInfo, error)\n GetPositions() ([]*Position, error)\n CreateOrder(*OrderParams) (*Order, error)\n // ... more methods\n}\n```\n\n---\n\n### 2. Decision Engine (`decision/`)\n\n**Purpose:** AI-powered trading decision making\n\n**Key Files:**\n- `engine.go` - Decision logic with historical feedback\n- `prompt_manager.go` - Template system for AI prompts\n\n**Features:**\n- Chain-of-Thought reasoning\n- Historical performance analysis\n- Risk-aware decision making\n- Multi-model support (DeepSeek, Qwen, custom)\n\n**Flow:**\n```\nHistorical Data → Prompt Generation → AI API Call →\nDecision Parsing → Risk Validation → Execution\n```\n\n---\n\n### 3. Market Data System (`market/`)\n\n**Purpose:** Fetch and analyze market data\n\n**Key Files:**\n- `data.go` - Market data fetching and technical indicators\n\n**Features:**\n- Multi-timeframe K-line data (3min, 4hour)\n- Technical indicators via TA-Lib:\n - EMA (20, 50)\n - MACD\n - RSI (7, 14)\n - ATR (volatility)\n- Open Interest tracking\n\n---\n\n### 4. Manager (`manager/`)\n\n**Purpose:** Multi-trader orchestration\n\n**Key Files:**\n- `trader_manager.go` - Manages multiple trader instances\n\n**Responsibilities:**\n- Trader lifecycle (start, stop, restart)\n- Resource allocation\n- Concurrent execution coordination\n\n---\n\n### 5. API Server (`api/`)\n\n**Purpose:** HTTP API for frontend communication\n\n**Key Files:**\n- `server.go` - Gin framework RESTful API\n\n**Endpoints:**\n```\nGET /api/traders # List all traders\nPOST /api/traders # Create trader\nPOST /api/traders/:id/start # Start trader\nGET /api/status # System status\nGET /api/positions # Current positions\nGET /api/decisions/latest # Recent decisions\n```\n\n---\n\n### 6. Database Layer (`config/`)\n\n**Purpose:** SQLite data persistence\n\n**Key Files:**\n- `database.go` - Database operations and schema\n\n**Tables:**\n- `users` - User accounts (with 2FA support)\n- `ai_models` - AI model configurations\n- `exchanges` - Exchange credentials\n- `traders` - Trader instances\n- `equity_history` - Performance tracking\n- `system_config` - Application settings\n\n---\n\n### 7. Authentication (`auth/`)\n\n**Purpose:** User authentication and authorization\n\n**Features:**\n- JWT token-based auth\n- 2FA with TOTP (Google Authenticator)\n- Bcrypt password hashing\n- Admin mode (simplified single-user)\n\n---\n\n## 🔄 Request Flow Examples\n\n### Example 1: Create New Trader\n\n```\nUser Action (Frontend)\n ↓\nPOST /api/traders\n ↓\nAPI Server (auth middleware)\n ↓\nDatabase.CreateTrader()\n ↓\nTraderManager.StartTrader()\n ↓\nAutoTrader.Run() → goroutine\n ↓\nResponse: {trader_id, status}\n```\n\n### Example 2: Trading Decision Cycle\n\n```\nAutoTrader (every 3-5 min)\n ↓\n1. FetchAccountStatus()\n ↓\n2. GetOpenPositions()\n ↓\n3. FetchMarketData() → TA-Lib indicators\n ↓\n4. AnalyzeHistory() → last 20 trades\n ↓\n5. GeneratePrompt() → full context\n ↓\n6. CallAI() → DeepSeek/Qwen\n ↓\n7. ParseDecision() → structured output\n ↓\n8. ValidateRisk() → position limits, margin\n ↓\n9. ExecuteOrders() → exchange API\n ↓\n10. LogDecision() → JSON file + database\n```\n\n---\n\n## 📊 Data Flow\n\n### Market Data Flow\n\n```\nExchange API\n ↓\nmarket.FetchKlines()\n ↓\nTA-Lib.Calculate(EMA, MACD, RSI)\n ↓\nDecisionEngine (as context)\n ↓\nAI Model (reasoning)\n```\n\n### Decision Logging Flow\n\n```\nAI Response\n ↓\ndecision_logger.go\n ↓\nJSON file: decision_logs/{trader_id}/{timestamp}.json\n ↓\nDatabase: performance tracking\n ↓\nFrontend: /api/decisions/latest\n```\n\n---\n\n## 🗄️ Database Schema\n\n### Core Tables\n\n**users**\n```sql\n- id (INTEGER PRIMARY KEY)\n- username (TEXT UNIQUE)\n- password_hash (TEXT)\n- totp_secret (TEXT)\n- is_admin (BOOLEAN)\n- created_at (DATETIME)\n```\n\n**ai_models**\n```sql\n- id (INTEGER PRIMARY KEY)\n- name (TEXT)\n- model_type (TEXT) -- deepseek, qwen, custom\n- api_key (TEXT)\n- api_url (TEXT)\n- enabled (BOOLEAN)\n```\n\n**traders**\n```sql\n- id (TEXT PRIMARY KEY)\n- name (TEXT)\n- ai_model_id (INTEGER FK)\n- exchange_id (INTEGER FK)\n- initial_balance (REAL)\n- current_equity (REAL)\n- status (TEXT) -- running, stopped\n- created_at (DATETIME)\n```\n\n*(More details: database-schema.md - coming soon)*\n\n---\n\n## 🔌 API Reference\n\n### Authentication\n\n**POST /api/auth/login**\n```json\nRequest: {\n \"username\": \"string\",\n \"password\": \"string\",\n \"totp_code\": \"string\" // optional\n}\n\nResponse: {\n \"token\": \"jwt_token\",\n \"user\": {...}\n}\n```\n\n### Trader Management\n\n**GET /api/traders**\n```json\nResponse: {\n \"traders\": [\n {\n \"id\": \"string\",\n \"name\": \"string\",\n \"status\": \"running|stopped\",\n \"balance\": 1000.0,\n \"roi\": 5.2\n }\n ]\n}\n```\n\n*(Full API reference: api-reference.md - coming soon)*\n\n---\n\n## 🧪 Testing Architecture\n\n### Current State\n- ⚠️ No unit tests yet\n- ⚠️ Manual testing only\n- ⚠️ Testnet verification\n\n### Planned Testing Strategy\n\n**Unit Tests (Priority 1)**\n```\ntrader/binance_futures_test.go\n- Mock API responses\n- Test precision handling\n- Validate order construction\n```\n\n**Integration Tests (Priority 2)**\n```\n- End-to-end trading flow (testnet)\n- Multi-trader scenarios\n- Database operations\n```\n\n**Frontend Tests (Priority 3)**\n```\n- Component tests (Vitest + React Testing Library)\n- API integration tests\n- E2E tests (Playwright)\n```\n\n*(Testing guide: testing-guide.md - coming soon)*\n\n---\n\n## 🔧 Development Tools\n\n### Build & Run\n\n```bash\n# Backend\ngo build -o nofx\n./nofx\n\n# Frontend\ncd web\nnpm run dev\n\n# Docker\ndocker compose up --build\n```\n\n### Code Quality\n\n```bash\n# Format Go code\ngo fmt ./...\n\n# Lint (if configured)\ngolangci-lint run\n\n# Type check TypeScript\ncd web && npm run build\n```\n\n---\n\n## 📈 Performance Considerations\n\n### Backend\n- **Concurrency:** Each trader runs in separate goroutine\n- **Database:** SQLite (good for <100 traders)\n- **API Rate Limits:** Handled per exchange\n- **Memory:** ~50-100MB per trader\n\n### Frontend\n- **Data Fetching:** SWR with 5-10s polling\n- **State:** Zustand (lightweight)\n- **Bundle Size:** ~500KB (gzipped)\n\n---\n\n## 🔮 Future Architecture Plans\n\n### Planned Improvements\n\n1. **Microservices Split** (if scaling needed)\n - Separate decision engine service\n - Market data service\n - Execution service\n\n2. **Database Migration**\n - Mysql for production (>100 traders)\n - Redis for caching\n\n3. **Event-Driven Architecture**\n - WebSocket for real-time updates\n - Message queue (RabbitMQ/NATS)\n\n4. **Kubernetes Deployment**\n - Helm charts\n - Auto-scaling\n - High availability\n\n---\n\n## 🆘 For Developers\n\n**Want to contribute?**\n- Read [Contributing Guide](../../CONTRIBUTING.md)\n- Check [Open Issues](https://github.com/tinkle-community/nofx/issues)\n- Join [Telegram Community](https://t.me/nofx_dev_community)\n\n**Need clarification?**\n- Open a [GitHub Discussion](https://github.com/tinkle-community/nofx/discussions)\n- Ask in Telegram\n\n---\n\n## 📚 Related Documentation\n\n- [Getting Started](../getting-started/README.md) - Setup and deployment\n- [Contributing](../../CONTRIBUTING.md) - How to contribute\n- [Community](../community/README.md) - Bounties and recognition\n\n---\n\n[← Back to Documentation Home](../README.md)\n"}}}}