content.go

  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}