message

package
v0.1.34 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package message owns the provider-neutral wire DTOs shared by every chat turn: a role-tagged Message carrying an ordered Content of [Part]s, plus the tool-call DTOs (ToolDefinition, ToolCall, ToolResult) and the JSON Schema helpers that build a ToolDefinition.

The package has no runtime: no providers, no executors, no middleware. It is the canonical shape of "what travels on the wire" — the layer above this package (core/inference for LLM calls, core/tool for tool execution, core/memory for stored turns) operates on these types but does not own them. Two consumers can therefore share a message log without pulling in each other's runtime.

The wire format is JSON. Content.MarshalJSON / Content.UnmarshalJSON tag each part with its PartKind so the same payload survives a round-trip through any tool that ignores FlowCraft's Go types.

The media subpackage holds the operation-neutral Source / Format types that ride inside multimodal Parts (images, audio, video). They are DTOs for the same reason and live here for the same reason.

Live media rides the same Part model: media.Stream / media.Pipe define a bounded pull transport, and a stream-backed Audio/Video Source is how a part exists while it is in flight. Streams are never serialized and never enter durable context or history — MaterializeContent converts them back into inline-byte parts at the commit boundary.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HasStreamSource added in v0.1.17

func HasStreamSource(content Content) bool

HasStreamSource reports whether any part in content carries a stream-backed media source. It is the cheap pre-check callers use before deciding whether MaterializeContent has work to do.

func Items

func Items(typ string) map[string]any

Items is the schema for the elements of a ToolArrayProperty: the common "array of plain-typed values" case without a raw map.

func MarshalPart

func MarshalPart(part Part) ([]byte, error)

MarshalPart encodes one canonical part in its wire form: a type-discriminated object, e.g. {"type":"image","source":...}. This is the same shape Content uses for each entry of its "parts" array, exported so single-part carriers (stream deltas, protocol envelopes) can reuse the canonical encoding instead of duplicating the switch.

func NewAudioStream added in v0.1.17

func NewAudioStream(stream Stream, mediaType string) (media.AudioSource, error)

NewAudioStream wraps a live part stream as an audio source, the message-level convenience for media.NewAudioStream instantiated over Part.

func NewVideoStream added in v0.1.17

func NewVideoStream(stream Stream, mediaType string) (media.VideoSource, error)

NewVideoStream wraps a live part stream as a video source, the message-level convenience for media.NewVideoStream instantiated over Part.

Types

type AudioPart

type AudioPart struct {
	Source         media.AudioSource  `json:"source"`
	Format         *media.AudioFormat `json:"format,omitempty"`
	DurationMillis *int64             `json:"duration_millis,omitempty"`
}

func (AudioPart) Clone

func (p AudioPart) Clone() Part

func (AudioPart) Kind

func (AudioPart) Kind() PartKind

func (AudioPart) Validate

func (p AudioPart) Validate() error

type Content

type Content struct {
	Parts []Part `json:"parts"`
}

Content is an ordered collection of canonical parts. Intent deliberately does not belong here; only InputContent may attach execution intent.

func MaterializeContent added in v0.1.17

func MaterializeContent(ctx context.Context, content Content) (Content, error)

MaterializeContent converts every stream-backed audio and video part in content into its durable form by draining the stream to completion:

  • a stream-backed AudioPart becomes one inline-byte AudioPart;
  • a stream-backed VideoPart becomes one inline-byte VideoPart.

Content without stream sources is returned unchanged. The stream is consumed exactly once: callers that also read it live must materialize from their own accumulated bytes instead (or ensure the stream is still readable here).

func (Content) Clone

func (c Content) Clone() Content

func (Content) MarshalJSON

func (c Content) MarshalJSON() ([]byte, error)

func (Content) Text

func (c Content) Text() string

Text concatenates the message's text parts in order. Non-text parts are skipped; it answers "what did the user/assistant say" without imposing prose structure on multi-modal content.

func (*Content) UnmarshalJSON

func (c *Content) UnmarshalJSON(data []byte) error

func (Content) Validate

func (c Content) Validate() error

type DataPart

type DataPart struct {
	MediaType string          `json:"media_type,omitempty"`
	Value     json.RawMessage `json:"value"`
}

func (DataPart) Clone

func (p DataPart) Clone() Part

func (DataPart) Kind

func (DataPart) Kind() PartKind

func (DataPart) Validate

func (p DataPart) Validate() error

type FilePart

type FilePart struct {
	URI       string `json:"uri"`
	MediaType string `json:"media_type,omitempty"`
	Name      string `json:"name,omitempty"`
}

func (FilePart) Clone

func (p FilePart) Clone() Part

func (FilePart) Kind

func (FilePart) Kind() PartKind

func (FilePart) Validate

func (p FilePart) Validate() error

type ImagePart

type ImagePart struct {
	Source media.ImageSource `json:"source"`
}

func (ImagePart) Clone

func (p ImagePart) Clone() Part

func (ImagePart) Kind

func (ImagePart) Kind() PartKind

func (ImagePart) Validate

func (p ImagePart) Validate() error

type Message

type Message struct {
	Role    Role    `json:"role"`
	Content Content `json:"content"`
}

func CloneMessages

func CloneMessages(msgs []Message) []Message

CloneMessages returns a deep copy of msgs. Nil stays nil so callers can preserve the usual JSON / len semantics.

func LastByRole

func LastByRole(msgs []Message, role Role) (Message, bool)

LastByRole returns the last message in msgs whose Role matches role. The boolean is false when no such message exists. The returned Message is the slice element itself (not a deep copy); callers that intend to mutate it should call Message.Clone first.

Typical use is for graph nodes that need to read a single role-scoped turn from a board channel — e.g. "the latest user message on MainChannel" — without re-implementing the reverse scan everywhere.

func NewTextMessage

func NewTextMessage(role Role, text string) Message

NewTextMessage builds a message carrying a single text part.

func (Message) Clone

func (m Message) Clone() Message

func (Message) HasToolCalls

func (m Message) HasToolCalls() bool

func (Message) ToolCalls

func (m Message) ToolCalls() []ToolCall

func (Message) ToolResults

func (m Message) ToolResults() []ToolResult

func (Message) Validate

func (m Message) Validate() error

type Part

type Part interface {
	Kind() PartKind
	Clone() Part
	Validate() error
	// contains filtered or unexported methods
}

Part is the sealed canonical content union. Each operation validates which kinds it accepts; for example, Embed currently accepts only text and image.

func MaterializePart added in v0.1.17

func MaterializePart(ctx context.Context, part Part) ([]Part, error)

MaterializePart returns part in its durable form. Stream-backed audio and video parts are drained into inline-byte parts; every other part is returned unchanged. See MaterializeContent for the rules.

func NormalizePart

func NormalizePart(part Part) (Part, error)

NormalizePart returns part in its canonical value form. Both T and *T satisfy Part, so values may cross runtime boundaries as either form; NormalizePart collapses pointers back to values and leaves value parts untouched. This lets callers switch on the canonical part types without handling pointer duplicates.

It returns an error if part is nil (including a typed nil pointer) or is not one of the canonical part types.

func UnmarshalPart

func UnmarshalPart(data []byte) (Part, error)

UnmarshalPart decodes one type-discriminated wire object back into its canonical part value. Unknown "type" values are rejected so a typo cannot silently decode into a zero part.

type PartKind

type PartKind string
const (
	PartText       PartKind = "text"
	PartImage      PartKind = "image"
	PartAudio      PartKind = "audio"
	PartVideo      PartKind = "video"
	PartFile       PartKind = "file"
	PartData       PartKind = "data"
	PartToolCall   PartKind = "tool_call"
	PartToolResult PartKind = "tool_result"
	PartReasoning  PartKind = "reasoning"
)

func (PartKind) Validate added in v0.1.22

func (k PartKind) Validate() error

Validate reports whether k is one of the canonical content part kinds.

type Pipe added in v0.1.17

type Pipe = media.Pipe[Part]

Pipe is the bounded, buffered implementation of Stream. Producers Send into it (blocking when the buffer is full, which is the backpressure contract); consumers Read from it. See media.Pipe for the full Close / Interrupt semantics.

func NewPartPipe added in v0.1.17

func NewPartPipe(bufferSize int) *Pipe

NewPartPipe creates a bounded part pipe with the given buffer capacity.

type ReasoningPart

type ReasoningPart struct {
	Text      string `json:"text,omitempty"`
	Signature string `json:"signature,omitempty"`
	ID        string `json:"id,omitempty"`
}

ReasoningPart is one provider reasoning trace: the thinking a model produced while composing the answer. It is a trace, not a requested artifact — reasoning-capable models emit it whether or not the request set a reasoning intent, so responses may always carry it.

Text holds the visible reasoning; providers that hide the content (Anthropic redacted_thinking, OpenAI encrypted reasoning without a summary) deliver an empty Text. Signature is the provider-issued opaque verification payload (Anthropic signature / redacted data, OpenAI encrypted_content): providers that sign reasoning require it verbatim when the part round-trips through conversation context, so consumers building agent loops must preserve the whole part. The convention Text=="" with Signature!="" therefore means "reasoning happened, content withheld" — it is derived, never a separate flag. ID is the provider-issued trace identifier (OpenAI reasoning item ids); providers that address traces by id require it on round-trip, and providers without item ids (Anthropic thinking blocks) leave it empty.

func (ReasoningPart) Clone

func (p ReasoningPart) Clone() Part

func (ReasoningPart) Kind

func (ReasoningPart) Kind() PartKind

func (ReasoningPart) Validate

func (p ReasoningPart) Validate() error

type Role

type Role string
const (
	RoleSystem    Role = "system"
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	RoleTool      Role = "tool"
)

type SchemaBuilder

type SchemaBuilder struct {
	// contains filtered or unexported fields
}

SchemaBuilder constructs a ToolDefinition using a fluent API.

func DefineSchema

func DefineSchema(name, description string, props ...ToolPropertyDef) *SchemaBuilder

DefineSchema starts building a ToolDefinition with the given properties.

func (*SchemaBuilder) Build

func (b *SchemaBuilder) Build() ToolDefinition

Build returns the final ToolDefinition.

func (*SchemaBuilder) DisallowAdditionalProperties

func (b *SchemaBuilder) DisallowAdditionalProperties() *SchemaBuilder

DisallowAdditionalProperties marks the object schema as closed: providers that honor additionalProperties will reject arguments outside the declared properties.

func (*SchemaBuilder) Required

func (b *SchemaBuilder) Required(names ...string) *SchemaBuilder

Required marks the given property names as required in the JSON Schema. Duplicate names are silently ignored.

type Stream added in v0.1.17

type Stream = media.Stream[Part]

Stream is the canonical pull-based transport for live content: the message-level instantiation of media.Stream over Part.

A Stream is how a message "exists" while it is in flight — for example the audio/video input of a live session. It is deliberately not a new DTO: the items are ordinary Part values, and a stream-backed part is materialized back into ordinary parts before it enters durable context or history (see MaterializeContent).

type TextPart

type TextPart struct {
	Text string `json:"text"`
}

func (TextPart) Clone

func (p TextPart) Clone() Part

func (TextPart) Kind

func (TextPart) Kind() PartKind

func (TextPart) Validate

func (TextPart) Validate() error

type ToolCall

type ToolCall struct {
	ID        string          `json:"id"`
	Name      string          `json:"name"`
	Arguments json.RawMessage `json:"arguments"`
}

ToolCall is a provider-requested tool invocation. Arguments is always a JSON object and remains opaque to the inference package.

func NewToolCall

func NewToolCall(id, name string, args any) (ToolCall, error)

NewToolCall encodes args as the argument object for a tool invocation.

func (ToolCall) Clone

func (c ToolCall) Clone() ToolCall

func (ToolCall) Validate

func (c ToolCall) Validate() error

type ToolCallPart

type ToolCallPart struct {
	Call ToolCall `json:"call"`
}

func (ToolCallPart) Clone

func (p ToolCallPart) Clone() Part

func (ToolCallPart) Kind

func (ToolCallPart) Kind() PartKind

func (ToolCallPart) Validate

func (p ToolCallPart) Validate() error

type ToolDefinition

type ToolDefinition struct {
	Name        string          `json:"name"`
	Description string          `json:"description,omitempty"`
	InputSchema json.RawMessage `json:"input_schema"`
}

ToolDefinition describes a callable tool. InputSchema must be a JSON Schema object; it is kept as raw JSON so inference requests never expose an untyped provider-parameter map.

func (ToolDefinition) Clone

func (d ToolDefinition) Clone() ToolDefinition

func (ToolDefinition) Validate

func (d ToolDefinition) Validate() error

type ToolPropertyDef

type ToolPropertyDef struct {
	// contains filtered or unexported fields
}

ToolPropertyDef describes a single JSON Schema property.

func ToolArrayProperty

func ToolArrayProperty(name, description string, items map[string]any) ToolPropertyDef

ToolArrayProperty creates an array property with item schema.

func ToolEnumProperty

func ToolEnumProperty(name, typ, description string, values ...string) ToolPropertyDef

ToolEnumProperty creates a property restricted to a set of string values.

func ToolObjectProperty

func ToolObjectProperty(name, description string, properties map[string]any) ToolPropertyDef

ToolObjectProperty creates an object property with nested properties schema.

func ToolProperty

func ToolProperty(name, typ, description string) ToolPropertyDef

ToolProperty creates a simple typed property definition.

func ToolPropertyWithDefault

func ToolPropertyWithDefault(name, typ, description string, defaultVal any) ToolPropertyDef

ToolPropertyWithDefault creates a typed property with a default value.

func ToolStringMapProperty

func ToolStringMapProperty(name, description string) ToolPropertyDef

ToolStringMapProperty creates an object property whose values must be strings.

type ToolResult

type ToolResult struct {
	CallID  string `json:"call_id"`
	Content string `json:"content"`
	IsError bool   `json:"is_error,omitempty"`
}

ToolResult carries one tool execution result back into a chat operation.

func (ToolResult) Validate

func (r ToolResult) Validate() error

type ToolResultPart

type ToolResultPart struct {
	Result ToolResult `json:"result"`
}

func (ToolResultPart) Clone

func (p ToolResultPart) Clone() Part

func (ToolResultPart) Kind

func (ToolResultPart) Kind() PartKind

func (ToolResultPart) Validate

func (p ToolResultPart) Validate() error

type VideoPart

type VideoPart struct {
	Source media.VideoSource `json:"source"`
}

func (VideoPart) Clone

func (p VideoPart) Clone() Part

func (VideoPart) Kind

func (VideoPart) Kind() PartKind

func (VideoPart) Validate

func (p VideoPart) Validate() error

Directories

Path Synopsis
Package media defines operation-neutral image, audio, and video value types.
Package media defines operation-neutral image, audio, and video value types.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL