Skip to content

Commit ce37f7e

Browse files
committed
Updated Anthropic
1 parent f751b7b commit ce37f7e

File tree

17 files changed

+390
-469
lines changed

17 files changed

+390
-469
lines changed

pkg/anthropic/client.go

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
/*
2-
anthropic implements an API client for anthropic (https://docs.anthropic.com/en/api/getting-started)
2+
anthropic implements an API client for anthropic
3+
https://docs.anthropic.com/en/api/getting-started
34
*/
45
package anthropic
56

67
import (
78
// Packages
8-
"context"
9-
109
client "github.com/mutablelogic/go-client"
1110
llm "github.com/mutablelogic/go-llm"
11+
impl "github.com/mutablelogic/go-llm/pkg/internal/impl"
1212
)
1313

1414
///////////////////////////////////////////////////////////////////////////////
1515
// TYPES
1616

1717
type Client struct {
1818
*client.Client
19-
cache map[string]llm.Model
19+
*impl.ModelCache
2020
}
2121

2222
var _ llm.Agent = (*Client)(nil)
@@ -37,14 +37,15 @@ const (
3737
func New(ApiKey string, opts ...client.ClientOpt) (*Client, error) {
3838
// Create client
3939
opts = append(opts, client.OptEndpoint(endPoint))
40-
opts = append(opts, client.OptHeader("x-api-key", ApiKey), client.OptHeader("anthropic-version", defaultVersion))
40+
opts = append(opts, client.OptHeader("x-api-key", ApiKey))
41+
opts = append(opts, client.OptHeader("anthropic-version", defaultVersion))
4142
client, err := client.New(opts...)
4243
if err != nil {
4344
return nil, err
4445
}
4546

4647
// Return the client
47-
return &Client{client, nil}, nil
48+
return &Client{client, impl.NewModelCache()}, nil
4849
}
4950

5051
///////////////////////////////////////////////////////////////////////////////
@@ -54,36 +55,3 @@ func New(ApiKey string, opts ...client.ClientOpt) (*Client, error) {
5455
func (*Client) Name() string {
5556
return defaultName
5657
}
57-
58-
// Return the models
59-
func (anthropic *Client) Models(ctx context.Context) ([]llm.Model, error) {
60-
// Cache models
61-
if anthropic.cache == nil {
62-
models, err := anthropic.ListModels(ctx)
63-
if err != nil {
64-
return nil, err
65-
}
66-
anthropic.cache = make(map[string]llm.Model, len(models))
67-
for _, model := range models {
68-
anthropic.cache[model.Name()] = model
69-
}
70-
}
71-
72-
// Return models
73-
result := make([]llm.Model, 0, len(anthropic.cache))
74-
for _, model := range anthropic.cache {
75-
result = append(result, model)
76-
}
77-
return result, nil
78-
}
79-
80-
// Return a model by name, or nil if not found.
81-
// Panics on error.
82-
func (anthropic *Client) Model(ctx context.Context, name string) llm.Model {
83-
if anthropic.cache == nil {
84-
if _, err := anthropic.Models(ctx); err != nil {
85-
panic(err)
86-
}
87-
}
88-
return anthropic.cache[name]
89-
}

pkg/anthropic/content.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package anthropic
2+
3+
import (
4+
"encoding/json"
5+
"strings"
6+
7+
llm "github.com/mutablelogic/go-llm"
8+
)
9+
10+
///////////////////////////////////////////////////////////////////////////////
11+
// TYPES
12+
13+
type Content struct {
14+
Type string `json:"type"` // image, document, text, tool_use
15+
ContentText
16+
ContentAttachment
17+
*ContentTool
18+
ContentToolResult
19+
CacheControl *cachecontrol `json:"cache_control,omitempty"` // ephemeral
20+
}
21+
22+
type ContentText struct {
23+
Text string `json:"text,omitempty"` // text content
24+
}
25+
26+
type ContentTool struct {
27+
Id string `json:"id,omitempty"` // tool id
28+
Name string `json:"name,omitempty"` // tool name
29+
Input map[string]any `json:"input"` // tool input
30+
InputJson string `json:"partial_json,omitempty"` // partial json input (for streaming)
31+
}
32+
33+
type ContentAttachment struct {
34+
Title string `json:"title,omitempty"` // title of the document
35+
Context string `json:"context,omitempty"` // context of the document
36+
Citations *contentcitation `json:"citations,omitempty"` // citations of the document
37+
Source *contentsource `json:"source,omitempty"` // image or document content
38+
}
39+
40+
type ContentToolResult struct {
41+
Id string `json:"tool_use_id,omitempty"` // tool id
42+
Content any `json:"content,omitempty"`
43+
}
44+
45+
type contentsource struct {
46+
Type string `json:"type"` // base64 or text
47+
MediaType string `json:"media_type"` // image/jpeg, image/png, image/gif, image/webp, application/pdf, text/plain
48+
Data any `json:"data"` // ...base64 or text encoded data
49+
}
50+
51+
type cachecontrol struct {
52+
Type string `json:"type"` // ephemeral
53+
}
54+
55+
type contentcitation struct {
56+
Enabled bool `json:"enabled"` // true
57+
}
58+
59+
///////////////////////////////////////////////////////////////////////////////
60+
// GLOBALS
61+
62+
var (
63+
supportedAttachments = map[string]string{
64+
"image/jpeg": "image",
65+
"image/png": "image",
66+
"image/gif": "image",
67+
"image/webp": "image",
68+
"application/pdf": "document",
69+
"text/plain": "text",
70+
}
71+
)
72+
73+
///////////////////////////////////////////////////////////////////////////////
74+
// LIFECYCLE
75+
76+
// Return a content object with text content
77+
func NewTextContent(v string) *Content {
78+
return &Content{
79+
Type: "text",
80+
ContentText: ContentText{
81+
Text: v,
82+
},
83+
}
84+
}
85+
86+
// Return a content object with tool result
87+
func NewToolResultContent(v llm.ToolResult) *Content {
88+
content := new(Content)
89+
content.Type = "tool_result"
90+
content.ContentToolResult.Id = v.Call().Id()
91+
// content.ContentToolResult.Name = v.Call().Name()
92+
93+
// We only support JSON encoding for the moment
94+
data, err := json.Marshal(v.Value())
95+
if err != nil {
96+
content.ContentToolResult.Content = err.Error()
97+
} else {
98+
content.ContentToolResult.Content = string(data)
99+
}
100+
101+
return content
102+
}
103+
104+
// Make attachment content
105+
func NewAttachment(attachment *llm.Attachment, ephemeral, citations bool) (*Content, error) {
106+
// Detect mimetype
107+
mimetype := attachment.Type()
108+
if strings.HasPrefix(mimetype, "text/") {
109+
// Switch to text/plain - TODO: charsets?
110+
mimetype = "text/plain"
111+
}
112+
113+
// Check supported mimetype
114+
typ, exists := supportedAttachments[mimetype]
115+
if !exists {
116+
return nil, llm.ErrBadParameter.Withf("unsupported or undetected mimetype %q", mimetype)
117+
}
118+
119+
// Create attachment
120+
content := new(Content)
121+
content.Type = typ
122+
if ephemeral {
123+
content.CacheControl = &cachecontrol{Type: "ephemeral"}
124+
}
125+
126+
// Handle by type
127+
switch typ {
128+
case "text":
129+
content.Type = "document"
130+
content.Title = attachment.Filename()
131+
content.Source = &contentsource{
132+
Type: "text",
133+
MediaType: mimetype,
134+
Data: string(attachment.Data()),
135+
}
136+
if citations {
137+
content.Citations = &contentcitation{Enabled: true}
138+
}
139+
case "document":
140+
content.Source = &contentsource{
141+
Type: "base64",
142+
MediaType: mimetype,
143+
Data: attachment.Data(),
144+
}
145+
if citations {
146+
content.Citations = &contentcitation{Enabled: true}
147+
}
148+
case "image":
149+
content.Source = &contentsource{
150+
Type: "base64",
151+
MediaType: mimetype,
152+
Data: attachment.Data(),
153+
}
154+
default:
155+
return nil, llm.ErrBadParameter.Withf("unsupported attachment type %q", typ)
156+
}
157+
158+
// Return success
159+
return content, nil
160+
}

0 commit comments

Comments
 (0)