1 line
18 KiB
JSON
1 line
18 KiB
JSON
{"repo": "abstractlabz/libra", "files": {"pipeline/api/llm/inferencer.go": {"95dbe10ef39ebaa0b0843672b189d756b010e84a": {"t": 1785746091.3914664, "n": 8006, "h": "f7ac5c80184c53e3", "c": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/0xPCDefenders/LIBRA/utils\"\n\t\"github.com/joho/godotenv\"\n\t\"github.com/segmentio/kafka-go\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n)\n\ntype LLMMessage struct {\n\tTopic string `json:\"topic\"`\n\tSegment string `json:\"segment\"`\n\tTicker string `json:\"ticker\"`\n\tData interface{} `json:\"data\"`\n\tTimestamp int64 `json:\"timestamp\"`\n\tPromptTemplate string `json:\"prompt_template\"`\n}\n\ntype OpenAIRequest struct {\n\tModel string `json:\"model\"`\n\tMessages []ChatMessage `json:\"messages\"`\n\tTemperature float64 `json:\"temperature\"`\n}\n\ntype ChatMessage struct {\n\tRole string `json:\"role\"`\n\tContent string `json:\"content\"`\n}\n\ntype OpenAIResponse struct {\n\tChoices []struct {\n\t\tMessage ChatMessage `json:\"message\"`\n\t} `json:\"choices\"`\n}\n\ntype DeepSeekRequest struct {\n\tModel string `json:\"model\"`\n\tMessages []ChatMessage `json:\"messages\"`\n\tStream bool `json:\"stream\"`\n}\n\ntype Allocation struct {\n\tTicker string `json:\"ticker\"`\n\tOptimizedWeight float64 `json:\"optimized_weight\"`\n\tLLMAnalysis string `json:\"llm_analysis\"`\n\tValueDifference float64 `json:\"value_difference\"`\n}\n\ntype Portfolio struct {\n\tID string `json:\"_id\"`\n\tUserID string `json:\"userId\"`\n\tName string `json:\"name\"`\n\tHoldings []Holding `json:\"holdings\"`\n\tPolicy Policy `json:\"policy\"`\n\tLastUpdated string `json:\"lastUpdated\"`\n}\n\ntype Holding struct {\n\tSymbol string `json:\"symbol\"`\n\tName string `json:\"name\"`\n\tQuantity int `json:\"quantity\"`\n\tCostBasis float64 `json:\"costBasis\"`\n\tCurrentPrice float64 `json:\"currentPrice\"`\n\tCurrency string `json:\"currency\"`\n}\n\ntype Policy struct {\n\tUserID string `json:\"userId\"`\n\tRiskTolerance float64 `json:\"riskTolerance\"`\n\tInvestmentHorizon int `json:\"investmentHorizon\"`\n\tBrokerType int `json:\"brokerType\"`\n\tTargetAllocation TargetAllocation `json:\"targetAllocation\"`\n\tRebalanceFrequency string `json:\"rebalanceFrequency\"`\n}\n\ntype TargetAllocation struct {\n\tUserID string `json:\"userId\"`\n\tName string `json:\"name\"`\n\tAllocations interface{} `json:\"allocations\"`\n\tLastUpdated string `json:\"lastUpdated\"`\n}\n\nfunc callDeepSeekAPI(prompt string) (string, error) {\n\tapiKey := os.Getenv(\"DEEPSEEK_API_KEY\")\n\tif apiKey == \"\" {\n\t\treturn \"\", fmt.Errorf(\"DEEPSEEK_API_KEY not set in environment\")\n\t}\n\n\turl := \"https://api.deepseek.com/chat/completions\"\n\n\trequest := DeepSeekRequest{\n\t\tModel: \"deepseek-chat\",\n\t\tMessages: []ChatMessage{\n\t\t\t{\n\t\t\t\tRole: \"system\",\n\t\t\t\tContent: \"Look at the following portfolio holding data for the user and provide a detailed analysis of the portfolio's performance, key metrics, and potential risks or opportunities with the ticker symbol in relation to fundamentals and sentiment.\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tRole: \"user\",\n\t\t\t\tContent: prompt,\n\t\t\t},\n\t\t},\n\t\tStream: false,\n\t}\n\n\tjsonData, err := json.Marshal(request)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"error marshaling request: %v\", err)\n\t}\n\n\t// Implement retry logic\n\tmaxRetries := 3\n\tfor attempt := 1; attempt <= maxRetries; attempt++ {\n\t\treq, err := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"error creating request: %v\", err)\n\t\t}\n\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\treq.Header.Set(\"Authorization\", \"Bearer \"+apiKey)\n\n\t\t// Increased timeout to 90 seconds\n\t\tclient := &http.Client{Timeout: 90 * time.Second}\n\n\t\t// Add context with timeout\n\t\tctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)\n\t\tdefer cancel()\n\t\treq = req.WithContext(ctx)\n\n\t\tresp, err := client.Do(req)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Attempt %d failed: %v\", attempt, err)\n\t\t\tif attempt == maxRetries {\n\t\t\t\treturn \"\", fmt.Errorf(\"max retries reached: %v\", err)\n\t\t\t}\n\t\t\ttime.Sleep(time.Second * time.Duration(attempt)) // Exponential backoff\n\t\t\tcontinue\n\t\t}\n\t\tdefer resp.Body.Close()\n\n\t\tbody, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Attempt %d failed reading body: %v\", attempt, err)\n\t\t\tif attempt == maxRetries {\n\t\t\t\treturn \"\", fmt.Errorf(\"max retries reached: %v\", err)\n\t\t\t}\n\t\t\ttime.Sleep(time.Second * time.Duration(attempt))\n\t\t\tcontinue\n\t\t}\n\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tlog.Printf(\"Attempt %d failed with status %d: %s\", attempt, resp.StatusCode, string(body))\n\t\t\tif attempt == maxRetries {\n\t\t\t\treturn \"\", fmt.Errorf(\"API request failed with status %d: %s\", resp.StatusCode, string(body))\n\t\t\t}\n\t\t\ttime.Sleep(time.Second * time.Duration(attempt))\n\t\t\tcontinue\n\t\t}\n\n\t\tvar response OpenAIResponse\n\t\tif err := json.Unmarshal(body, &response); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"error unmarshaling response: %v\", err)\n\t\t}\n\n\t\tif len(response.Choices) == 0 {\n\t\t\treturn \"\", fmt.Errorf(\"no response choices received\")\n\t\t}\n\n\t\treturn response.Choices[0].Message.Content, nil\n\t}\n\n\treturn \"\", fmt.Errorf(\"failed after %d retries\", maxRetries)\n}\n\nfunc processLLMMessage(job interface{}) interface{} {\n\tmessage := job.(string)\n\n\t// Add debug logging\n\tlog.Printf(\"Received data: %v\", message)\n\n\t// Parse portfolio data directly from the JSON string\n\tvar portfolio Portfolio\n\tif err := json.Unmarshal([]byte(message), &portfolio); err != nil {\n\t\tlog.Printf(\"Error unmarshaling portfolio data: %v\", err)\n\t\treturn nil\n\t}\n\n\t// Create allocations slice\n\tvar allocations []Allocation\n\n\t// Process each holding\n\tfor _, holding := range portfolio.Holdings {\n\t\t// Prepare prompt for DeepSeek\n\t\tprompt := fmt.Sprintf(\"Analyze the following financial data and provide detailed insights about the company's financial health, key metrics, and potential risks or opportunities. %s\", holding.Symbol, holding)\n\n\t\t// Call DeepSeek API\n\t\tanalysis, err := callDeepSeekAPI(prompt)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error calling DeepSeek API for %s: %v\", holding.Symbol, err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Create allocation object\n\t\tallocation := Allocation{\n\t\t\tTicker: holding.Symbol,\n\t\t\tOptimizedWeight: holding.CurrentPrice,\n\t\t\tLLMAnalysis: analysis,\n\t\t\tValueDifference: 0, // Calculate based on your requirements\n\t\t}\n\t\tallocations = append(allocations, allocation)\n\t}\n\n\t// Get MongoDB client from utils (you'll need to implement this)\n\tclient := utils.GetMongoClient()\n\tif client == nil {\n\t\tlog.Printf(\"Failed to get MongoDB client\")\n\t\treturn nil\n\t}\n\n\t// Update MongoDB document\n\tcollection := client.Database(\"Integrations\").Collection(\"Portfolios\")\n\tfilter := bson.M{\"userId\": portfolio.UserID} // Assuming segment is the user ID\n\tupdate := bson.M{\n\t\t\"$set\": bson.M{\n\t\t\t\"policy.target_allocations\": allocations,\n\t\t},\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tresult, err := collection.UpdateOne(ctx, filter, update)\n\tif err != nil {\n\t\tlog.Printf(\"Error updating MongoDB document: %v\", err)\n\t\treturn nil\n\t}\n\n\tif result.MatchedCount == 0 {\n\t\tlog.Printf(\"No document found for user %s\", portfolio.UserID)\n\t\treturn nil\n\t}\n\n\tlog.Printf(\"Successfully updated allocations for user %s\", portfolio.UserID)\n\treturn allocations\n}\n\nfunc StartLLMProcessor() error {\n\t// Create worker pool with 5 workers\n\tworkerPool := utils.NewWorkerPool(5, 100, processLLMMessage)\n\tworkerPool.Start()\n\n\t// Listen for messages from alert_llm topic\n\talertBuffer := make(chan kafka.Message)\n\tgo utils.ConsumeToBuffer(alertBuffer, \"alert_optimize\", \"llm-optimization-group\", \".env\")\n\n\t// Process messages from the buffer\n\tfor msg := range alertBuffer {\n\t\tworkerPool.Submit(string(msg.Value))\n\t}\n\n\treturn nil\n}\n\nfunc main() {\n\t// Load environment variables\n\tif err := godotenv.Load(\".env\"); err != nil {\n\t\tlog.Printf(\"Warning: Error loading .env file: %v\", err)\n\t}\n\n\tlog.Println(\"Starting Portfolio Optimizer...\")\n\tif err := StartLLMProcessor(); err != nil {\n\t\tlog.Fatalf(\"Error starting Portfolio Optimizer: %v\", err)\n\t}\n}\n"}, "7bb5bc1da6b7d2e8171273fdfb2e6801e95ed883": {"t": 1785759800.7397335, "n": 8006, "h": "f7ac5c80184c53e3", "c": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/0xPCDefenders/LIBRA/utils\"\n\t\"github.com/joho/godotenv\"\n\t\"github.com/segmentio/kafka-go\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n)\n\ntype LLMMessage struct {\n\tTopic string `json:\"topic\"`\n\tSegment string `json:\"segment\"`\n\tTicker string `json:\"ticker\"`\n\tData interface{} `json:\"data\"`\n\tTimestamp int64 `json:\"timestamp\"`\n\tPromptTemplate string `json:\"prompt_template\"`\n}\n\ntype OpenAIRequest struct {\n\tModel string `json:\"model\"`\n\tMessages []ChatMessage `json:\"messages\"`\n\tTemperature float64 `json:\"temperature\"`\n}\n\ntype ChatMessage struct {\n\tRole string `json:\"role\"`\n\tContent string `json:\"content\"`\n}\n\ntype OpenAIResponse struct {\n\tChoices []struct {\n\t\tMessage ChatMessage `json:\"message\"`\n\t} `json:\"choices\"`\n}\n\ntype DeepSeekRequest struct {\n\tModel string `json:\"model\"`\n\tMessages []ChatMessage `json:\"messages\"`\n\tStream bool `json:\"stream\"`\n}\n\ntype Allocation struct {\n\tTicker string `json:\"ticker\"`\n\tOptimizedWeight float64 `json:\"optimized_weight\"`\n\tLLMAnalysis string `json:\"llm_analysis\"`\n\tValueDifference float64 `json:\"value_difference\"`\n}\n\ntype Portfolio struct {\n\tID string `json:\"_id\"`\n\tUserID string `json:\"userId\"`\n\tName string `json:\"name\"`\n\tHoldings []Holding `json:\"holdings\"`\n\tPolicy Policy `json:\"policy\"`\n\tLastUpdated string `json:\"lastUpdated\"`\n}\n\ntype Holding struct {\n\tSymbol string `json:\"symbol\"`\n\tName string `json:\"name\"`\n\tQuantity int `json:\"quantity\"`\n\tCostBasis float64 `json:\"costBasis\"`\n\tCurrentPrice float64 `json:\"currentPrice\"`\n\tCurrency string `json:\"currency\"`\n}\n\ntype Policy struct {\n\tUserID string `json:\"userId\"`\n\tRiskTolerance float64 `json:\"riskTolerance\"`\n\tInvestmentHorizon int `json:\"investmentHorizon\"`\n\tBrokerType int `json:\"brokerType\"`\n\tTargetAllocation TargetAllocation `json:\"targetAllocation\"`\n\tRebalanceFrequency string `json:\"rebalanceFrequency\"`\n}\n\ntype TargetAllocation struct {\n\tUserID string `json:\"userId\"`\n\tName string `json:\"name\"`\n\tAllocations interface{} `json:\"allocations\"`\n\tLastUpdated string `json:\"lastUpdated\"`\n}\n\nfunc callDeepSeekAPI(prompt string) (string, error) {\n\tapiKey := os.Getenv(\"DEEPSEEK_API_KEY\")\n\tif apiKey == \"\" {\n\t\treturn \"\", fmt.Errorf(\"DEEPSEEK_API_KEY not set in environment\")\n\t}\n\n\turl := \"https://api.deepseek.com/chat/completions\"\n\n\trequest := DeepSeekRequest{\n\t\tModel: \"deepseek-chat\",\n\t\tMessages: []ChatMessage{\n\t\t\t{\n\t\t\t\tRole: \"system\",\n\t\t\t\tContent: \"Look at the following portfolio holding data for the user and provide a detailed analysis of the portfolio's performance, key metrics, and potential risks or opportunities with the ticker symbol in relation to fundamentals and sentiment.\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tRole: \"user\",\n\t\t\t\tContent: prompt,\n\t\t\t},\n\t\t},\n\t\tStream: false,\n\t}\n\n\tjsonData, err := json.Marshal(request)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"error marshaling request: %v\", err)\n\t}\n\n\t// Implement retry logic\n\tmaxRetries := 3\n\tfor attempt := 1; attempt <= maxRetries; attempt++ {\n\t\treq, err := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"error creating request: %v\", err)\n\t\t}\n\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\treq.Header.Set(\"Authorization\", \"Bearer \"+apiKey)\n\n\t\t// Increased timeout to 90 seconds\n\t\tclient := &http.Client{Timeout: 90 * time.Second}\n\n\t\t// Add context with timeout\n\t\tctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)\n\t\tdefer cancel()\n\t\treq = req.WithContext(ctx)\n\n\t\tresp, err := client.Do(req)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Attempt %d failed: %v\", attempt, err)\n\t\t\tif attempt == maxRetries {\n\t\t\t\treturn \"\", fmt.Errorf(\"max retries reached: %v\", err)\n\t\t\t}\n\t\t\ttime.Sleep(time.Second * time.Duration(attempt)) // Exponential backoff\n\t\t\tcontinue\n\t\t}\n\t\tdefer resp.Body.Close()\n\n\t\tbody, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Attempt %d failed reading body: %v\", attempt, err)\n\t\t\tif attempt == maxRetries {\n\t\t\t\treturn \"\", fmt.Errorf(\"max retries reached: %v\", err)\n\t\t\t}\n\t\t\ttime.Sleep(time.Second * time.Duration(attempt))\n\t\t\tcontinue\n\t\t}\n\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tlog.Printf(\"Attempt %d failed with status %d: %s\", attempt, resp.StatusCode, string(body))\n\t\t\tif attempt == maxRetries {\n\t\t\t\treturn \"\", fmt.Errorf(\"API request failed with status %d: %s\", resp.StatusCode, string(body))\n\t\t\t}\n\t\t\ttime.Sleep(time.Second * time.Duration(attempt))\n\t\t\tcontinue\n\t\t}\n\n\t\tvar response OpenAIResponse\n\t\tif err := json.Unmarshal(body, &response); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"error unmarshaling response: %v\", err)\n\t\t}\n\n\t\tif len(response.Choices) == 0 {\n\t\t\treturn \"\", fmt.Errorf(\"no response choices received\")\n\t\t}\n\n\t\treturn response.Choices[0].Message.Content, nil\n\t}\n\n\treturn \"\", fmt.Errorf(\"failed after %d retries\", maxRetries)\n}\n\nfunc processLLMMessage(job interface{}) interface{} {\n\tmessage := job.(string)\n\n\t// Add debug logging\n\tlog.Printf(\"Received data: %v\", message)\n\n\t// Parse portfolio data directly from the JSON string\n\tvar portfolio Portfolio\n\tif err := json.Unmarshal([]byte(message), &portfolio); err != nil {\n\t\tlog.Printf(\"Error unmarshaling portfolio data: %v\", err)\n\t\treturn nil\n\t}\n\n\t// Create allocations slice\n\tvar allocations []Allocation\n\n\t// Process each holding\n\tfor _, holding := range portfolio.Holdings {\n\t\t// Prepare prompt for DeepSeek\n\t\tprompt := fmt.Sprintf(\"Analyze the following financial data and provide detailed insights about the company's financial health, key metrics, and potential risks or opportunities. %s\", holding.Symbol, holding)\n\n\t\t// Call DeepSeek API\n\t\tanalysis, err := callDeepSeekAPI(prompt)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error calling DeepSeek API for %s: %v\", holding.Symbol, err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Create allocation object\n\t\tallocation := Allocation{\n\t\t\tTicker: holding.Symbol,\n\t\t\tOptimizedWeight: holding.CurrentPrice,\n\t\t\tLLMAnalysis: analysis,\n\t\t\tValueDifference: 0, // Calculate based on your requirements\n\t\t}\n\t\tallocations = append(allocations, allocation)\n\t}\n\n\t// Get MongoDB client from utils (you'll need to implement this)\n\tclient := utils.GetMongoClient()\n\tif client == nil {\n\t\tlog.Printf(\"Failed to get MongoDB client\")\n\t\treturn nil\n\t}\n\n\t// Update MongoDB document\n\tcollection := client.Database(\"Integrations\").Collection(\"Portfolios\")\n\tfilter := bson.M{\"userId\": portfolio.UserID} // Assuming segment is the user ID\n\tupdate := bson.M{\n\t\t\"$set\": bson.M{\n\t\t\t\"policy.target_allocations\": allocations,\n\t\t},\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tresult, err := collection.UpdateOne(ctx, filter, update)\n\tif err != nil {\n\t\tlog.Printf(\"Error updating MongoDB document: %v\", err)\n\t\treturn nil\n\t}\n\n\tif result.MatchedCount == 0 {\n\t\tlog.Printf(\"No document found for user %s\", portfolio.UserID)\n\t\treturn nil\n\t}\n\n\tlog.Printf(\"Successfully updated allocations for user %s\", portfolio.UserID)\n\treturn allocations\n}\n\nfunc StartLLMProcessor() error {\n\t// Create worker pool with 5 workers\n\tworkerPool := utils.NewWorkerPool(5, 100, processLLMMessage)\n\tworkerPool.Start()\n\n\t// Listen for messages from alert_llm topic\n\talertBuffer := make(chan kafka.Message)\n\tgo utils.ConsumeToBuffer(alertBuffer, \"alert_optimize\", \"llm-optimization-group\", \".env\")\n\n\t// Process messages from the buffer\n\tfor msg := range alertBuffer {\n\t\tworkerPool.Submit(string(msg.Value))\n\t}\n\n\treturn nil\n}\n\nfunc main() {\n\t// Load environment variables\n\tif err := godotenv.Load(\".env\"); err != nil {\n\t\tlog.Printf(\"Warning: Error loading .env file: %v\", err)\n\t}\n\n\tlog.Println(\"Starting Portfolio Optimizer...\")\n\tif err := StartLLMProcessor(); err != nil {\n\t\tlog.Fatalf(\"Error starting Portfolio Optimizer: %v\", err)\n\t}\n}\n"}}}} |