diff --git a/README.md b/README.md index 883643a..5636feb 100644 --- a/README.md +++ b/README.md @@ -51,11 +51,11 @@ func main() { option.WithAPIKey("my-anthropic-api-key"), // defaults to os.LookupEnv("ANTHROPIC_API_KEY") ) message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{ + Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620), MaxTokens: anthropic.F(int64(1024)), 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 { panic(err.Error()) @@ -65,6 +65,205 @@ func main() { ``` +
+Conversations + +```go +messages := []anthropic.MessageParam{ + anthropic.NewUserMessage(anthropic.NewTextBlock("What is my first name?")), +} + +message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{ + Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620), + Messages: anthropic.F(messages), + MaxTokens: anthropic.F(int64(1024)), +}) + +messages = append(messages, message.ToParam()) +messages = append(messages, anthropic.NewUserMessage( + anthropic.NewTextBlock("My full name is John Doe"), +)) + +message, err = client.Messages.New(context.TODO(), anthropic.MessageNewParams{ + Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620), + Messages: anthropic.F(messages), + MaxTokens: anthropic.F(int64(1024)), +}) +``` + +
+ +
+System prompts + +```go +messages := []anthropic.MessageParam{ + anthropic.NewUserMessage(anthropic.NewTextBlock("What is my first name?")), +} + +message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{ + Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620), + MaxTokens: anthropic.Int(1024), + System: anthropic.F([]anthropic.TextBlockParam{ + anthropic.NewTextBlock("Be very serious at all times."), + }), + Messages: anthropic.F(messages), +}) +``` + +
+ +
+Streaming + +```go +stream := client.Messages.NewStreaming(context.TODO(), anthropic.MessageNewParams{ + Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620), + MaxTokens: anthropic.Int(1024), + Messages: anthropic.F([]anthropic.MessageParam{ + anthropic.NewUserMessage(anthropic.NewTextBlock(content)), + }), +}) + +message := anthropic.Message{} +for stream.Next() { + event := stream.Current() + message.Accumulate(event) + + switch delta := event.Delta.(type) { + case anthropic.ContentBlockDeltaEventDelta: + if delta.Text != "" { + print(delta.Text) + } + } +} + +if stream.Err() != nil { + panic(stream.Err()) +} +``` + +
+ +
+Tool calling + +```go +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/invopop/jsonschema" + "github.com/anthropics/anthropic-sdk-go" +) + +func main() { + client := anthropic.NewClient() + + content := "Where is San Francisco?" + + println("[user]: " + content) + + messages := []anthropic.MessageParam{ + anthropic.NewUserMessage(anthropic.NewTextBlock(content)), + } + + tools := []anthropic.ToolParam{ + { + Name: anthropic.F("get_coordinates"), + Description: anthropic.F("Accepts a place as an address, then returns the latitude and longitude coordinates."), + InputSchema: anthropic.F(GetCoordinatesInputSchema), + }, + } + + for { + message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{ + Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620), + MaxTokens: anthropic.Int(1024), + Messages: anthropic.F(messages), + Tools: anthropic.F(tools), + }) + + if err != nil { + panic(err) + } + + print(color("[assistant]: ")) + for _, block := range message.Content { + switch block := block.AsUnion().(type) { + case anthropic.TextBlock: + println(block.Text) + case anthropic.ToolUseBlock: + println(block.Name + ": " + string(block.Input)) + } + } + + messages = append(messages, message.ToParam()) + toolResults := []anthropic.MessageParamContentUnion{} + + for _, block := range message.Content { + if block.Type == anthropic.ContentBlockTypeToolUse { + print("[user (" + block.Name + ")]: ") + + var response interface{} + switch block.Name { + case "get_coordinates": + input := GetCoordinatesInput{} + err := json.Unmarshal(block.Input, &input) + if err != nil { + panic(err) + } + response = GetCoordinates(input.Location) + } + + b, err := json.Marshal(response) + if err != nil { + panic(err) + } + + toolResults = append(toolResults, anthropic.NewToolResultBlock(block.ID, string(b), false)) + } + } + if len(toolResults) == 0 { + break + } + messages = append(messages, anthropic.NewUserMessage(toolResults...)) + } +} + +type GetCoordinatesInput struct { + Location string `json:"location" jsonschema_description:"The location to look up."` +} + +var GetCoordinatesInputSchema = GenerateSchema[GetCoordinatesInput]() + +type GetCoordinateResponse struct { + Long float64 `json:"long"` + Lat float64 `json:"lat"` +} + +func GetCoordinates(location string) GetCoordinateResponse { + return GetCoordinateResponse{ + Long: -122.4194, + Lat: 37.7749, + } +} + +func GenerateSchema[T any]() interface{} { + reflector := jsonschema.Reflector{ + AllowAdditionalProperties: false, + DoNotReference: true, + } + var v T + return reflector.Reflect(v) +} +``` + +
+ ### Request fields All request parameters are wrapped in a generic `Field` type, @@ -276,17 +475,17 @@ To make requests to undocumented endpoints, you can use `client.Get`, `client.Po ```go var ( - // params can be an io.Reader, a []byte, an encoding/json serializable object, - // or a "…Params" struct defined in this library. - params map[string]interface{} + // params can be an io.Reader, a []byte, an encoding/json serializable object, + // or a "…Params" struct defined in this library. + params map[string]interface{} - // result can be an []byte, *http.Response, a encoding/json deserializable object, - // or a model defined in this library. - result *http.Response + // result can be an []byte, *http.Response, a encoding/json deserializable object, + // or a model defined in this library. + result *http.Response ) err := client.Post(context.Background(), "/unspecified", params, &result) if err != nil { - … + … } ``` @@ -297,10 +496,10 @@ or the `option.WithJSONSet()` methods. ```go params := FooNewParams{ - ID: anthropic.F("id_xxxx"), - Data: anthropic.F(FooNewParamsData{ - FirstName: anthropic.F("John"), - }), + ID: anthropic.F("id_xxxx"), + Data: anthropic.F(FooNewParamsData{ + FirstName: anthropic.F("John"), + }), } client.Foo.New(context.Background(), params, option.WithJSONSet("data.last_name", "Doe")) ``` @@ -331,7 +530,7 @@ func Logger(req *http.Request, next option.MiddlewareNext) (res *http.Response, end := time.Now() LogRes(res, err, start - end) - return res, err + return res, err } client := anthropic.NewClient( diff --git a/examples/message/main.go b/examples/message/main.go index 00806f0..5ccf36c 100644 --- a/examples/message/main.go +++ b/examples/message/main.go @@ -15,6 +15,9 @@ func main() { message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{ MaxTokens: anthropic.Int(1024), + System: anthropic.F([]anthropic.TextBlockParam{ + anthropic.NewTextBlock("Be very serious"), + }), Messages: anthropic.F([]anthropic.MessageParam{ anthropic.NewUserMessage(anthropic.NewTextBlock(content)), }), diff --git a/examples/tools/main.go b/examples/tools/main.go index b426396..1b6494d 100644 --- a/examples/tools/main.go +++ b/examples/tools/main.go @@ -150,10 +150,7 @@ func main() { break } - messages = append(messages, anthropic.MessageParam{ - Role: anthropic.F(anthropic.MessageParamRoleUser), - Content: anthropic.F(toolResults), - }) + messages = append(messages, anthropic.NewUserMessage(toolResults...)) } } diff --git a/message.go b/message.go index 32fcd64..32d644e 100644 --- a/message.go +++ b/message.go @@ -338,6 +338,8 @@ func (a *Message) Accumulate(event MessageStreamEvent) error { return nil } +// ToParam converts a Message to a MessageParam, which can be used when constructing a new +// Create type Message struct { // Unique object identifier. // @@ -437,6 +439,29 @@ type messageJSON struct { ExtraFields map[string]apijson.Field } +// ToParam converts a Message to a MessageParam which can be used when making another network +// request. This is useful when interacting with Claude conversationally or when tool calling. +// +// messages := []anthropic.MessageParam{ +// anthropic.NewUserMessage(anthropic.NewTextBlock("What is my first name?")), +// } +// +// message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{ +// MaxTokens: anthropic.F(int64(1024)), +// Messages: anthropic.F(messages), +// Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620), +// }) +// +// messages = append(messages, message.ToParam()) +// messages = append(messages, anthropic.NewUserMessage( +// anthropic.NewTextBlock("My full name is John Doe"), +// )) +// +// message, err = client.Messages.New(context.TODO(), anthropic.MessageNewParams{ +// MaxTokens: anthropic.F(int64(1024)), +// Messages: anthropic.F(messages), +// Model: anthropic.F(anthropic.ModelClaude_3_5_Sonnet_20240620), +// }) func (r *Message) ToParam() MessageParam { content := []MessageParamContentUnion{}