Skip to content

Commit

Permalink
chore: add back custom code
Browse files Browse the repository at this point in the history
This reverts commit ad38619.
  • Loading branch information
yjp20 authored and stainless-bot committed Aug 14, 2024
1 parent 2bb2325 commit 106c404
Show file tree
Hide file tree
Showing 10 changed files with 765 additions and 101 deletions.
84 changes: 68 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,9 @@ func main() {
)
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
MaxTokens: anthropic.F(int64(1024)),
Messages: anthropic.F([]anthropic.MessageParam{{
Role: anthropic.F(anthropic.MessageParamRoleUser),
Content: anthropic.F([]anthropic.MessageParamContentUnion{anthropic.TextBlockParam{Type: anthropic.F(anthropic.TextBlockParamTypeText), Text: anthropic.F("What is a quaternion?")}}),
}}),
Messages: anthropic.F([]anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in SF?")),
}),
Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620),
})
if err != nil {
Expand Down Expand Up @@ -181,10 +180,9 @@ To handle errors, we recommend that you use the `errors.As` pattern:
```go
_, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
MaxTokens: anthropic.F(int64(1024)),
Messages: anthropic.F([]anthropic.MessageParam{{
Role: anthropic.F(anthropic.MessageParamRoleUser),
Content: anthropic.F([]anthropic.MessageParamContentUnion{anthropic.TextBlockParam{Type: anthropic.F(anthropic.TextBlockParamTypeText), Text: anthropic.F("What is a quaternion?")}}),
}}),
Messages: anthropic.F([]anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in SF?")),
}),
Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620),
})
if err != nil {
Expand Down Expand Up @@ -215,10 +213,9 @@ client.Messages.New(
ctx,
anthropic.MessageNewParams{
MaxTokens: anthropic.F(int64(1024)),
Messages: anthropic.F([]anthropic.MessageParam{{
Role: anthropic.F(anthropic.MessageParamRoleUser),
Content: anthropic.F([]anthropic.MessageParamContentUnion{anthropic.TextBlockParam{Type: anthropic.F(anthropic.TextBlockParamTypeText), Text: anthropic.F("What is a quaternion?")}}),
}}),
Messages: anthropic.F([]anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in SF?")),
}),
Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620),
},
// This sets the per-retry timeout
Expand Down Expand Up @@ -258,10 +255,9 @@ client.Messages.New(
context.TODO(),
anthropic.MessageNewParams{
MaxTokens: anthropic.F(int64(1024)),
Messages: anthropic.F([]anthropic.MessageParam{{
Role: anthropic.F(anthropic.MessageParamRoleUser),
Content: anthropic.F([]anthropic.MessageParamContentUnion{anthropic.TextBlockParam{Type: anthropic.F(anthropic.TextBlockParamTypeText), Text: anthropic.F("What is a quaternion?")}}),
}}),
Messages: anthropic.F([]anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in SF?")),
}),
Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620),
},
option.WithMaxRetries(5),
Expand Down Expand Up @@ -354,6 +350,62 @@ You may also replace the default `http.Client` with
accepted (this overwrites any previous client) and receives requests after any
middleware has been applied.

## Amazon Bedrock

To use this library with [Amazon Bedrock](https://aws.amazon.com/bedrock/claude/),
use the bedrock request option `bedrock.WithLoadDefaultConfig(…)` which reads the
[default config](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html).

Importing the `bedrock` library also globally registers a decoder for `application/vnd.amazon.eventstream` for
streaming.

```go
package main

import (
"github.com/anthropics/anthropic-sdk-go"
"github.com/anthropics/anthropic-sdk-go/bedrock"
)

func main() {
client := anthropic.NewClient(
bedrock.WithLoadDefaultConfig(context.Background()),
)
}
```

If you already have an `aws.Config`, you can also use it directly with `bedrock.WithConfig(cfg)`.

Read more about Anthropic and Amazon Bedrock [here](https://docs.anthropic.com/en/api/claude-on-amazon-bedrock).

## Google Vertex AI

To use this library with [Google Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude),
use the request option `vertex.WithGoogleAuth(…)` which reads the
[Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials).

```go
package main

import (
"context"

"github.com/anthropics/anthropic-sdk-go"
"github.com/anthropics/anthropic-sdk-go/vertex"
)

func main() {
client := anthropic.NewClient(
vertex.WithGoogleAuth(context.Background(), "us-central1", "stainless-399616"),
)
}
```

If you already have `*google.Credentials`, you can also use it directly with
`vertex.WithCredentials(ctx, region, projectId, creds)`.

Read more about Anthropic and Google Vertex [here](https://docs.anthropic.com/en/api/claude-on-vertex-ai).

## Semantic versioning

This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
Expand Down
210 changes: 210 additions & 0 deletions bedrock/bedrock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package bedrock

import (
"bytes"
"context"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream"
"github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream/eventstreamapi"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"

"github.com/anthropics/anthropic-sdk-go/internal/requestconfig"
"github.com/anthropics/anthropic-sdk-go/option"
"github.com/anthropics/anthropic-sdk-go/packages/ssestream"
)

const DefaultVersion = "bedrock-2023-05-31"

var DefaultEndpoints = map[string]bool{
"/v1/complete": true,
"/v1/messages": true,
}

type eventstreamChunk struct {
Bytes string `json:"bytes"`
P string `json:"p"`
}

type eventstreamDecoder struct {
eventstream.Decoder

rc io.ReadCloser
evt ssestream.Event
err error
}

func (e *eventstreamDecoder) Close() error {
return nil
}

func (e *eventstreamDecoder) Err() error {
return nil
}

func (e *eventstreamDecoder) Next() bool {
if e.err != nil {
return false
}

msg, err := e.Decoder.Decode(e.rc, nil)
if err != nil {
e.err = err
return false
}

messageType := msg.Headers.Get(eventstreamapi.MessageTypeHeader)
if messageType == nil {
e.err = fmt.Errorf("%s event header not present", eventstreamapi.MessageTypeHeader)
return false
}

switch messageType.String() {
case eventstreamapi.EventMessageType:
eventType := msg.Headers.Get(eventstreamapi.EventTypeHeader)
if eventType == nil {
e.err = fmt.Errorf("%s event header not present", eventstreamapi.EventTypeHeader)
return false
}

if eventType.String() == "chunk" {
chunk := eventstreamChunk{}
err = json.Unmarshal(msg.Payload, &chunk)
if err != nil {
e.err = err
return false
}
decoded, err := base64.StdEncoding.DecodeString(chunk.Bytes)
if err != nil {
e.err = err
return false
}
e.evt = ssestream.Event{
Type: gjson.GetBytes(decoded, "type").String(),
Data: decoded,
}
}

case eventstreamapi.ExceptionMessageType:
case eventstreamapi.ErrorMessageType:
errorCode := "UnknownError"
errorMessage := errorCode
if header := msg.Headers.Get(eventstreamapi.ErrorCodeHeader); header != nil {
errorCode = header.String()
}
if header := msg.Headers.Get(eventstreamapi.ErrorMessageHeader); header != nil {
errorMessage = header.String()
}
e.err = fmt.Errorf("received error or exception %s: %s", errorCode, errorMessage)
return false
}

return true
}

func (e *eventstreamDecoder) Event() ssestream.Event {
return e.evt
}

var (
_ ssestream.Decoder = &eventstreamDecoder{}
)

func init() {
ssestream.RegisterDecoder("application/vnd.amazon.eventstream", func(rc io.ReadCloser) ssestream.Decoder {
return &eventstreamDecoder{rc: rc}
})
}

// WithLoadDefaultConfig returns a request option which loads the default config for Amazon and registers
// middleware that intercepts request to the Messages API so that this SDK can be used with Amazon Bedrock.
//
// If you already have an [aws.Config], it is recommended that you instead call [WithConfig] directly.
func WithLoadDefaultConfig(ctx context.Context, optFns ...func(*config.LoadOptions) error) option.RequestOption {
cfg, err := config.LoadDefaultConfig(ctx, optFns...)
if err != nil {
panic(err)
}
return WithConfig(cfg)
}

// WithConfig returns a request option which uses the provided config and registers middleware that
// intercepts request to the Messages API so that this SDK can be used with Amazon Bedrock.
func WithConfig(cfg aws.Config) option.RequestOption {
signer := v4.NewSigner()
middleware := bedrockMiddleware(signer, cfg)

return func(rc *requestconfig.RequestConfig) error {
return rc.Apply(
option.WithBaseURL(fmt.Sprintf("https://bedrock-runtime.%s.amazonaws.com", cfg.Region)),
option.WithMiddleware(middleware),
)
}
}

func bedrockMiddleware(signer *v4.Signer, cfg aws.Config) option.Middleware {
return func(r *http.Request, next option.MiddlewareNext) (res *http.Response, err error) {
var body []byte
if r.Body != nil {
body, err = io.ReadAll(r.Body)
if err != nil {
return nil, err
}
r.Body.Close()

if !gjson.GetBytes(body, "anthropic_version").Exists() {
body, _ = sjson.SetBytes(body, "anthropic_version", DefaultVersion)
}

if r.Method == http.MethodPost && DefaultEndpoints[r.URL.Path] {
model := gjson.GetBytes(body, "model").String()
stream := gjson.GetBytes(body, "stream").Bool()

body, _ = sjson.DeleteBytes(body, "model")
body, _ = sjson.DeleteBytes(body, "stream")

var path string
if stream {
path = fmt.Sprintf("/model/%s/invoke-with-response-stream", model)
} else {
path = fmt.Sprintf("/model/%s/invoke", model)
}

r.URL.Path = path
}

reader := bytes.NewReader(body)
r.Body = io.NopCloser(reader)
r.GetBody = func() (io.ReadCloser, error) {
_, err := reader.Seek(0, 0)
return io.NopCloser(reader), err
}
r.ContentLength = int64(len(body))
}

ctx := r.Context()
credentials, err := cfg.Credentials.Retrieve(ctx)
if err != nil {
return nil, err
}

hash := sha256.Sum256(body)
err = signer.SignHTTP(ctx, credentials, r, hex.EncodeToString(hash[:]), "bedrock", cfg.Region, time.Now())
if err != nil {
return nil, err
}

return next(r)
}
}
2 changes: 1 addition & 1 deletion examples/tools-streaming-jsonschema/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"encoding/json"
"fmt"

"github.com/anthropics/anthropic-sdk-go"
"github.com/invopop/jsonschema"
"github.com/anthropics/anthropic-sdk-go"
)

func main() {
Expand Down
43 changes: 42 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,50 @@ module github.com/anthropics/anthropic-sdk-go
go 1.19

require (
github.com/google/uuid v1.3.0 // indirect
cloud.google.com/go/auth v0.7.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
cloud.google.com/go/compute/metadata v0.5.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // 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
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/api v0.189.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect
google.golang.org/grpc v1.64.1 // indirect
google.golang.org/protobuf v1.34.2 // indirect
)
Loading

0 comments on commit 106c404

Please sign in to comment.