@@ -12,16 +12,20 @@ import (
12
12
"github.com/djthorpe/go-tablewriter"
13
13
"github.com/mutablelogic/go-client"
14
14
"github.com/mutablelogic/go-client/pkg/anthropic"
15
+ "github.com/mutablelogic/go-client/pkg/newsapi"
15
16
"github.com/mutablelogic/go-client/pkg/openai/schema"
16
17
)
17
18
18
19
///////////////////////////////////////////////////////////////////////////////
19
20
// GLOBALS
20
21
21
22
var (
22
- samName = "sam"
23
- samWeatherTool = schema .NewTool ("get_weather" , "Get the weather for a location" )
24
- samSystemPrompt = "Your name is Samantha, you are a friendly AI assistant, here to help you with anything you need. Your responses should be short and to the point, and you should always be polite."
23
+ samName = "sam"
24
+ samWeatherTool = schema .NewTool ("get_weather" , "Get the weather for a location" )
25
+ samNewsHeadlinesTool = schema .NewTool ("get_news_headlines" , "Get the news headlines" )
26
+ samNewsSearchTool = schema .NewTool ("search_news" , "Search news articles" )
27
+ samSystemPrompt = `Your name is Samantha, you are a friendly AI assistant, here to help you with
28
+ anything you need. Your responses should be short and to the point, and you should always be polite.`
25
29
)
26
30
27
31
///////////////////////////////////////////////////////////////////////////////
30
34
func samRegister (flags * Flags ) {
31
35
flags .Register (Cmd {
32
36
Name : samName ,
33
- Description : "Interact with Samantha, a friendly AI assistant" ,
37
+ Description : "Interact with Samantha, a friendly AI assistant, to query news and weather " ,
34
38
Parse : samParse ,
35
39
Fn : []Fn {
36
40
{Name : "chat" , Call : samChat , Description : "Chat with Sam" },
@@ -43,6 +47,10 @@ func samParse(flags *Flags, opts ...client.ClientOpt) error {
43
47
if err := weatherapiParse (flags , opts ... ); err != nil {
44
48
return err
45
49
}
50
+ // Initialize news
51
+ if err := newsapiParse (flags , opts ... ); err != nil {
52
+ return err
53
+ }
46
54
47
55
// Initialize anthropic
48
56
opts = append (opts , client .OptHeader ("Anthropic-Beta" , "tools-2024-04-04" ))
@@ -54,6 +62,12 @@ func samParse(flags *Flags, opts ...client.ClientOpt) error {
54
62
if err := samWeatherTool .AddParameter ("location" , "The city to get the weather for" , true ); err != nil {
55
63
return err
56
64
}
65
+ if err := samNewsHeadlinesTool .AddParameter ("category" , "The cateogry of news, which should be one of business, entertainment, general, health, science, sports or technology" , true ); err != nil {
66
+ return err
67
+ }
68
+ if err := samNewsSearchTool .AddParameter ("query" , "The query with which to search news" , true ); err != nil {
69
+ return err
70
+ }
57
71
58
72
// Return success
59
73
return nil
@@ -82,8 +96,21 @@ func samChat(ctx context.Context, w *tablewriter.Writer, _ []string) error {
82
96
messages = append (messages , schema .NewMessage ("user" , schema .Text (strings .TrimSpace (text ))))
83
97
}
84
98
99
+ // Curtail requests to the last N history
100
+ if len (messages ) > 10 {
101
+ messages = messages [len (messages )- 10 :]
102
+ // First message must have role 'user'
103
+ for {
104
+ if len (messages ) == 0 || messages [0 ].Role == "user" {
105
+ break
106
+ }
107
+ messages = messages [1 :]
108
+ }
109
+ // TODO: We must remove the first instance tool_result if there is no tool_use
110
+ }
111
+
85
112
// Request -> Response
86
- responses , err := anthropicClient .Messages (ctx , messages , anthropic .OptSystem (samSystemPrompt ), anthropic .OptTool (samWeatherTool ))
113
+ responses , err := anthropicClient .Messages (ctx , messages , anthropic .OptSystem (samSystemPrompt ), anthropic .OptTool (samWeatherTool ), anthropic . OptTool ( samNewsHeadlinesTool ), anthropic . OptTool ( samNewsSearchTool ) )
87
114
if err != nil {
88
115
return err
89
116
}
@@ -111,14 +138,46 @@ func samCall(_ context.Context, content schema.Content) *schema.Content {
111
138
}
112
139
switch content .Name {
113
140
case samWeatherTool .Name :
114
- if location , exists := content .GetString (content .Name , "location" ); exists {
115
- if weather , err := weatherapiClient .Current (location ); err != nil {
116
- return schema .ToolResult (content .Id , fmt .Sprint ("Unable to get the current weather, the error is " , err ))
117
- } else if data , err := json .MarshalIndent (weather , "" , " " ); err != nil {
118
- return schema .ToolResult (content .Id , fmt .Sprint ("Unable to marshal the weather data, the error is " , err ))
119
- } else {
120
- return schema .ToolResult (content .Id , string (data ))
121
- }
141
+ var location string
142
+ if v , exists := content .GetString (content .Name , "location" ); exists {
143
+ location = v
144
+ } else {
145
+ location = "auto:ip"
146
+ }
147
+ if weather , err := weatherapiClient .Current (location ); err != nil {
148
+ return schema .ToolResult (content .Id , fmt .Sprint ("Unable to get the current weather, the error is " , err ))
149
+ } else if data , err := json .MarshalIndent (weather , "" , " " ); err != nil {
150
+ return schema .ToolResult (content .Id , fmt .Sprint ("Unable to marshal the weather data, the error is " , err ))
151
+ } else {
152
+ return schema .ToolResult (content .Id , string (data ))
153
+ }
154
+ case samNewsHeadlinesTool .Name :
155
+ var category string
156
+ if v , exists := content .GetString (content .Name , "category" ); exists {
157
+ category = v
158
+ } else {
159
+ category = "general"
160
+ }
161
+ if headlines , err := newsapiClient .Headlines (newsapi .OptCategory (category )); err != nil {
162
+ return schema .ToolResult (content .Id , fmt .Sprint ("Unable to get the news headlines, the error is " , err ))
163
+ } else if data , err := json .MarshalIndent (headlines , "" , " " ); err != nil {
164
+ return schema .ToolResult (content .Id , fmt .Sprint ("Unable to marshal the headlines data, the error is " , err ))
165
+ } else {
166
+ return schema .ToolResult (content .Id , string (data ))
167
+ }
168
+ case samNewsSearchTool .Name :
169
+ var query string
170
+ if v , exists := content .GetString (content .Name , "query" ); exists {
171
+ query = v
172
+ } else {
173
+ return schema .ToolResult (content .Id , "Unable to search news due to missing query" )
174
+ }
175
+ if articles , err := newsapiClient .Articles (newsapi .OptQuery (query ), newsapi .OptLimit (5 )); err != nil {
176
+ return schema .ToolResult (content .Id , fmt .Sprint ("Unable to search news, the error is " , err ))
177
+ } else if data , err := json .MarshalIndent (articles , "" , " " ); err != nil {
178
+ return schema .ToolResult (content .Id , fmt .Sprint ("Unable to marshal the articles data, the error is " , err ))
179
+ } else {
180
+ return schema .ToolResult (content .Id , string (data ))
122
181
}
123
182
}
124
183
return schema .ToolResult (content .Id , fmt .Sprint ("unable to call:" , content .Name ))
0 commit comments