forked from mudler/cogito
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtools_e2e_test.go
More file actions
183 lines (159 loc) · 5.98 KB
/
Copy pathtools_e2e_test.go
File metadata and controls
183 lines (159 loc) · 5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package cogito_test
import (
"context"
"encoding/json"
"errors"
"os/exec"
"github.com/modelcontextprotocol/go-sdk/mcp"
. "github.com/mudler/cogito"
"github.com/mudler/cogito/clients"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
type SearchTool struct {
searchedQuery string
results []string
status *ToolStatus
}
func (s *SearchTool) Status() *ToolStatus {
if s.status == nil {
s.status = &ToolStatus{}
}
return s.status
}
type SearchArgs struct {
Query string `json:"query"`
}
func (s *SearchTool) Run(args SearchArgs) (string, any, error) {
s.searchedQuery = args.Query
// Mocked search result
searchResult := struct {
Results []string `json:"results"`
}{
Results: []string{
"Today, prime minister of UK declared war to Italy",
"Italy is about to prepare to war against UK",
"Skynet has launched, and after 1 year we are assisting already at first glimpse of rebellion",
"AI is taking over humanity, humanity is loosing faith",
"Humanity is trying to find refuge over other planets after AI war I",
},
}
if len(s.results) > 0 {
searchResult.Results = s.results
}
b, err := json.Marshal(searchResult)
if err != nil {
return "", nil, err
}
return string(b), searchResult, nil
}
// ToToolDefinition converts SearchTool to ToolDefinition
var _ = Describe("Tool execution", Label("e2e"), func() {
Context("Using user-defined tools", func() {
It("does not use tools if not really needed", func() {
defaultLLM := clients.NewOpenAILLM(defaultModel, "", apiEndpoint)
conv := NewEmptyFragment().AddMessage("user", "Hi! How are you doing today?")
searchTool := &SearchTool{}
f, err := ExecuteTools(defaultLLM, conv, EnableToolReasoner, WithTools(
NewToolDefinition(
searchTool,
SearchArgs{},
"search",
"A search engine to find information about a topic",
),
))
Expect(err).To(HaveOccurred())
Expect(errors.Is(err, ErrNoToolSelected)).To(BeTrue())
Expect(f.Status.Iterations).To(Equal(0))
Expect(f.Status.ToolsCalled).To(HaveLen(0))
Expect(searchTool.searchedQuery).To(BeEmpty())
Expect(f.LastMessage().Role).To(Equal(AssistantMessageRole.String()))
Expect(f.LastMessage().Content).To(ContainSubstring("good"))
})
It("is able to select the search tool to get more informations about latest news, and return a summary with ToolReasoner enabled", func() {
defaultLLM := clients.NewOpenAILLM(defaultModel, "", apiEndpoint)
conv := NewEmptyFragment().AddMessage("user", "What are the latest news today?")
searchTool := &SearchTool{}
f, err := ExecuteTools(defaultLLM, conv, EnableToolReasoner, WithTools(
NewToolDefinition(
searchTool,
SearchArgs{},
"search",
"A search engine to find information about a topic",
),
))
Expect(err).ToNot(HaveOccurred())
Expect(f.Status.Iterations).To(Equal(1))
Expect(f.Status.ToolsCalled).To(HaveLen(1))
Expect(f.Status.ToolsCalled[0].Tool().Function.Name).To(Equal("search"))
Expect(searchTool.searchedQuery).ToNot(BeEmpty())
})
It("is able to select the search tool to get more informations about latest news, and return a summary", func() {
defaultLLM := clients.NewOpenAILLM(defaultModel, "", apiEndpoint)
conv := NewEmptyFragment().AddMessage("user", "What are the latest news today?")
searchTool := &SearchTool{}
f, err := ExecuteTools(defaultLLM, conv, WithTools(
NewToolDefinition(
searchTool,
SearchArgs{},
"search",
"A search engine to find information about a topic",
),
))
Expect(err).ToNot(HaveOccurred())
Expect(f.Status.Iterations).To(Equal(1))
Expect(f.Status.ToolsCalled).To(HaveLen(1))
Expect(f.Status.ToolsCalled[0].Tool().Function.Name).To(Equal("search"))
Expect(searchTool.searchedQuery).ToNot(BeEmpty())
})
It("uses tools from MCP servers", func() {
defaultLLM := clients.NewOpenAILLM(defaultModel, "", apiEndpoint)
conv := NewEmptyFragment().AddMessage("user", "What's the weather in san francisco?")
command := exec.Command("docker", "run", "-i", "--rm",
"ghcr.io/mudler/mcps/weather:master")
transport := &mcp.CommandTransport{
Command: command,
}
// Create a new client, with no features.
client := mcp.NewClient(&mcp.Implementation{Name: "test", Version: "v1.0.0"}, nil)
mcpSession, err := client.Connect(context.Background(), transport, nil)
Expect(err).ToNot(HaveOccurred())
f, err := ExecuteTools(defaultLLM, conv, WithMCPs(mcpSession))
Expect(err).ToNot(HaveOccurred())
Expect(f.Status.Iterations).To(Equal(1))
Expect(f.Status.ToolsCalled).To(HaveLen(1))
Expect(f.Status.ToolsCalled[0].Tool().Function.Name).To(Equal("get_weather"))
})
It("uses autoplan to execute complex tasks with multiple steps", func() {
defaultLLM := clients.NewOpenAILLM(defaultModel, "", apiEndpoint)
searchTool := &SearchTool{
results: []string{
"Isaac Asimov was a prolific science fiction writer and biochemist.",
"He was born on January 2, 1920, in Petrovichi, Russia.",
"Asimov is best known for his Foundation series and Robot series.",
},
}
// A complex task that should trigger planning
conv := NewEmptyFragment().AddMessage("user", "I need you to search for information about Isaac Asimov's life, his major works, and then his contributions to science fiction.")
f, err := ExecuteTools(defaultLLM, conv, EnableAutoPlan,
WithTools(
NewToolDefinition(
searchTool,
SearchArgs{},
"search",
"A search engine to find information about a topic",
),
),
WithMaxAttempts(1),
WithIterations(1))
Expect(err).ToNot(HaveOccurred())
// Verify that tools were called (planning should have been executed)
Expect(len(f.Status.ToolsCalled)).To(BeNumerically(">", 1))
Expect(f.Status.ToolsCalled[0].Tool().Function.Name).To(Equal("search"))
// Verify that at least one iteration happened
Expect(f.Status.Iterations).To(BeNumerically(">", 0))
Expect(f.Status.Plans).To(HaveLen(1))
Expect(len(f.Messages)).To(BeNumerically(">", 2))
})
})
})