Skip to content

Commit 7a678dc

Browse files
committed
Feat: Increase unit test coverage from 0 to 100% in the AI module using Keploy's Agent
1 parent b2e2784 commit 7a678dc

14 files changed

+2905
-301
lines changed

coverage.out

+2,345
Large diffs are not rendered by default.

go.mod

+26-26
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,27 @@ require (
1212
github.com/gin-gonic/gin v1.10.0
1313
github.com/go-git/go-git/v5 v5.12.0
1414
github.com/go-shiori/go-readability v0.0.0-20241012063810-92284fa8a71f
15-
github.com/google/generative-ai-go v0.18.0
15+
github.com/google/generative-ai-go v0.19.0
1616
github.com/jessevdk/go-flags v1.6.1
1717
github.com/joho/godotenv v1.5.1
1818
github.com/ollama/ollama v0.4.1
1919
github.com/otiai10/copy v1.14.0
2020
github.com/pkg/errors v0.9.1
2121
github.com/samber/lo v1.47.0
2222
github.com/sashabaranov/go-openai v1.35.6
23-
github.com/stretchr/testify v1.9.0
24-
golang.org/x/text v0.20.0
25-
google.golang.org/api v0.205.0
23+
github.com/stretchr/testify v1.10.0
24+
golang.org/x/text v0.21.0
25+
google.golang.org/api v0.220.0
2626
gopkg.in/yaml.v2 v2.4.0
27+
gopkg.in/yaml.v3 v3.0.1
2728
)
2829

2930
require (
3031
cloud.google.com/go v0.116.0 // indirect
3132
cloud.google.com/go/ai v0.8.0 // indirect
32-
cloud.google.com/go/auth v0.10.1 // indirect
33-
cloud.google.com/go/auth/oauth2adapt v0.2.5 // indirect
34-
cloud.google.com/go/compute/metadata v0.5.2 // indirect
33+
cloud.google.com/go/auth v0.14.1 // indirect
34+
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
35+
cloud.google.com/go/compute/metadata v0.6.0 // indirect
3536
cloud.google.com/go/longrunning v0.5.7 // indirect
3637
dario.cat/mergo v1.0.1 // indirect
3738
github.com/Microsoft/go-winio v0.6.2 // indirect
@@ -59,10 +60,10 @@ require (
5960
github.com/goccy/go-json v0.10.3 // indirect
6061
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
6162
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
62-
github.com/google/s2a-go v0.1.8 // indirect
63+
github.com/google/s2a-go v0.1.9 // indirect
6364
github.com/google/uuid v1.6.0 // indirect
6465
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
65-
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
66+
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
6667
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
6768
github.com/json-iterator/go v1.1.12 // indirect
6869
github.com/kevinburke/ssh_config v1.2.0 // indirect
@@ -83,23 +84,22 @@ require (
8384
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
8485
github.com/ugorji/go/codec v1.2.12 // indirect
8586
github.com/xanzy/ssh-agent v0.3.3 // indirect
86-
go.opencensus.io v0.24.0 // indirect
87-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
88-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
89-
go.opentelemetry.io/otel v1.32.0 // indirect
90-
go.opentelemetry.io/otel/metric v1.32.0 // indirect
91-
go.opentelemetry.io/otel/trace v1.32.0 // indirect
87+
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
88+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
89+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
90+
go.opentelemetry.io/otel v1.34.0 // indirect
91+
go.opentelemetry.io/otel/metric v1.34.0 // indirect
92+
go.opentelemetry.io/otel/trace v1.34.0 // indirect
9293
golang.org/x/arch v0.12.0 // indirect
93-
golang.org/x/crypto v0.29.0 // indirect
94-
golang.org/x/net v0.31.0 // indirect
95-
golang.org/x/oauth2 v0.24.0 // indirect
96-
golang.org/x/sync v0.9.0 // indirect
97-
golang.org/x/sys v0.27.0 // indirect
98-
golang.org/x/time v0.7.0 // indirect
99-
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
100-
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
101-
google.golang.org/grpc v1.68.0 // indirect
102-
google.golang.org/protobuf v1.35.1 // indirect
94+
golang.org/x/crypto v0.32.0 // indirect
95+
golang.org/x/net v0.34.0 // indirect
96+
golang.org/x/oauth2 v0.25.0 // indirect
97+
golang.org/x/sync v0.10.0 // indirect
98+
golang.org/x/sys v0.29.0 // indirect
99+
golang.org/x/time v0.9.0 // indirect
100+
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
101+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 // indirect
102+
google.golang.org/grpc v1.70.0 // indirect
103+
google.golang.org/protobuf v1.36.4 // indirect
103104
gopkg.in/warnings.v0 v0.1.2 // indirect
104-
gopkg.in/yaml.v3 v3.0.1 // indirect
105105
)

go.sum

+123-125
Large diffs are not rendered by default.

plugins/ai/anthropic/anthropic.go

+115-120
Original file line numberDiff line numberDiff line change
@@ -1,150 +1,145 @@
11
package anthropic
22

33
import (
4-
"context"
5-
"fmt"
6-
"strings"
7-
8-
"github.com/anthropics/anthropic-sdk-go"
9-
"github.com/anthropics/anthropic-sdk-go/option"
10-
"github.com/danielmiessler/fabric/common"
11-
"github.com/danielmiessler/fabric/plugins"
12-
goopenai "github.com/sashabaranov/go-openai"
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/anthropics/anthropic-sdk-go"
9+
"github.com/anthropics/anthropic-sdk-go/option"
10+
"github.com/danielmiessler/fabric/common"
11+
"github.com/danielmiessler/fabric/plugins"
12+
goopenai "github.com/sashabaranov/go-openai"
1313
)
1414

1515
const defaultBaseUrl = "https://api.anthropic.com/"
1616

1717
func NewClient() (ret *Client) {
18-
vendorName := "Anthropic"
19-
ret = &Client{}
20-
21-
ret.PluginBase = &plugins.PluginBase{
22-
Name: vendorName,
23-
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
24-
ConfigureCustom: ret.configure,
25-
}
26-
27-
ret.ApiBaseURL = ret.AddSetupQuestion("API Base URL", false)
28-
ret.ApiBaseURL.Value = defaultBaseUrl
29-
ret.ApiKey = ret.PluginBase.AddSetupQuestion("API key", true)
30-
31-
// we could provide a setup question for the following settings
32-
ret.maxTokens = 4096
33-
ret.defaultRequiredUserMessage = "Hi"
34-
ret.models = []string{
35-
anthropic.ModelClaude3_5HaikuLatest, anthropic.ModelClaude3_5Haiku20241022,
36-
anthropic.ModelClaude3_5SonnetLatest, anthropic.ModelClaude3_5Sonnet20241022,
37-
anthropic.ModelClaude_3_5_Sonnet_20240620, anthropic.ModelClaude3OpusLatest,
38-
anthropic.ModelClaude_3_Opus_20240229, anthropic.ModelClaude_3_Sonnet_20240229,
39-
anthropic.ModelClaude_3_Haiku_20240307, anthropic.ModelClaude_2_1,
40-
anthropic.ModelClaude_2_0, anthropic.ModelClaude_Instant_1_2,
41-
}
42-
43-
return
18+
vendorName := "Anthropic"
19+
ret = &Client{}
20+
21+
ret.PluginBase = &plugins.PluginBase{
22+
Name: vendorName,
23+
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
24+
ConfigureCustom: ret.configure,
25+
}
26+
27+
ret.ApiBaseURL = ret.AddSetupQuestion("API Base URL", false)
28+
ret.ApiBaseURL.Value = defaultBaseUrl
29+
ret.ApiKey = ret.PluginBase.AddSetupQuestion("API key", true)
30+
31+
ret.maxTokens = 4096
32+
ret.defaultRequiredUserMessage = "Hi"
33+
ret.models = []string{
34+
anthropic.ModelClaude3_5HaikuLatest, anthropic.ModelClaude3_5Haiku20241022,
35+
anthropic.ModelClaude3_5SonnetLatest, anthropic.ModelClaude3_5Sonnet20241022,
36+
anthropic.ModelClaude_3_5_Sonnet_20240620, anthropic.ModelClaude3OpusLatest,
37+
anthropic.ModelClaude_3_Opus_20240229, anthropic.ModelClaude_3_Sonnet_20240229,
38+
anthropic.ModelClaude_3_Haiku_20240307, anthropic.ModelClaude_2_1,
39+
anthropic.ModelClaude_2_0, anthropic.ModelClaude_Instant_1_2,
40+
}
41+
42+
return
4443
}
4544

4645
type Client struct {
47-
*plugins.PluginBase
48-
ApiBaseURL *plugins.SetupQuestion
49-
ApiKey *plugins.SetupQuestion
46+
*plugins.PluginBase
47+
ApiBaseURL *plugins.SetupQuestion
48+
ApiKey *plugins.SetupQuestion
5049

51-
maxTokens int
52-
defaultRequiredUserMessage string
53-
models []string
50+
maxTokens int
51+
defaultRequiredUserMessage string
52+
models []string
5453

55-
client *anthropic.Client
54+
client *anthropic.Client
5655
}
5756

5857
func (an *Client) configure() (err error) {
59-
if an.ApiBaseURL.Value != "" {
60-
baseURL := an.ApiBaseURL.Value
61-
62-
// If the base URL contains a UUID, ensure it ends with /v1
63-
if strings.Contains(baseURL, "-") && !strings.HasSuffix(baseURL, "/v1") {
64-
if strings.HasSuffix(baseURL, "/") {
65-
baseURL = strings.TrimSuffix(baseURL, "/")
66-
}
67-
baseURL = baseURL + "/v1"
68-
}
69-
70-
an.client = anthropic.NewClient(
71-
option.WithAPIKey(an.ApiKey.Value),
72-
option.WithBaseURL(baseURL),
73-
)
74-
} else {
75-
an.client = anthropic.NewClient(option.WithAPIKey(an.ApiKey.Value))
76-
}
77-
return
58+
if an.ApiBaseURL.Value != "" {
59+
baseURL := an.ApiBaseURL.Value
60+
61+
if strings.Contains(baseURL, "-") && !strings.HasSuffix(baseURL, "/v1") {
62+
if strings.HasSuffix(baseURL, "/") {
63+
baseURL = strings.TrimSuffix(baseURL, "/")
64+
}
65+
baseURL = baseURL + "/v1"
66+
}
67+
68+
an.client = anthropic.NewClient(
69+
option.WithAPIKey(an.ApiKey.Value),
70+
option.WithBaseURL(baseURL),
71+
)
72+
} else {
73+
an.client = anthropic.NewClient(option.WithAPIKey(an.ApiKey.Value))
74+
}
75+
return
7876
}
7977

8078
func (an *Client) ListModels() (ret []string, err error) {
81-
return an.models, nil
79+
return an.models, nil
8280
}
8381

8482
func (an *Client) SendStream(
85-
msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string,
83+
msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string,
8684
) (err error) {
87-
88-
messages := an.toMessages(msgs)
89-
90-
ctx := context.Background()
91-
stream := an.client.Messages.NewStreaming(ctx, anthropic.MessageNewParams{
92-
Model: anthropic.F(opts.Model),
93-
MaxTokens: anthropic.F(int64(an.maxTokens)),
94-
TopP: anthropic.F(opts.TopP),
95-
Temperature: anthropic.F(opts.Temperature),
96-
Messages: anthropic.F(messages),
97-
})
98-
99-
for stream.Next() {
100-
event := stream.Current()
101-
102-
switch delta := event.Delta.(type) {
103-
case anthropic.ContentBlockDeltaEventDelta:
104-
if delta.Text != "" {
105-
channel <- delta.Text
106-
}
107-
}
108-
}
109-
110-
if stream.Err() != nil {
111-
fmt.Printf("Messages stream error: %v\n", stream.Err())
112-
}
113-
close(channel)
114-
return
85+
messages := an.toMessages(msgs)
86+
87+
ctx := context.Background()
88+
stream := an.client.Messages.NewStreaming(ctx, anthropic.MessageNewParams{
89+
Model: anthropic.F(opts.Model),
90+
MaxTokens: anthropic.F(int64(an.maxTokens)),
91+
TopP: anthropic.F(opts.TopP),
92+
Temperature: anthropic.F(opts.Temperature),
93+
Messages: anthropic.F(messages),
94+
})
95+
96+
for stream.Next() {
97+
event := stream.Current()
98+
99+
switch delta := event.Delta.(type) {
100+
case anthropic.ContentBlockDeltaEventDelta:
101+
if delta.Text != "" {
102+
channel <- delta.Text
103+
}
104+
}
105+
}
106+
107+
if stream.Err() != nil {
108+
fmt.Printf("Messages stream error: %v\n", stream.Err())
109+
}
110+
close(channel)
111+
return
115112
}
116113

117114
func (an *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (ret string, err error) {
118-
messages := an.toMessages(msgs)
119-
120-
var message *anthropic.Message
121-
if message, err = an.client.Messages.New(ctx, anthropic.MessageNewParams{
122-
Model: anthropic.F(opts.Model),
123-
MaxTokens: anthropic.F(int64(an.maxTokens)),
124-
TopP: anthropic.F(opts.TopP),
125-
Temperature: anthropic.F(opts.Temperature),
126-
Messages: anthropic.F(messages),
127-
}); err != nil {
128-
return
129-
}
130-
ret = message.Content[0].Text
131-
return
115+
messages := an.toMessages(msgs)
116+
117+
var message *anthropic.Message
118+
if message, err = an.client.Messages.New(ctx, anthropic.MessageNewParams{
119+
Model: anthropic.F(opts.Model),
120+
MaxTokens: anthropic.F(int64(an.maxTokens)),
121+
TopP: anthropic.F(opts.TopP),
122+
Temperature: anthropic.F(opts.Temperature),
123+
Messages: anthropic.F(messages),
124+
}); err != nil {
125+
return
126+
}
127+
ret = message.Content[0].Text
128+
return
132129
}
133130

134131
func (an *Client) toMessages(msgs []*goopenai.ChatCompletionMessage) (ret []anthropic.MessageParam) {
135-
// we could call the method before calling the specific vendor
136-
normalizedMessages := common.NormalizeMessages(msgs, an.defaultRequiredUserMessage)
137-
138-
// Iterate over the incoming session messages and process them
139-
for _, msg := range normalizedMessages {
140-
var message anthropic.MessageParam
141-
switch msg.Role {
142-
case goopenai.ChatMessageRoleUser:
143-
message = anthropic.NewUserMessage(anthropic.NewTextBlock(msg.Content))
144-
default:
145-
message = anthropic.NewAssistantMessage(anthropic.NewTextBlock(msg.Content))
146-
}
147-
ret = append(ret, message)
148-
}
149-
return
132+
normalizedMessages := common.NormalizeMessages(msgs, an.defaultRequiredUserMessage)
133+
134+
for _, msg := range normalizedMessages {
135+
var message anthropic.MessageParam
136+
switch msg.Role {
137+
case goopenai.ChatMessageRoleUser:
138+
message = anthropic.NewUserMessage(anthropic.NewTextBlock(msg.Content))
139+
default:
140+
message = anthropic.NewAssistantMessage(anthropic.NewTextBlock(msg.Content))
141+
}
142+
ret = append(ret, message)
143+
}
144+
return
150145
}

0 commit comments

Comments
 (0)