Skip to content

Commit 3f9bc5a

Browse files
Merge pull request #193 from anthropics/release-please--branches--main--changes--next
release: 1.2.1
2 parents 9fb6954 + 3cb988f commit 3f9bc5a

8 files changed

Lines changed: 302 additions & 19 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Create releases
2+
on:
3+
schedule:
4+
- cron: '0 5 * * *' # every day at 5am UTC
5+
push:
6+
branches:
7+
- main
8+
9+
jobs:
10+
release:
11+
name: release
12+
if: github.ref == 'refs/heads/main' && github.repository == 'anthropics/anthropic-sdk-go'
13+
runs-on: ubuntu-latest
14+
environment: production-release
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- uses: stainless-api/trigger-release-please@v1
20+
id: release
21+
with:
22+
repo: ${{ github.event.repository.full_name }}
23+
stainless-api-key: ${{ secrets.STAINLESS_API_KEY }}
24+
25+
- name: Generate godocs
26+
if: ${{ steps.release.outputs.releases_created }}
27+
run: |
28+
version=$(jq -r '. | to_entries[0] | .value' .release-please-manifest.json)
29+
curl -X POST https://pkg.go.dev/fetch/github.com/anthropics/anthropic-sdk-go@v${version}

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "1.2.0"
2+
".": "1.2.1"
33
}

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## 1.2.1 (2025-05-23)
4+
5+
Full Changelog: [v1.2.0...v1.2.1](https://github.com/anthropics/anthropic-sdk-go/compare/v1.2.0...v1.2.1)
6+
7+
### Chores
8+
9+
* **examples:** clean up MCP example ([66f406a](https://github.com/anthropics/anthropic-sdk-go/commit/66f406a04b9756281e7716e9b635c3e3f29397fb))
10+
* **internal:** fix release workflows ([6a0ff4c](https://github.com/anthropics/anthropic-sdk-go/commit/6a0ff4cad1c1b4ab6435df80fccd945d6ce07be7))
11+
312
## 1.2.0 (2025-05-22)
413

514
Full Changelog: [v1.1.0...v1.2.0](https://github.com/anthropics/anthropic-sdk-go/compare/v1.1.0...v1.2.0)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Or to pin the version:
2222
<!-- x-release-please-start-version -->
2323

2424
```sh
25-
go get -u 'github.com/anthropics/[email protected].0'
25+
go get -u 'github.com/anthropics/[email protected].1'
2626
```
2727

2828
<!-- x-release-please-end -->

betamessage.go

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,10 +1141,10 @@ type BetaContentBlockUnion struct {
11411141
// Any of "text", "tool_use", "server_tool_use", "web_search_tool_result",
11421142
// "code_execution_tool_result", "mcp_tool_use", "mcp_tool_result",
11431143
// "container_upload", "thinking", "redacted_thinking".
1144-
Type string `json:"type"`
1145-
ID string `json:"id"`
1146-
Input any `json:"input"`
1147-
Name string `json:"name"`
1144+
Type string `json:"type"`
1145+
ID string `json:"id"`
1146+
Input json.RawMessage `json:"input"`
1147+
Name string `json:"name"`
11481148
// This field is a union of [BetaWebSearchToolResultBlockContentUnion],
11491149
// [BetaCodeExecutionToolResultBlockContentUnion],
11501150
// [BetaMCPToolResultBlockContentUnion]
@@ -3138,6 +3138,82 @@ func (r *BetaRawMessageStreamEventUnionDelta) UnmarshalJSON(data []byte) error {
31383138
return apijson.UnmarshalRoot(data, r)
31393139
}
31403140

3141+
// Accumulate builds up the Message incrementally from a MessageStreamEvent. The Message then can be used as
3142+
// any other Message, except with the caveat that the Message.JSON field which normally can be used to inspect
3143+
// the JSON sent over the network may not be populated fully.
3144+
//
3145+
// message := anthropic.Message{}
3146+
// for stream.Next() {
3147+
// event := stream.Current()
3148+
// message.Accumulate(event)
3149+
// }
3150+
func (acc *BetaMessage) Accumulate(event BetaRawMessageStreamEventUnion) error {
3151+
if acc == nil {
3152+
return fmt.Errorf("accumulate: cannot accumlate into nil Message")
3153+
}
3154+
3155+
switch event := event.AsAny().(type) {
3156+
case BetaRawMessageStartEvent:
3157+
*acc = event.Message
3158+
case BetaRawMessageDeltaEvent:
3159+
acc.StopReason = event.Delta.StopReason
3160+
acc.StopSequence = event.Delta.StopSequence
3161+
acc.Usage.OutputTokens = event.Usage.OutputTokens
3162+
case BetaRawMessageStopEvent:
3163+
accJson, err := json.Marshal(acc)
3164+
if err != nil {
3165+
return fmt.Errorf("error converting content block to JSON: %w", err)
3166+
}
3167+
acc.JSON.raw = string(accJson)
3168+
case BetaRawContentBlockStartEvent:
3169+
acc.Content = append(acc.Content, BetaContentBlockUnion{})
3170+
err := acc.Content[len(acc.Content)-1].UnmarshalJSON([]byte(event.ContentBlock.RawJSON()))
3171+
if err != nil {
3172+
return err
3173+
}
3174+
case BetaRawContentBlockDeltaEvent:
3175+
if len(acc.Content) == 0 {
3176+
return fmt.Errorf("received event of type %s but there was no content block", event.Type)
3177+
}
3178+
cb := &acc.Content[len(acc.Content)-1]
3179+
switch delta := event.Delta.AsAny().(type) {
3180+
case BetaTextDelta:
3181+
cb.Text += delta.Text
3182+
case BetaInputJSONDelta:
3183+
if len(delta.PartialJSON) != 0 {
3184+
if string(cb.Input) == "{}" {
3185+
cb.Input = []byte(delta.PartialJSON)
3186+
} else {
3187+
cb.Input = append(cb.Input, []byte(delta.PartialJSON)...)
3188+
}
3189+
}
3190+
case BetaThinkingDelta:
3191+
cb.Thinking += delta.Thinking
3192+
case BetaSignatureDelta:
3193+
cb.Signature += delta.Signature
3194+
case BetaCitationsDelta:
3195+
citation := BetaTextCitationUnion{}
3196+
err := citation.UnmarshalJSON([]byte(delta.Citation.RawJSON()))
3197+
if err != nil {
3198+
return fmt.Errorf("could not unmarshal citation delta into citation type: %w", err)
3199+
}
3200+
cb.Citations = append(cb.Citations, citation)
3201+
}
3202+
case BetaRawContentBlockStopEvent:
3203+
if len(acc.Content) == 0 {
3204+
return fmt.Errorf("received event of type %s but there was no content block", event.Type)
3205+
}
3206+
contentBlock := &acc.Content[len(acc.Content)-1]
3207+
cbJson, err := json.Marshal(contentBlock)
3208+
if err != nil {
3209+
return fmt.Errorf("error converting content block to JSON: %w", err)
3210+
}
3211+
contentBlock.JSON.raw = string(cbJson)
3212+
}
3213+
3214+
return nil
3215+
}
3216+
31413217
type BetaRedactedThinkingBlock struct {
31423218
Data string `json:"data,required"`
31433219
Type constant.RedactedThinking `json:"type,required"`

betamessage_test.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package anthropic_test
44

55
import (
66
"context"
7+
"encoding/json"
78
"errors"
89
"os"
910
"testing"
@@ -190,3 +191,168 @@ func TestBetaMessageCountTokensWithOptionalParams(t *testing.T) {
190191
t.Fatalf("err should be nil: %s", err.Error())
191192
}
192193
}
194+
195+
func TestBetaAccumulate(t *testing.T) {
196+
for name, testCase := range map[string]struct {
197+
expected anthropic.BetaMessage
198+
events []string
199+
}{
200+
"empty message": {
201+
expected: anthropic.BetaMessage{Usage: anthropic.BetaUsage{}},
202+
events: []string{
203+
`{"type": "message_start", "message": {}}`,
204+
`{"type: "message_stop"}`,
205+
},
206+
},
207+
"text content block": {
208+
events: []string{
209+
`{"type": "message_start", "message": {}}`,
210+
`{"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": "This "}}`,
211+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "is a "}}`,
212+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta": "text": "text block!"}}`,
213+
`{"type": "content_block_stop", "index": 0}`,
214+
`{"type": "message_stop"}`,
215+
},
216+
expected: anthropic.BetaMessage{Content: []anthropic.BetaContentBlockUnion{
217+
{Type: "text", Text: "This is a text block!"},
218+
}},
219+
},
220+
"text content block with citations": {
221+
events: []string{
222+
`{"type": "message_start", "message": {}}`,
223+
`{"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": "1 + 1"}}`,
224+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": " = 2"}}`,
225+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "citations_delta", "citation": {"type": "char_location", "cited_text": "1 + 1 = 2", "document_index": 0, "document_title": "Math Facts", "start_char_index": 300, "end_char_index": 310 }}}`,
226+
`{"type": "content_block_stop", "index": 0}`,
227+
`{"type": "message_stop"}`,
228+
},
229+
expected: anthropic.BetaMessage{Content: []anthropic.BetaContentBlockUnion{
230+
{Type: "text", Text: "1 + 1 = 2", Citations: []anthropic.BetaTextCitationUnion{{
231+
Type: "char_location",
232+
CitedText: "1 + 1 = 2",
233+
DocumentIndex: 0,
234+
DocumentTitle: "Math Facts",
235+
StartCharIndex: 300,
236+
EndCharIndex: 310,
237+
}}},
238+
}},
239+
},
240+
"tool use block": {
241+
events: []string{
242+
`{"type": "message_start", "message": {}}`,
243+
`{"type": "content_block_start", "index": 0, "content_block": {"type": "tool_use", "id": "toolu_id", "name": "tool_name", "input": {}}}`,
244+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": "{\"argument\":"}}`,
245+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": " \"value\"}"}}`,
246+
`{"type": "content_block_stop", "index": 0}`,
247+
`{"type": "message_stop"}`,
248+
},
249+
expected: anthropic.BetaMessage{Content: []anthropic.BetaContentBlockUnion{
250+
{Type: "tool_use", ID: "toolu_id", Name: "tool_name", Input: []byte(`{"argument": "value"}`)},
251+
}},
252+
},
253+
"tool use block with no params": {
254+
events: []string{
255+
`{"type": "message_start", "message": {}}`,
256+
`{"type": "content_block_start": "index": 0, "content_block": {"type": "tool_use", "id": "toolu_id", "name": "tool_name", input: {}}}`,
257+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": ""}}`,
258+
`{"type": "content_block_stop", "index": 0}`,
259+
`{"type": "message_stop"}`,
260+
},
261+
expected: anthropic.BetaMessage{Content: []anthropic.BetaContentBlockUnion{
262+
{Type: "tool_use", ID: "toolu_id", Name: "tool_name"},
263+
}},
264+
},
265+
"server tool use block": {
266+
events: []string{
267+
`{"type": "message_start", "message": {}}`,
268+
`{"type": "content_block_start": "index": 0, "content_block": {"type": "server_tool_use", "id": "srvtoolu_id", "name": "web_search", input: {}}}`,
269+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": ""}}`,
270+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": "{\"query\": \"weat"}}`,
271+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": "her\"}"}}`,
272+
`{"type": "content_block_stop", "index": 0}`,
273+
`{"type": "message_stop"}`,
274+
},
275+
expected: anthropic.BetaMessage{Content: []anthropic.BetaContentBlockUnion{
276+
{Type: "server_tool_use", ID: "srvtoolu_id", Name: "web_search", Input: []byte(`{"query": "weather"}`)},
277+
}},
278+
},
279+
"thinking block": {
280+
events: []string{
281+
`{"type": "message_start", "message": {}}`,
282+
`{"type": "content_block_start", "index": 0, "content_block": {"type": "thinking", "thinking": "Let me think..."}}`,
283+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "
284+
First, let's try this..."}}`,
285+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "
286+
Therefore, the answer is..."}}`,
287+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "signature_delta", "signature": "ThinkingSignature"}}`,
288+
`{"type": "content_block_stop", "index": 0}`,
289+
`{"type": "message_stop"}`,
290+
},
291+
expected: anthropic.BetaMessage{Content: []anthropic.BetaContentBlockUnion{
292+
{Type: "thinking", Thinking: "Let me think...\nFirst, let's try this...\nTherefore, the answer is...", Signature: "ThinkingSignature"},
293+
}},
294+
},
295+
"redacted thinking block": {
296+
events: []string{
297+
`{"type": "message_start", "message": {}}`,
298+
`{"type": "content_block_start", "index": 0, "content_block": {"type": "redacted_thinking", "data": "Redacted"}}`,
299+
`{"type": "content_block_stop", "index": 0}`,
300+
`{"type": "message_stop"}`,
301+
},
302+
expected: anthropic.BetaMessage{Content: []anthropic.BetaContentBlockUnion{
303+
{Type: "redacted_thinking", Data: "Redacted"},
304+
}},
305+
},
306+
"multiple content blocks": {
307+
events: []string{
308+
`{"type": "message_start", "message": {}}`,
309+
`{"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": "Let me look up "}}`,
310+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "the weather for "}}`,
311+
`{"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta": "text": "you."}}`,
312+
`{"type": "content_block_stop", "index": 0}`,
313+
`{"type": "content_block_start", "index": 1, "content_block": {"type": "thinking", "thinking": ""}}`,
314+
`{"type": "content_block_delta", "index": 1, "delta": {"type": "thinking_delta", "thinking": "I can look this "}}`,
315+
`{"type": "content_block_delta", "index": 1, "delta": {"type": "thinking_delta", "thinking": "up using a tool."}}`,
316+
`{"type": "content_block_stop", "index": 1}`,
317+
`{"type": "content_block_start", "index": 2, "content_block": {"type": "tool_use", "id": "toolu_id", "name": "get_weather", "input": {}}}`,
318+
`{"type": "content_block_delta", "index": 2, "delta": {"type": "input_json_delta", "partial_json": "{\"city\": "}}`,
319+
`{"type": "content_block_delta", "index": 2, "delta": {"type": "input_json_delta", "partial_json": "\"Los Angeles\"}"}}`,
320+
`{"type": "content_block_stop", "index": 2}`,
321+
`{"type": "content_block_start", "index": 3, "content_block": {"type": "text", "text": ""}}`,
322+
`{"type": "content_block_delta", "index": 3, "delta": {"type": "text_delta", "text": "The weather in Los Angeles"}}`,
323+
`{"type": "content_block_delta", "index": 3, "delta": {"type": "text_delta", "text": " is 85 degrees Fahrenheit!"}}`,
324+
`{"type": "content_block_stop", "index": 3"}`,
325+
`{"type": "message_stop"}`,
326+
},
327+
expected: anthropic.BetaMessage{Content: []anthropic.BetaContentBlockUnion{
328+
{Type: "text", Text: "Let me look up the weather for you."},
329+
{Type: "thinking", Thinking: "I can look this up using a tool."},
330+
{Type: "tool_use", ID: "toolu_id", Name: "get_weather", Input: []byte(`{"city": "Los Angeles"}`)},
331+
{Type: "text", Text: "The weather in Los Angeles is 85 degrees Fahrenheit!"},
332+
}},
333+
},
334+
} {
335+
t.Run(name, func(t *testing.T) {
336+
message := anthropic.BetaMessage{}
337+
for _, eventStr := range testCase.events {
338+
event := anthropic.BetaRawMessageStreamEventUnion{}
339+
err := (&event).UnmarshalJSON([]byte(eventStr))
340+
if err != nil {
341+
t.Fatal(err)
342+
}
343+
(&message).Accumulate(event)
344+
}
345+
marshaledMessage, err := json.Marshal(message)
346+
if err != nil {
347+
t.Fatal(err)
348+
}
349+
marshaledExpectedMessage, err := json.Marshal(testCase.expected)
350+
if err != nil {
351+
t.Fatal(err)
352+
}
353+
if string(marshaledMessage) != string(marshaledExpectedMessage) {
354+
t.Fatalf("Mismatched message: expected %s but got %s", marshaledExpectedMessage, marshaledMessage)
355+
}
356+
})
357+
}
358+
}

examples/message-mcp-streaming/main.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,44 @@ package main
33
import (
44
"context"
55
"fmt"
6-
"github.com/anthropics/anthropic-sdk-go/option"
7-
86
"github.com/anthropics/anthropic-sdk-go"
7+
"github.com/anthropics/anthropic-sdk-go/option"
8+
"github.com/anthropics/anthropic-sdk-go/packages/param"
99
)
1010

1111
func main() {
12-
client := anthropic.NewClient(option.WithHeader("anthropic-beta", "mcp-client-2025-04-04"))
13-
14-
content := "Use the eBird API to fetch the hotspot details of McGolrick park (L2987624)"
12+
client := anthropic.NewClient(option.WithHeader("anthropic-beta", anthropic.AnthropicBetaMCPClient2025_04_04))
1513

1614
mcpServers := []anthropic.BetaRequestMCPServerURLDefinitionParam{
1715
{
18-
URL: "https://remote-ebird-mcp-server-authless.dev-66f.workers.dev/sse",
19-
Name: "ebird",
16+
URL: "http://example-server.modelcontextprotocol.io/sse",
17+
Name: "example",
18+
AuthorizationToken: param.NewOpt("YOUR_TOKEN"),
2019
ToolConfiguration: anthropic.BetaRequestMCPServerToolConfigurationParam{
21-
Enabled: anthropic.Bool(true),
20+
Enabled: anthropic.Bool(true),
21+
AllowedTools: []string{"echo", "add"},
2222
},
2323
},
2424
}
2525

2626
stream := client.Beta.Messages.NewStreaming(context.TODO(), anthropic.BetaMessageNewParams{
2727
MaxTokens: 1024,
2828
Messages: []anthropic.BetaMessageParam{
29-
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(content)),
29+
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("what is 1+1?")),
3030
},
3131
MCPServers: mcpServers,
3232
Model: anthropic.ModelClaude3_7Sonnet20250219,
3333
StopSequences: []string{"```\n"},
3434
})
3535

36-
//message := anthropic.BetaMessage{}
37-
36+
message := anthropic.BetaMessage{}
3837
for stream.Next() {
3938
event := stream.Current()
40-
//err := message.Accumulate(event)
39+
err := message.Accumulate(event)
40+
if err != nil {
41+
fmt.Printf("error accumulating event: %v\n", err)
42+
continue
43+
}
4144

4245
switch eventVariant := event.AsAny().(type) {
4346
case anthropic.BetaRawMessageDeltaEvent:

internal/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
package internal
44

5-
const PackageVersion = "1.2.0" // x-release-please-version
5+
const PackageVersion = "1.2.1" // x-release-please-version

0 commit comments

Comments
 (0)