Files
gnoma/internal/message/message.go
T
vikingowl d37cc2dad3 feat(message): add ContentImage type for inline image bytes
Extends the Content discriminated union with a fifth variant for
inline image payloads. Image carries the raw bytes (captured at
user-input time so the message snapshot is self-contained and
survives source-file deletion), the IANA media type for the
provider's image part, and the original path for logging.

HasImages() lets providers decide whether to fall back to a
text-only representation; providers that don't know about
ContentImage will simply skip those blocks via TextContent().
2026-05-22 11:50:20 +02:00

102 lines
2.1 KiB
Go

package message
import "strings"
// Role identifies the sender of a message.
type Role string
const (
RoleUser Role = "user"
RoleAssistant Role = "assistant"
RoleSystem Role = "system"
)
// Message represents a single turn in the conversation.
type Message struct {
Role Role `json:"role"`
Content []Content `json:"content"`
}
func NewUserText(text string) Message {
return Message{
Role: RoleUser,
Content: []Content{NewTextContent(text)},
}
}
func NewAssistantText(text string) Message {
return Message{
Role: RoleAssistant,
Content: []Content{NewTextContent(text)},
}
}
func NewAssistantContent(blocks ...Content) Message {
return Message{
Role: RoleAssistant,
Content: blocks,
}
}
func NewSystemText(text string) Message {
return Message{
Role: RoleSystem,
Content: []Content{NewTextContent(text)},
}
}
func NewToolResults(results ...ToolResult) Message {
content := make([]Content, len(results))
for i, r := range results {
content[i] = NewToolResultContent(r)
}
return Message{
Role: RoleUser,
Content: content,
}
}
// HasToolCalls returns true if any content block is a tool call.
func (m Message) HasToolCalls() bool {
for _, c := range m.Content {
if c.Type == ContentToolCall {
return true
}
}
return false
}
// ToolCalls extracts all tool call blocks.
func (m Message) ToolCalls() []ToolCall {
var calls []ToolCall
for _, c := range m.Content {
if c.Type == ContentToolCall && c.ToolCall != nil {
calls = append(calls, *c.ToolCall)
}
}
return calls
}
// TextContent concatenates all text blocks.
func (m Message) TextContent() string {
var b strings.Builder
for _, c := range m.Content {
if c.Type == ContentText {
b.WriteString(c.Text)
}
}
return b.String()
}
// HasImages reports whether any content block in the message is an inline
// image. Providers that don't support vision can use this to decide whether
// to fall back to a text-only representation.
func (m Message) HasImages() bool {
for _, c := range m.Content {
if c.Type == ContentImage {
return true
}
}
return false
}