Skip to content

Commit

Permalink
Merge pull request #43 from piqoni/analyst
Browse files Browse the repository at this point in the history
Add Analyst Feature
  • Loading branch information
piqoni authored Jan 5, 2025
2 parents 971e465 + 68ff4d5 commit aea377f
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 5 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,27 @@ openai_model:
summary_feeds:
```
### Analyst LLM Feature
The Analyst feature enables you to gather articles from specified feeds and analyze them using a prompt sent to a language model like GPT-4o (default). The result is included in the daily digest under an Analysis section. You write on your analyst_prompt setting what do you want the analyst to do on your behalf, for example picking relevant news to your liking (example: a cybersecurity expert interested only in certain type of attack), or having an investing analyst suggesting investment opportunities, etc.
Configuration Example of an analyst finding investment opportunities:
```yaml
openai_api_key: sk-xxxxxxxxxxxxxxxxx
analyst_feeds:
- https://feeds.bbci.co.uk/news/business/rss.xml
analyst_prompt: You are a world-class investing expert. Analyze the provided list of articles for potential investment opportunities. If no direct opportunities are found, identify industries, regions, or trends that could have indirect impacts on the investment landscape.
```
How it Works:
Gathering Articles: The RSS feeds specified in analyst_feeds are fetched, and the titles along with their rss descriptions are attached to the analyst_prompt to form a single input prompt.
Then the prompt is sent to the specified language model (analyst_model), and the response is included in the daily markdown file under the Analysis section.
Default model is OpenAI's gpt-4o but to override model add configuration:
```
analyst_model: o1-preview
```
### Summarization of Articles using ChatGPT
In order to use the summarization feature, you'll first need to set up an OpenAI account. If you haven't already done so, you can sign up [here](https://platform.openai.com/login?launch). Once registered, you'll need to acquire an OpenAI API key which can be found [here](https://platform.openai.com/account/api-keys).
Expand Down
78 changes: 78 additions & 0 deletions analyst.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package main

import (
"context"
"fmt"
"strings"

"github.com/mmcdole/gofeed"
openai "github.com/sashabaranov/go-openai"
"github.com/spf13/viper"
)

const defaultLimit = 20 // default number of articles per feed for analysis
var model = openai.GPT4o

func generateAnalysis(fp *gofeed.Parser, writer Writer) {
if !viper.IsSet("analyst_feeds") || !viper.IsSet("analyst_prompt") {
return
}

analystFeeds := viper.GetStringSlice("analyst_feeds")
analystPrompt := viper.GetString("analyst_prompt")
analystModel := viper.GetString("analyst_model")

var articleTitles []string
for _, feedURL := range analystFeeds {
parsedFeed := parseFeed(fp, feedURL, defaultLimit)
if parsedFeed == nil {
continue
}
for _, item := range parsedFeed.Items {
articleTitles = append(articleTitles, item.Title+": "+item.Description) // add also description for better context
}
}

if len(articleTitles) == 0 {
return
}

prompt := fmt.Sprintf("%s\n\n%s", analystPrompt, strings.Join(articleTitles, "\n"))
analysis := getLLMAnalysis(prompt, analystModel)

if analysis != "" {
writer.write("\n## Daily Analysis:\n")
writer.write(analysis + "\n")
}
}

func getLLMAnalysis(prompt string, analystModel string) string {
clientConfig := openai.DefaultConfig(openaiApiKey)
if openaiBaseURL != "" {
clientConfig.BaseURL = openaiBaseURL
}
if analystModel != "" {
model = analystModel
}
client := openai.NewClientWithConfig(clientConfig)

resp, err := client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: model,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: prompt,
},
},
},
)

if err != nil {
fmt.Printf("ChatCompletion error: %v\n", err)
return ""
}

return resp.Choices[0].Message.Content
}
7 changes: 6 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ openai_api_key:
openai_base_url:
openai_model:
summary_feeds:
show_images: false`
show_images: false
analyst_feeds:
- https://feeds.bbci.co.uk/news/business/rss.xml
analyst_prompt:
analyst_model:
`

func parseOPML(xmlContent []byte) []RSS {
o := Opml{}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.18
require (
github.com/mmcdole/gofeed v1.1.3
github.com/nathan-osman/go-sunrise v1.1.0
github.com/sashabaranov/go-openai v1.14.2
github.com/sashabaranov/go-openai v1.36.1
github.com/spf13/viper v1.16.0
modernc.org/sqlite v1.20.0
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sashabaranov/go-openai v1.14.2 h1:5DPTtR9JBjKPJS008/A409I5ntFhUPPGCmaAihcPRyo=
github.com/sashabaranov/go-openai v1.14.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/sashabaranov/go-openai v1.36.1 h1:EVfRXwIlW2rUzpx6vR+aeIKCK/xylSrVYAx1TMTSX3g=
github.com/sashabaranov/go-openai v1.36.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/savioxavier/termlink v1.2.1 h1:O9ZQvk9BPQQK4JQeMB56ZfV8uam0Ts+f97mJme7+dq8=
github.com/savioxavier/termlink v1.2.1/go.mod h1:WA7FTALNwN41NGnmQMIrnjAYTsEhIAZ4RuzgEiB0Jp8=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func main() {
writer := getWriter()
displayWeather(writer)
displaySunriseSunset(writer)
generateAnalysis(fp, writer)

for _, feed := range myFeeds {
parsedFeed := parseFeed(fp, feed.url, feed.limit)
Expand All @@ -26,6 +27,5 @@ func main() {
}
}

// Close the database connection after processing all the feeds
defer db.Close()
}

0 comments on commit aea377f

Please sign in to comment.