1package message
2
3import (
4 "encoding/base64"
5)
6
7type MessageRole string
8
9const (
10 Assistant MessageRole = "assistant"
11 User MessageRole = "user"
12 System MessageRole = "system"
13 Tool MessageRole = "tool"
14)
15
16type ContentPart interface {
17 isPart()
18}
19
20type ReasoningContent struct {
21 Thinking string `json:"thinking"`
22}
23
24func (tc ReasoningContent) String() string {
25 return tc.Thinking
26}
27func (ReasoningContent) isPart() {}
28
29type TextContent struct {
30 Text string `json:"text"`
31}
32
33func (tc TextContent) String() string {
34 return tc.Text
35}
36
37func (TextContent) isPart() {}
38
39type ImageURLContent struct {
40 URL string `json:"url"`
41 Detail string `json:"detail,omitempty"`
42}
43
44func (iuc ImageURLContent) String() string {
45 return iuc.URL
46}
47
48func (ImageURLContent) isPart() {}
49
50type BinaryContent struct {
51 MIMEType string
52 Data []byte
53}
54
55func (bc BinaryContent) String() string {
56 base64Encoded := base64.StdEncoding.EncodeToString(bc.Data)
57 return "data:" + bc.MIMEType + ";base64," + base64Encoded
58}
59
60func (BinaryContent) isPart() {}
61
62type ToolCall struct {
63 ID string `json:"id"`
64 Name string `json:"name"`
65 Input string `json:"input"`
66 Type string `json:"type"`
67 Finished bool `json:"finished"`
68}
69
70func (ToolCall) isPart() {}
71
72type ToolResult struct {
73 ToolCallID string `json:"tool_call_id"`
74 Name string `json:"name"`
75 Content string `json:"content"`
76 IsError bool `json:"is_error"`
77}
78
79func (ToolResult) isPart() {}
80
81type Finish struct {
82 Reason string `json:"reason"`
83}
84
85func (Finish) isPart() {}
86
87type Message struct {
88 ID string
89 Role MessageRole
90 SessionID string
91 Parts []ContentPart
92
93 CreatedAt int64
94 UpdatedAt int64
95}
96
97func (m *Message) Content() TextContent {
98 for _, part := range m.Parts {
99 if c, ok := part.(TextContent); ok {
100 return c
101 }
102 }
103 return TextContent{}
104}
105
106func (m *Message) ReasoningContent() ReasoningContent {
107 for _, part := range m.Parts {
108 if c, ok := part.(ReasoningContent); ok {
109 return c
110 }
111 }
112 return ReasoningContent{}
113}
114
115func (m *Message) ImageURLContent() []ImageURLContent {
116 imageURLContents := make([]ImageURLContent, 0)
117 for _, part := range m.Parts {
118 if c, ok := part.(ImageURLContent); ok {
119 imageURLContents = append(imageURLContents, c)
120 }
121 }
122 return imageURLContents
123}
124
125func (m *Message) BinaryContent() []BinaryContent {
126 binaryContents := make([]BinaryContent, 0)
127 for _, part := range m.Parts {
128 if c, ok := part.(BinaryContent); ok {
129 binaryContents = append(binaryContents, c)
130 }
131 }
132 return binaryContents
133}
134
135func (m *Message) ToolCalls() []ToolCall {
136 toolCalls := make([]ToolCall, 0)
137 for _, part := range m.Parts {
138 if c, ok := part.(ToolCall); ok {
139 toolCalls = append(toolCalls, c)
140 }
141 }
142 return toolCalls
143}
144
145func (m *Message) ToolResults() []ToolResult {
146 toolResults := make([]ToolResult, 0)
147 for _, part := range m.Parts {
148 if c, ok := part.(ToolResult); ok {
149 toolResults = append(toolResults, c)
150 }
151 }
152 return toolResults
153}
154
155func (m *Message) IsFinished() bool {
156 for _, part := range m.Parts {
157 if _, ok := part.(Finish); ok {
158 return true
159 }
160 }
161 return false
162}
163
164func (m *Message) FinishReason() string {
165 for _, part := range m.Parts {
166 if c, ok := part.(Finish); ok {
167 return c.Reason
168 }
169 }
170 return ""
171}
172
173func (m *Message) IsThinking() bool {
174 if m.ReasoningContent().Thinking != "" && m.Content().Text == "" && !m.IsFinished() {
175 return true
176 }
177 return false
178}
179
180func (m *Message) AppendContent(delta string) {
181 found := false
182 for i, part := range m.Parts {
183 if c, ok := part.(TextContent); ok {
184 m.Parts[i] = TextContent{Text: c.Text + delta}
185 found = true
186 }
187 }
188 if !found {
189 m.Parts = append(m.Parts, TextContent{Text: delta})
190 }
191}
192
193func (m *Message) AppendReasoningContent(delta string) {
194 found := false
195 for i, part := range m.Parts {
196 if c, ok := part.(ReasoningContent); ok {
197 m.Parts[i] = ReasoningContent{Thinking: c.Thinking + delta}
198 found = true
199 }
200 }
201 if !found {
202 m.Parts = append(m.Parts, ReasoningContent{Thinking: delta})
203 }
204}
205
206func (m *Message) AddToolCall(tc ToolCall) {
207 for i, part := range m.Parts {
208 if c, ok := part.(ToolCall); ok {
209 if c.ID == tc.ID {
210 m.Parts[i] = tc
211 return
212 }
213 }
214 }
215 m.Parts = append(m.Parts, tc)
216}
217
218func (m *Message) SetToolCalls(tc []ToolCall) {
219 for _, toolCall := range tc {
220 m.Parts = append(m.Parts, toolCall)
221 }
222}
223
224func (m *Message) AddToolResult(tr ToolResult) {
225 m.Parts = append(m.Parts, tr)
226}
227
228func (m *Message) SetToolResults(tr []ToolResult) {
229 for _, toolResult := range tr {
230 m.Parts = append(m.Parts, toolResult)
231 }
232}
233
234func (m *Message) AddFinish(reason string) {
235 m.Parts = append(m.Parts, Finish{Reason: reason})
236}
237
238func (m *Message) AddImageURL(url, detail string) {
239 m.Parts = append(m.Parts, ImageURLContent{URL: url, Detail: detail})
240}
241
242func (m *Message) AddBinary(mimeType string, data []byte) {
243 m.Parts = append(m.Parts, BinaryContent{MIMEType: mimeType, Data: data})
244}