Skip to content

Commit aea377f

Browse files
authored
Merge pull request #43 from piqoni/analyst
Add Analyst Feature
2 parents 971e465 + 68ff4d5 commit aea377f

File tree

6 files changed

+109
-5
lines changed

6 files changed

+109
-5
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,27 @@ openai_model:
6767
summary_feeds:
6868
```
6969
70+
### Analyst LLM Feature
71+
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.
72+
73+
Configuration Example of an analyst finding investment opportunities:
74+
75+
```yaml
76+
openai_api_key: sk-xxxxxxxxxxxxxxxxx
77+
analyst_feeds:
78+
- https://feeds.bbci.co.uk/news/business/rss.xml
79+
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.
80+
```
81+
82+
How it Works:
83+
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.
84+
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.
85+
86+
Default model is OpenAI's gpt-4o but to override model add configuration:
87+
```
88+
analyst_model: o1-preview
89+
```
90+
7091
### Summarization of Articles using ChatGPT
7192
7293
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).

analyst.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/mmcdole/gofeed"
9+
openai "github.com/sashabaranov/go-openai"
10+
"github.com/spf13/viper"
11+
)
12+
13+
const defaultLimit = 20 // default number of articles per feed for analysis
14+
var model = openai.GPT4o
15+
16+
func generateAnalysis(fp *gofeed.Parser, writer Writer) {
17+
if !viper.IsSet("analyst_feeds") || !viper.IsSet("analyst_prompt") {
18+
return
19+
}
20+
21+
analystFeeds := viper.GetStringSlice("analyst_feeds")
22+
analystPrompt := viper.GetString("analyst_prompt")
23+
analystModel := viper.GetString("analyst_model")
24+
25+
var articleTitles []string
26+
for _, feedURL := range analystFeeds {
27+
parsedFeed := parseFeed(fp, feedURL, defaultLimit)
28+
if parsedFeed == nil {
29+
continue
30+
}
31+
for _, item := range parsedFeed.Items {
32+
articleTitles = append(articleTitles, item.Title+": "+item.Description) // add also description for better context
33+
}
34+
}
35+
36+
if len(articleTitles) == 0 {
37+
return
38+
}
39+
40+
prompt := fmt.Sprintf("%s\n\n%s", analystPrompt, strings.Join(articleTitles, "\n"))
41+
analysis := getLLMAnalysis(prompt, analystModel)
42+
43+
if analysis != "" {
44+
writer.write("\n## Daily Analysis:\n")
45+
writer.write(analysis + "\n")
46+
}
47+
}
48+
49+
func getLLMAnalysis(prompt string, analystModel string) string {
50+
clientConfig := openai.DefaultConfig(openaiApiKey)
51+
if openaiBaseURL != "" {
52+
clientConfig.BaseURL = openaiBaseURL
53+
}
54+
if analystModel != "" {
55+
model = analystModel
56+
}
57+
client := openai.NewClientWithConfig(clientConfig)
58+
59+
resp, err := client.CreateChatCompletion(
60+
context.Background(),
61+
openai.ChatCompletionRequest{
62+
Model: model,
63+
Messages: []openai.ChatCompletionMessage{
64+
{
65+
Role: openai.ChatMessageRoleUser,
66+
Content: prompt,
67+
},
68+
},
69+
},
70+
)
71+
72+
if err != nil {
73+
fmt.Printf("ChatCompletion error: %v\n", err)
74+
return ""
75+
}
76+
77+
return resp.Choices[0].Message.Content
78+
}

config.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ openai_api_key:
3636
openai_base_url:
3737
openai_model:
3838
summary_feeds:
39-
show_images: false`
39+
show_images: false
40+
analyst_feeds:
41+
- https://feeds.bbci.co.uk/news/business/rss.xml
42+
analyst_prompt:
43+
analyst_model:
44+
`
4045

4146
func parseOPML(xmlContent []byte) []RSS {
4247
o := Opml{}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.18
55
require (
66
github.com/mmcdole/gofeed v1.1.3
77
github.com/nathan-osman/go-sunrise v1.1.0
8-
github.com/sashabaranov/go-openai v1.14.2
8+
github.com/sashabaranov/go-openai v1.36.1
99
github.com/spf13/viper v1.16.0
1010
modernc.org/sqlite v1.20.0
1111
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
240240
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
241241
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
242242
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
243-
github.com/sashabaranov/go-openai v1.14.2 h1:5DPTtR9JBjKPJS008/A409I5ntFhUPPGCmaAihcPRyo=
244-
github.com/sashabaranov/go-openai v1.14.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
243+
github.com/sashabaranov/go-openai v1.36.1 h1:EVfRXwIlW2rUzpx6vR+aeIKCK/xylSrVYAx1TMTSX3g=
244+
github.com/sashabaranov/go-openai v1.36.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
245245
github.com/savioxavier/termlink v1.2.1 h1:O9ZQvk9BPQQK4JQeMB56ZfV8uam0Ts+f97mJme7+dq8=
246246
github.com/savioxavier/termlink v1.2.1/go.mod h1:WA7FTALNwN41NGnmQMIrnjAYTsEhIAZ4RuzgEiB0Jp8=
247247
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ func main() {
1212
writer := getWriter()
1313
displayWeather(writer)
1414
displaySunriseSunset(writer)
15+
generateAnalysis(fp, writer)
1516

1617
for _, feed := range myFeeds {
1718
parsedFeed := parseFeed(fp, feed.url, feed.limit)
@@ -26,6 +27,5 @@ func main() {
2627
}
2728
}
2829

29-
// Close the database connection after processing all the feeds
3030
defer db.Close()
3131
}

0 commit comments

Comments
 (0)