Skip to content

Commit 9f1035b

Browse files
authored
feat(provider): support Anthropics API client (#208)
- Add a new package `anthropic` with a client implementation for interacting with the Anthropics API - Implement the `Completion` method to generate responses using the Anthropics API - Implement the `GetSummaryPrefix` method to get a summary prefix using function calls - Add configuration options for the client including API key, model, max tokens, temperature, and topP - Define tools for the `GetSummaryPrefix` function with JSON schema validation - Update `go.mod` to include the `go-anthropic/v2` dependency Signed-off-by: appleboy <[email protected]>
1 parent 0ca6230 commit 9f1035b

File tree

5 files changed

+269
-0
lines changed

5 files changed

+269
-0
lines changed

anthropic/anthropic.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package anthropic
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/appleboy/CodeGPT/core"
9+
10+
"github.com/appleboy/com/convert"
11+
"github.com/liushuangls/go-anthropic/v2"
12+
)
13+
14+
var _ core.Generative = (*Client)(nil)
15+
16+
type Client struct {
17+
client *anthropic.Client
18+
model anthropic.Model
19+
maxTokens int
20+
temperature float32
21+
topP float32
22+
}
23+
24+
// Completion is a method on the Client struct that takes a context.Context and a string argument
25+
func (c *Client) Completion(ctx context.Context, content string) (*core.Response, error) {
26+
resp, err := c.client.CreateMessages(ctx, anthropic.MessagesRequest{
27+
Model: c.model,
28+
Messages: []anthropic.Message{
29+
anthropic.NewUserTextMessage(content),
30+
},
31+
MaxTokens: c.maxTokens,
32+
Temperature: convert.ToPtr(c.temperature),
33+
TopP: convert.ToPtr(c.topP),
34+
})
35+
if err != nil {
36+
var e *anthropic.APIError
37+
if errors.As(err, &e) {
38+
fmt.Printf("Messages error, type: %s, message: %s", e.Type, e.Message)
39+
} else {
40+
fmt.Printf("Messages error: %v\n", err)
41+
}
42+
return nil, err
43+
}
44+
45+
return &core.Response{
46+
Content: resp.Content[0].GetText(),
47+
Usage: core.Usage{
48+
PromptTokens: resp.Usage.InputTokens,
49+
CompletionTokens: resp.Usage.OutputTokens,
50+
TotalTokens: resp.Usage.InputTokens + resp.Usage.OutputTokens,
51+
},
52+
}, nil
53+
}
54+
55+
// GetSummaryPrefix is an API call to get a summary prefix using function call.
56+
func (c *Client) GetSummaryPrefix(ctx context.Context, content string) (*core.Response, error) {
57+
request := anthropic.MessagesRequest{
58+
Model: c.model,
59+
Messages: []anthropic.Message{
60+
anthropic.NewUserTextMessage(content),
61+
},
62+
MaxTokens: c.maxTokens,
63+
Tools: tools,
64+
}
65+
66+
resp, err := c.client.CreateMessages(ctx, request)
67+
if err != nil {
68+
return nil, err
69+
}
70+
71+
var toolResult *anthropic.MessageContentToolResult
72+
73+
for _, c := range resp.Content {
74+
if c.Type == anthropic.MessagesContentTypeToolResult {
75+
toolResult = c.MessageContentToolResult
76+
}
77+
}
78+
79+
if toolResult == nil {
80+
return nil, errors.New("no tool use found in response")
81+
}
82+
83+
return &core.Response{
84+
Content: toolResult.Content[0].GetText(),
85+
Usage: core.Usage{
86+
PromptTokens: resp.Usage.InputTokens,
87+
CompletionTokens: resp.Usage.OutputTokens,
88+
TotalTokens: resp.Usage.InputTokens + resp.Usage.OutputTokens,
89+
},
90+
}, nil
91+
}
92+
93+
func New(opts ...Option) (c *Client, err error) {
94+
// Create a new config object with the given options.
95+
cfg := newConfig(opts...)
96+
97+
// Validate the config object, returning an error if it is invalid.
98+
if err := cfg.valid(); err != nil {
99+
return nil, err
100+
}
101+
102+
// Create a new client instance with the necessary fields.
103+
engine := &Client{
104+
client: anthropic.NewClient(cfg.apiKey),
105+
model: cfg.model,
106+
maxTokens: cfg.maxTokens,
107+
temperature: cfg.temperature,
108+
topP: cfg.topP,
109+
}
110+
111+
return engine, nil
112+
}

anthropic/func.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package anthropic
2+
3+
import (
4+
"github.com/liushuangls/go-anthropic/v2"
5+
"github.com/sashabaranov/go-openai/jsonschema"
6+
)
7+
8+
var tools = []anthropic.ToolDefinition{
9+
{
10+
Name: "get_summary_prefix",
11+
Description: "Get a summary prefix using function call",
12+
InputSchema: jsonschema.Definition{
13+
Type: jsonschema.Object,
14+
Properties: map[string]jsonschema.Definition{
15+
"unit": {
16+
Type: jsonschema.String,
17+
Enum: []string{
18+
"build", "chore", "ci",
19+
"docs", "feat", "fix",
20+
"perf", "refactor", "style",
21+
"test",
22+
},
23+
Description: "The prefix to use for the summary",
24+
},
25+
},
26+
Required: []string{"prefix"},
27+
},
28+
},
29+
}

anthropic/options.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package anthropic
2+
3+
import (
4+
"errors"
5+
6+
"github.com/liushuangls/go-anthropic/v2"
7+
)
8+
9+
var (
10+
errorsMissingAPIKey = errors.New("missing api key")
11+
errorsMissingModel = errors.New("missing model")
12+
)
13+
14+
var (
15+
defaultMaxTokens = 300
16+
defaultModel = anthropic.ModelClaude3Haiku20240307
17+
defaultTemperature = float32(1.0)
18+
defaultTopP = float32(1.0)
19+
)
20+
21+
// Option is an interface that specifies instrumentation configuration options.
22+
type Option interface {
23+
apply(*config)
24+
}
25+
26+
// optionFunc is a type of function that can be used to implement the Option interface.
27+
// It takes a pointer to a config struct and modifies it.
28+
type optionFunc func(*config)
29+
30+
// Ensure that optionFunc satisfies the Option interface.
31+
var _ Option = (*optionFunc)(nil)
32+
33+
// The apply method of optionFunc type is implemented here to modify the config struct based on the function passed.
34+
func (o optionFunc) apply(c *config) {
35+
o(c)
36+
}
37+
38+
// WithAPIKey is a function that returns an Option, which sets the token field of the config struct.
39+
func WithAPIKey(val string) Option {
40+
return optionFunc(func(c *config) {
41+
c.apiKey = val
42+
})
43+
}
44+
45+
// WithModel is a function that returns an Option, which sets the model field of the config struct.
46+
func WithModel(val anthropic.Model) Option {
47+
return optionFunc(func(c *config) {
48+
c.model = val
49+
})
50+
}
51+
52+
// WithMaxTokens returns a new Option that sets the max tokens for the client configuration.
53+
// The maximum number of tokens to generate in the chat completion.
54+
// The total length of input tokens and generated tokens is limited by the model's context length.
55+
func WithMaxTokens(val int) Option {
56+
if val <= 0 {
57+
val = defaultMaxTokens
58+
}
59+
return optionFunc(func(c *config) {
60+
c.maxTokens = val
61+
})
62+
}
63+
64+
// WithTemperature returns a new Option that sets the temperature for the client configuration.
65+
// What sampling temperature to use, between 0 and 2.
66+
// Higher values like 0.8 will make the output more random,
67+
// while lower values like 0.2 will make it more focused and deterministic.
68+
func WithTemperature(val float32) Option {
69+
if val <= 0 {
70+
val = defaultTemperature
71+
}
72+
return optionFunc(func(c *config) {
73+
c.temperature = val
74+
})
75+
}
76+
77+
// WithTopP returns a new Option that sets the topP for the client configuration.
78+
func WithTopP(val float32) Option {
79+
return optionFunc(func(c *config) {
80+
c.topP = val
81+
})
82+
}
83+
84+
// config is a struct that stores configuration options for the instrumentation.
85+
type config struct {
86+
apiKey string
87+
model anthropic.Model
88+
maxTokens int
89+
temperature float32
90+
topP float32
91+
}
92+
93+
// valid checks whether a config object is valid, returning an error if it is not.
94+
func (cfg *config) valid() error {
95+
// Check that the token is not empty.
96+
if cfg.apiKey == "" {
97+
return errorsMissingAPIKey
98+
}
99+
100+
if cfg.model == "" {
101+
return errorsMissingModel
102+
}
103+
104+
// If all checks pass, return nil (no error).
105+
return nil
106+
}
107+
108+
// newConfig creates a new config object with default values, and applies the given options.
109+
func newConfig(opts ...Option) *config {
110+
// Create a new config object with default values.
111+
c := &config{
112+
model: defaultModel,
113+
maxTokens: defaultMaxTokens,
114+
temperature: defaultTemperature,
115+
topP: defaultTopP,
116+
}
117+
118+
// Apply each of the given options to the config object.
119+
for _, opt := range opts {
120+
opt.apply(c)
121+
}
122+
123+
// Return the resulting config object.
124+
return c
125+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
github.com/fatih/color v1.18.0
1414
github.com/google/generative-ai-go v0.18.0
1515
github.com/joho/godotenv v1.5.1
16+
github.com/liushuangls/go-anthropic/v2 v2.12.2
1617
github.com/rodaine/table v1.3.0
1718
github.com/sashabaranov/go-openai v1.35.6
1819
github.com/spf13/cobra v1.8.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
106106
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
107107
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
108108
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
109+
github.com/liushuangls/go-anthropic/v2 v2.12.2 h1:nq+HR3RYYK4/btEjET8nDenQO3NoSDKAuGXP8qNq2c8=
110+
github.com/liushuangls/go-anthropic/v2 v2.12.2/go.mod h1:5ZwRLF5TQ+y5s/MC9Z1IJYx9WUFgQCKfqFM2xreIQLk=
109111
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
110112
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
111113
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=

0 commit comments

Comments
 (0)