-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathcontent_api.go
279 lines (242 loc) · 8.35 KB
/
content_api.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
package mcp_golang
import (
"encoding/json"
"fmt"
"github.com/tidwall/sjson"
)
type Role string
const RoleAssistant Role = "assistant"
const RoleUser Role = "user"
type Annotations struct {
// Describes who the intended customer of this object or data is.
//
// It can include multiple entries to indicate ToolResponse useful for multiple
// audiences (e.g., `["user", "assistant"]`).
Audience []Role `json:"audience,omitempty" yaml:"audience,omitempty" mapstructure:"audience,omitempty"`
// Describes how important this data is for operating the server.
//
// A value of 1 means "most important," and indicates that the data is
// effectively required, while 0 means "least important," and indicates that
// the data is entirely optional.
Priority *float64 `json:"priority,omitempty" yaml:"priority,omitempty" mapstructure:"priority,omitempty"`
}
// Text provided to or from an LLM.
type TextContent struct {
// The text ToolResponse of the message.
Text string `json:"text" yaml:"text" mapstructure:"text"`
}
// An image provided to or from an LLM.
type ImageContent struct {
// The base64-encoded image data.
Data string `json:"data" yaml:"data" mapstructure:"data"`
// The MIME type of the image. Different providers may support different image
// types.
MimeType string `json:"mimeType" yaml:"mimeType" mapstructure:"mimeType"`
}
type embeddedResourceType string
const (
embeddedResourceTypeBlob embeddedResourceType = "blob"
embeddedResourceTypeText embeddedResourceType = "text"
)
type BlobResourceContents struct {
// A base64-encoded string representing the binary data of the item.
Blob string `json:"blob" yaml:"blob" mapstructure:"blob"`
// The MIME type of this resource, if known.
MimeType *string `json:"mimeType,omitempty" yaml:"mimeType,omitempty" mapstructure:"mimeType,omitempty"`
// The URI of this resource.
Uri string `json:"uri" yaml:"uri" mapstructure:"uri"`
}
type TextResourceContents struct {
// The MIME type of this resource, if known.
MimeType *string `json:"mimeType,omitempty" yaml:"mimeType,omitempty" mapstructure:"mimeType,omitempty"`
// The text of the item. This must only be set if the item can actually be
// represented as text (not binary data).
Text string `json:"text" yaml:"text" mapstructure:"text"`
// The URI of this resource.
Uri string `json:"uri" yaml:"uri" mapstructure:"uri"`
}
// The contents of a resource, embedded into a prompt or tool call result.
//
// It is up to the client how best to render embedded resources for the benefit
// of the LLM and/or the user.
type EmbeddedResource struct {
EmbeddedResourceType embeddedResourceType
TextResourceContents *TextResourceContents
BlobResourceContents *BlobResourceContents
}
// Custom JSON marshaling for EmbeddedResource
func (c EmbeddedResource) MarshalJSON() ([]byte, error) {
switch c.EmbeddedResourceType {
case embeddedResourceTypeBlob:
return json.Marshal(c.BlobResourceContents)
case embeddedResourceTypeText:
return json.Marshal(c.TextResourceContents)
default:
return nil, fmt.Errorf("unknown embedded resource type: %s", c.EmbeddedResourceType)
}
}
type ContentType string
const (
// The value is the value of the "type" field in the Content so do not change
ContentTypeText ContentType = "text"
ContentTypeImage ContentType = "image"
ContentTypeEmbeddedResource ContentType = "resource"
)
type Content struct {
Type ContentType
TextContent *TextContent
ImageContent *ImageContent
EmbeddedResource *EmbeddedResource
Annotations *Annotations
}
func (c *Content) UnmarshalJSON(b []byte) error {
type typeWrapper struct {
Type ContentType `json:"type" yaml:"type" mapstructure:"type"`
Text *string `json:"text" yaml:"text" mapstructure:"text"`
Image *string `json:"image" yaml:"image" mapstructure:"image"`
Annotations *Annotations `json:"annotations" yaml:"annotations" mapstructure:"annotations"`
EmbeddedResource *EmbeddedResource `json:"resource" yaml:"resource" mapstructure:"resource"`
}
var tw typeWrapper
err := json.Unmarshal(b, &tw)
if err != nil {
return err
}
switch tw.Type {
case ContentTypeText:
c.Type = ContentTypeText
case ContentTypeImage:
c.Type = ContentTypeImage
case ContentTypeEmbeddedResource:
c.Type = ContentTypeEmbeddedResource
default:
return fmt.Errorf("unknown content type: %s", tw.Type)
}
switch c.Type {
case ContentTypeText:
c.TextContent = &TextContent{Text: *tw.Text}
default:
return fmt.Errorf("unknown content type: %s", c.Type)
}
return nil
}
// Custom JSON marshaling for ToolResponse Content
func (c Content) MarshalJSON() ([]byte, error) {
var rawJson []byte
switch c.Type {
case ContentTypeText:
j, err := json.Marshal(c.TextContent)
if err != nil {
return nil, err
}
rawJson = j
case ContentTypeImage:
j, err := json.Marshal(c.ImageContent)
if err != nil {
return nil, err
}
rawJson = j
case ContentTypeEmbeddedResource:
j, err := json.Marshal(c.EmbeddedResource)
if err != nil {
return nil, err
}
rawJson = j
default:
return nil, fmt.Errorf("unknown content type: %s", c.Type)
}
// Add the type
rawJson, err := sjson.SetBytes(rawJson, "type", string(c.Type))
if err != nil {
return nil, err
}
// Add the annotations
if c.Annotations != nil {
marshal, err := json.Marshal(c.Annotations)
if err != nil {
return nil, err
}
rawJson, err = sjson.SetBytes(rawJson, "annotations", marshal)
if err != nil {
return nil, err
}
}
return rawJson, nil
}
func (c *Content) WithAnnotations(annotations Annotations) *Content {
c.Annotations = &annotations
return c
}
// newToolResponseSentError creates a new ToolResponse that represents an error.
// This is used to create a result that will be returned to the client as an error for a tool call.
func newToolResponseSentError(err error) *toolResponseSent {
return &toolResponseSent{
Error: err,
}
}
// newToolResponseSent creates a new toolResponseSent
func newToolResponseSent(response *ToolResponse) *toolResponseSent {
return &toolResponseSent{
Response: response,
}
}
// NewImageContent creates a new ToolResponse that is an image.
// The given data is base64-encoded
func NewImageContent(base64EncodedStringData string, mimeType string) *Content {
return &Content{
Type: ContentTypeImage,
ImageContent: &ImageContent{Data: base64EncodedStringData, MimeType: mimeType},
}
}
// NewTextContent creates a new ToolResponse that is a simple text string.
// The client will render this as a single string.
func NewTextContent(content string) *Content {
return &Content{
Type: ContentTypeText,
TextContent: &TextContent{Text: content},
}
}
// NewBlobResourceContent creates a new ToolResponse that is a blob of binary data.
// The given data is base64-encoded; the client will decode it.
// The client will render this as a blob; it will not be human-readable.
func NewBlobResourceContent(uri string, base64EncodedData string, mimeType string) *Content {
return &Content{
Type: ContentTypeEmbeddedResource,
EmbeddedResource: &EmbeddedResource{
EmbeddedResourceType: embeddedResourceTypeBlob,
BlobResourceContents: &BlobResourceContents{
Blob: base64EncodedData,
MimeType: &mimeType,
Uri: uri,
}},
}
}
// NewTextResourceContent creates a new ToolResponse that is an embedded resource of type "text".
// The given text is embedded in the response as a TextResourceContents, which
// contains the given MIME type and URI. The text is not base64-encoded.
func NewTextResourceContent(uri string, text string, mimeType string) *Content {
return &Content{
Type: ContentTypeEmbeddedResource,
EmbeddedResource: &EmbeddedResource{
EmbeddedResourceType: embeddedResourceTypeText,
TextResourceContents: &TextResourceContents{
MimeType: &mimeType, Text: text, Uri: uri,
}},
}
}
func NewTextEmbeddedResource(uri string, text string, mimeType string) *EmbeddedResource {
return &EmbeddedResource{
EmbeddedResourceType: embeddedResourceTypeText,
TextResourceContents: &TextResourceContents{
MimeType: &mimeType, Text: text, Uri: uri,
}}
}
func NewBlobEmbeddedResource(uri string, base64EncodedData string, mimeType string) *EmbeddedResource {
return &EmbeddedResource{
EmbeddedResourceType: embeddedResourceTypeBlob,
BlobResourceContents: &BlobResourceContents{
Blob: base64EncodedData,
MimeType: &mimeType,
Uri: uri,
}}
}