feat: migrate to official anthropics Go SDK

This commit is contained in:
Eugen Eisler
2024-11-19 13:12:10 +01:00
parent 005f2b7db5
commit 3684031f44
3 changed files with 76 additions and 64 deletions

5
go.mod
View File

@@ -35,6 +35,7 @@ require (
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.2 // indirect github.com/ProtonMail/go-crypto v1.1.2 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/anthropics/anthropic-sdk-go v0.2.0-alpha.4 // indirect
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
github.com/bytedance/sonic v1.12.4 // indirect github.com/bytedance/sonic v1.12.4 // indirect
github.com/bytedance/sonic/loader v0.2.1 // indirect github.com/bytedance/sonic/loader v0.2.1 // indirect
@@ -75,6 +76,10 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect github.com/skeema/knownhosts v1.3.0 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect

12
go.sum
View File

@@ -25,6 +25,8 @@ github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsVi
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/anthropics/anthropic-sdk-go v0.2.0-alpha.4 h1:TdGQS+RoR4AUO6gqUL74yK1dz/Arrt/WG+dxOj6Yo6A=
github.com/anthropics/anthropic-sdk-go v0.2.0-alpha.4/go.mod h1:GJxtdOs9K4neo8Gg65CjJ7jNautmldGli5/OFNabOoo=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -209,6 +211,16 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=

View File

@@ -2,17 +2,16 @@ package anthropic
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/anthropics/anthropic-sdk-go"
"github.com/anthropics/anthropic-sdk-go/option"
"github.com/danielmiessler/fabric/common"
"github.com/danielmiessler/fabric/plugins" "github.com/danielmiessler/fabric/plugins"
goopenai "github.com/sashabaranov/go-openai" goopenai "github.com/sashabaranov/go-openai"
"github.com/danielmiessler/fabric/common"
"github.com/liushuangls/go-anthropic/v2"
) )
const baseUrl = "https://api.anthropic.com/v1" //const baseUrl = "https://api.anthropic.com/"
func NewClient() (ret *Client) { func NewClient() (ret *Client) {
vendorName := "Anthropic" vendorName := "Anthropic"
@@ -24,17 +23,20 @@ func NewClient() (ret *Client) {
ConfigureCustom: ret.configure, ConfigureCustom: ret.configure,
} }
ret.ApiBaseURL = ret.AddSetupQuestion("API Base URL", false) //ret.ApiBaseURL = ret.AddSetupQuestion("API Base URL", false)
ret.ApiBaseURL.Value = baseUrl //ret.ApiBaseURL.Value = baseUrl
ret.ApiKey = ret.PluginBase.AddSetupQuestion("API key", true) ret.ApiKey = ret.PluginBase.AddSetupQuestion("API key", true)
// we could provide a setup question for the following settings // we could provide a setup question for the following settings
ret.maxTokens = 4096 ret.maxTokens = 4096
ret.defaultRequiredUserMessage = "Hi" ret.defaultRequiredUserMessage = "Hi"
ret.models = []string{ ret.models = []string{
string(anthropic.ModelClaude3Dot5HaikuLatest), string(anthropic.ModelClaude3Opus20240229), anthropic.ModelClaude3_5HaikuLatest, anthropic.ModelClaude3_5Haiku20241022,
string(anthropic.ModelClaude3Opus20240229), string(anthropic.ModelClaude2Dot0), string(anthropic.ModelClaude2Dot1), anthropic.ModelClaude3_5SonnetLatest, anthropic.ModelClaude3_5Sonnet20241022,
string(anthropic.ModelClaude3Dot5SonnetLatest), string(anthropic.ModelClaude3Dot5HaikuLatest), anthropic.ModelClaude_3_5_Sonnet_20240620, anthropic.ModelClaude3OpusLatest,
anthropic.ModelClaude_3_Opus_20240229, anthropic.ModelClaude_3_Sonnet_20240229,
anthropic.ModelClaude_3_Haiku_20240307, anthropic.ModelClaude_2_1,
anthropic.ModelClaude_2_0, anthropic.ModelClaude_Instant_1_2,
} }
return return
@@ -42,8 +44,8 @@ func NewClient() (ret *Client) {
type Client struct { type Client struct {
*plugins.PluginBase *plugins.PluginBase
ApiBaseURL *plugins.SetupQuestion //ApiBaseURL *plugins.SetupQuestion
ApiKey *plugins.SetupQuestion ApiKey *plugins.SetupQuestion
maxTokens int maxTokens int
defaultRequiredUserMessage string defaultRequiredUserMessage string
@@ -53,11 +55,14 @@ type Client struct {
} }
func (an *Client) configure() (err error) { func (an *Client) configure() (err error) {
if an.ApiBaseURL.Value != "" { /*if an.ApiBaseURL.Value != "" {
an.client = anthropic.NewClient(an.ApiKey.Value, anthropic.WithBaseURL(an.ApiBaseURL.Value)) an.client = anthropic.NewClient(
option.WithAPIKey(an.ApiKey.Value), option.WithBaseURL(an.ApiBaseURL.Value),
)
} else { } else {
an.client = anthropic.NewClient(an.ApiKey.Value) */
} an.client = anthropic.NewClient(option.WithAPIKey(an.ApiKey.Value))
//}
return return
} }
@@ -68,75 +73,65 @@ func (an *Client) ListModels() (ret []string, err error) {
func (an *Client) SendStream( func (an *Client) SendStream(
msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string,
) (err error) { ) (err error) {
ctx := context.Background()
req := an.buildMessagesRequest(msgs, opts)
req.Stream = true
if _, err = an.client.CreateMessagesStream(ctx, anthropic.MessagesStreamRequest{ messages := an.toMessages(msgs)
MessagesRequest: req,
OnContentBlockDelta: func(data anthropic.MessagesEventContentBlockDeltaData) { ctx := context.Background()
// fmt.Printf("Stream Content: %s\n", data.Delta.Text) stream := an.client.Messages.NewStreaming(ctx, anthropic.MessageNewParams{
channel <- *data.Delta.Text Model: anthropic.F(opts.Model),
}, MaxTokens: anthropic.F(int64(an.maxTokens)),
}); err != nil { TopP: anthropic.F(opts.TopP),
var e *anthropic.APIError Temperature: anthropic.F(opts.Temperature),
if errors.As(err, &e) { Messages: anthropic.F(messages),
fmt.Printf("Messages stream error, type: %s, message: %s", e.Type, e.Message) })
} else {
fmt.Printf("Messages stream error: %v\n", err) for stream.Next() {
event := stream.Current()
switch delta := event.Delta.(type) {
case anthropic.ContentBlockDeltaEventDelta:
if delta.Text != "" {
channel <- delta.Text
}
} }
} else {
close(channel)
} }
if stream.Err() != nil {
fmt.Printf("Messages stream error: %v\n", stream.Err())
}
close(channel)
return return
} }
func (an *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (ret string, err error) { func (an *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (ret string, err error) {
req := an.buildMessagesRequest(msgs, opts)
req.Stream = false
var resp anthropic.MessagesResponse
if resp, err = an.client.CreateMessages(ctx, req); err == nil {
ret = *resp.Content[0].Text
} else {
var e *anthropic.APIError
if errors.As(err, &e) {
fmt.Printf("Messages error, type: %s, message: %s", e.Type, e.Message)
} else {
fmt.Printf("Messages error: %v\n", err)
}
}
return
}
func (an *Client) buildMessagesRequest(msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (ret anthropic.MessagesRequest) {
temperature := float32(opts.Temperature)
topP := float32(opts.TopP)
messages := an.toMessages(msgs) messages := an.toMessages(msgs)
ret = anthropic.MessagesRequest{ var message *anthropic.Message
Model: anthropic.Model(opts.Model), if message, err = an.client.Messages.New(ctx, anthropic.MessageNewParams{
Temperature: &temperature, Model: anthropic.F(opts.Model),
TopP: &topP, MaxTokens: anthropic.F(int64(an.maxTokens)),
Messages: messages, TopP: anthropic.F(opts.TopP),
MaxTokens: an.maxTokens, Temperature: anthropic.F(opts.Temperature),
Messages: anthropic.F(messages),
}); err != nil {
return
} }
ret = message.Content[0].Text
return return
} }
func (an *Client) toMessages(msgs []*goopenai.ChatCompletionMessage) (ret []anthropic.Message) { func (an *Client) toMessages(msgs []*goopenai.ChatCompletionMessage) (ret []anthropic.MessageParam) {
// we could call the method before calling the specific vendor // we could call the method before calling the specific vendor
normalizedMessages := common.NormalizeMessages(msgs, an.defaultRequiredUserMessage) normalizedMessages := common.NormalizeMessages(msgs, an.defaultRequiredUserMessage)
// Iterate over the incoming session messages and process them // Iterate over the incoming session messages and process them
for _, msg := range normalizedMessages { for _, msg := range normalizedMessages {
var message anthropic.Message var message anthropic.MessageParam
switch msg.Role { switch msg.Role {
case goopenai.ChatMessageRoleUser: case goopenai.ChatMessageRoleUser:
message = anthropic.NewUserTextMessage(msg.Content) message = anthropic.NewUserMessage(anthropic.NewTextBlock(msg.Content))
default: default:
message = anthropic.NewAssistantTextMessage(msg.Content) message = anthropic.NewAssistantMessage(anthropic.NewTextBlock(msg.Content))
} }
ret = append(ret, message) ret = append(ret, message)
} }