chat

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package chat defines the serializable provider-neutral chat protocol and its minimal synchronous Model and optional Streamer capabilities.

Construct messages and requests with NewSystemMessage, NewUserMessage, and NewRequest. Constructors validate their initial values; call Validate again after mutating exported fields. Options express only per-call overrides; ReasoningEffort carries the model-advertised intensity vocabulary without imposing one provider's closed enum, and OutputFormat describes the requested representation without adopting any provider's wire naming. Namespaced Extensions preserve provider data without expanding the shared protocol for every provider feature.

ToolDefinition describes wire schema only. Executable tools, registries, history, retries, middleware policy, and tool loops belong to higher-level modules. Protocol values therefore never retain callbacks, provider clients, or other runtime objects.

Example
package main

import (
	"fmt"

	"github.com/Tangerg/scope/core/chat"
)

func main() {
	request, err := chat.NewRequest(
		chat.NewSystemMessage("Answer concisely."),
		chat.NewUserMessage(chat.NewTextPart("What is a scope?")),
	)
	if err != nil {
		panic(err)
	}
	request.Options = chat.Options{Model: "provider-model"}

	fmt.Println(request.Messages[1].Text())
	fmt.Println(request.Options.Model)
}
Output:
What is a scope?
provider-model

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidMessage    = errors.New("chat: invalid message")
	ErrInvalidPart       = errors.New("chat: invalid part")
	ErrInvalidToolCall   = errors.New("chat: invalid tool call")
	ErrInvalidToolResult = errors.New("chat: invalid tool result")
)
View Source
var ErrInvalidOptions = errors.New("chat: invalid options")
View Source
var ErrInvalidOutputFormat = errors.New("chat: invalid output format")
View Source
var ErrInvalidRequest = errors.New("chat: invalid request")
View Source
var ErrInvalidResponse = errors.New("chat: invalid response")
View Source
var ErrInvalidToolDefinition = errors.New("chat: invalid tool definition")
View Source
var ErrInvalidUsage = errors.New("chat: invalid usage")

Functions

This section is empty.

Types

type CallMiddleware

type CallMiddleware func(next Model) Model

CallMiddleware wraps a Model with cross-cutting call behavior. Concrete logging, tracing, retry, history, and safety policy belong to upper modules; core/chat only owns this composition vocabulary.

type FinishReason

type FinishReason string

FinishReason explains why generation stopped. The empty value means that a streaming output has not finished yet.

const (
	FinishReasonStop          FinishReason = "stop"
	FinishReasonLength        FinishReason = "length"
	FinishReasonToolCalls     FinishReason = "tool_calls"
	FinishReasonContentFilter FinishReason = "content_filter"
	FinishReasonOther         FinishReason = "other"
)

func (FinishReason) String

func (f FinishReason) String() string

func (FinishReason) Valid

func (f FinishReason) Valid() bool

type Message

type Message struct {
	Role     Role         `json:"role"`
	Parts    []Part       `json:"parts"`
	Metadata metadata.Map `json:"metadata,omitzero"`
}

Message is one provider-neutral conversation entry. Parts retain their order so interleaved assistant text, reasoning, and tool calls round-trip. Clone recursively owns every mutable protocol value. Text projection concatenates only text parts and deliberately ignores reasoning, media, and tool payloads.

func NewAssistantMessage

func NewAssistantMessage(parts ...Part) Message

func NewSystemMessage

func NewSystemMessage(text string) Message

func NewToolMessage

func NewToolMessage(results ...ToolResult) Message

func NewUserMessage

func NewUserMessage(parts ...Part) Message

func (Message) Clone

func (m Message) Clone() Message

func (Message) MarshalJSON

func (m Message) MarshalJSON() ([]byte, error)

func (*Message) Text

func (m *Message) Text() string

func (*Message) UnmarshalJSON

func (m *Message) UnmarshalJSON(data []byte) error

func (Message) Validate

func (m Message) Validate() error

type Model

type Model interface {
	// Call performs one complete model exchange. It must reject an invalid
	// request before provider I/O, must not retain or mutate request, and
	// transfers ownership of the returned response to the caller. Context
	// cancellation remains identifiable through errors.Is.
	Call(ctx context.Context, request *Request) (*Response, error)
}

Model is the minimal synchronous chat capability. Implementations must validate request before provider I/O, honor context cancellation, and return a provider-neutral Response. Cancellation errors must retain context.Canceled or context.DeadlineExceeded for errors.Is.

Streaming, default configuration, and provider identity are independent concerns and deliberately are not methods of Model.

func Wrap

func Wrap(model Model, middlewares ...CallMiddleware) Model

Wrap composes call middlewares around model. The first middleware is the outermost wrapper. Nil entries are ignored so optional middleware can be supplied without a separate branch.

type ModelFunc

type ModelFunc func(ctx context.Context, request *Request) (*Response, error)

func (ModelFunc) Call

func (m ModelFunc) Call(ctx context.Context, request *Request) (*Response, error)

type Options

type Options struct {
	Model            string              `json:"model,omitempty"`
	OutputFormat     *OutputFormat       `json:"output_format,omitempty"`
	FrequencyPenalty *float64            `json:"frequency_penalty,omitempty"`
	MaxTokens        *int64              `json:"max_tokens,omitempty"`
	PresencePenalty  *float64            `json:"presence_penalty,omitempty"`
	ReasoningEffort  ReasoningEffort     `json:"reasoning_effort,omitempty"`
	Stop             []string            `json:"stop,omitempty"`
	Temperature      *float64            `json:"temperature,omitempty"`
	TopK             *int64              `json:"top_k,omitempty"`
	TopP             *float64            `json:"top_p,omitempty"`
	Extensions       metadata.Extensions `json:"extensions,omitzero"`
}

Options contains provider-neutral per-request generation overrides. Its zero value means provider defaults. Resolve overlays only explicitly populated fields, merges namespaced extensions, snapshots mutable values, and leaves both source values unchanged.

func (Options) Clone

func (o Options) Clone() Options

func (Options) MarshalJSON

func (o Options) MarshalJSON() ([]byte, error)

func (Options) Resolve

func (o Options) Resolve(override Options) (Options, error)

func (*Options) UnmarshalJSON

func (o *Options) UnmarshalJSON(data []byte) error

func (Options) Validate

func (o Options) Validate() error

type Output

type Output struct {
	Message      *Message        `json:"message,omitempty"`
	FinishReason FinishReason    `json:"finish_reason,omitempty"`
	Metadata     *OutputMetadata `json:"metadata,omitempty"`
}

Output is the single provider generation produced by a chat call. Message may be nil on a streaming chunk that only carries a finish reason or output metadata.

func NewOutput

func NewOutput(message *Message, finishReason FinishReason, outputMetadata *OutputMetadata) (*Output, error)

func (Output) MarshalJSON

func (o Output) MarshalJSON() ([]byte, error)

func (*Output) Text

func (o *Output) Text() string

func (*Output) UnmarshalJSON

func (o *Output) UnmarshalJSON(data []byte) error

func (*Output) Validate

func (o *Output) Validate() error

type OutputFormat

type OutputFormat struct {
	Type        OutputFormatType `json:"type"`
	Name        string           `json:"name,omitempty"`
	Description string           `json:"description,omitempty"`
	Schema      json.RawMessage  `json:"schema,omitempty"`
}

OutputFormat is the provider-neutral representation contract for one model result. Name, Description, and Schema belong only to OutputFormatJSONSchema. Provider adapters decode Schema into their native SDK shape when supported; otherwise FallbackInstruction derives the single equivalent prompt contract. Schema bytes are always snapshotted at construction and cloning boundaries.

func NewJSONSchemaOutputFormat

func NewJSONSchemaOutputFormat(name string, schema json.RawMessage) (OutputFormat, error)

func NewOutputFormat

func NewOutputFormat(formatType OutputFormatType) (OutputFormat, error)

func (*OutputFormat) Clone

func (o *OutputFormat) Clone() *OutputFormat

func (*OutputFormat) FallbackInstruction

func (o *OutputFormat) FallbackInstruction() (string, error)

FallbackInstruction returns an equivalent model instruction for adapters whose native protocol cannot represent o. A nil or text format needs no instruction.

func (OutputFormat) MarshalJSON

func (o OutputFormat) MarshalJSON() ([]byte, error)

func (*OutputFormat) SchemaAs

func (o *OutputFormat) SchemaAs[T any]() (T, error)

func (*OutputFormat) UnmarshalJSON

func (o *OutputFormat) UnmarshalJSON(data []byte) error

func (OutputFormat) Validate

func (o OutputFormat) Validate() error

type OutputFormatType

type OutputFormatType string

OutputFormatType identifies the representation requested for a chat result. Provider adapters map the format to a native control when available and may fall back to equivalent prompt instructions otherwise.

const (
	OutputFormatText       OutputFormatType = "text"
	OutputFormatJSON       OutputFormatType = "json"
	OutputFormatJSONSchema OutputFormatType = "json_schema"
)

type OutputMetadata

type OutputMetadata struct {
	Extra metadata.Map `json:"extra,omitzero"`
}

OutputMetadata holds provider-specific metadata for one generation output.

func (OutputMetadata) MarshalJSON

func (o OutputMetadata) MarshalJSON() ([]byte, error)

func (*OutputMetadata) UnmarshalJSON

func (o *OutputMetadata) UnmarshalJSON(data []byte) error

type Part

type Part struct {
	Kind          PartKind       `json:"kind"`
	Text          string         `json:"text,omitempty"`
	Media         *media.Media   `json:"media,omitempty"`
	Signature     []byte         `json:"signature,omitempty"`
	ToolCall      *ToolCall      `json:"tool_call,omitempty"`
	ToolCallDelta *ToolCallDelta `json:"tool_call_delta,omitempty"`
	ToolResult    *ToolResult    `json:"tool_result,omitempty"`
	Metadata      metadata.Map   `json:"metadata,omitzero"`
}

Part is a tagged protocol value. Kind selects exactly one payload shape: Text, Media, reasoning Text/Signature, ToolCall, or ToolResult. Metadata retains JSON-safe, part-scoped provider state without weakening the common semantic payload.

func NewMediaPart

func NewMediaPart(value *media.Media) Part

func NewReasoningPart

func NewReasoningPart(text string, signature []byte) Part

func NewTextPart

func NewTextPart(text string) Part

func NewToolCallDeltaPart

func NewToolCallDeltaPart(delta ToolCallDelta) Part

func NewToolCallPart

func NewToolCallPart(call ToolCall) Part

func NewToolResultPart

func NewToolResultPart(result ToolResult) Part

func (Part) Clone

func (p Part) Clone() Part

func (Part) MarshalJSON

func (p Part) MarshalJSON() ([]byte, error)

func (*Part) UnmarshalJSON

func (p *Part) UnmarshalJSON(data []byte) error

func (Part) Validate

func (p Part) Validate() error

type PartKind

type PartKind string

PartKind identifies which payload in Part is active.

const (
	// PartText carries plain text.
	PartText PartKind = "text"
	// PartMedia carries an image, audio, document, or other media value.
	PartMedia PartKind = "media"
	// PartReasoning carries visible reasoning and an optional opaque signature.
	PartReasoning PartKind = "reasoning"
	// PartToolCall carries one tool invocation request.
	PartToolCall PartKind = "tool_call"
	// PartToolCallDelta carries one streaming tool-call fragment. It is response-
	// only and [ResponseAccumulator] promotes it into PartToolCall.
	PartToolCallDelta PartKind = "tool_call_delta"
	// PartToolResult carries one tool execution result.
	PartToolResult PartKind = "tool_result"
)

func (PartKind) Valid

func (p PartKind) Valid() bool

type ReasoningEffort

type ReasoningEffort string

ReasoningEffort is a provider-neutral reasoning intensity selected from a model's advertised values. It is intentionally open rather than a fixed enum: the selected model owns its accepted vocabulary.

func (ReasoningEffort) Validate

func (r ReasoningEffort) Validate() error

Validate rejects values whose identity would change under trimming. Empty is valid and asks the provider adapter to use the selected model's default.

type Request

type Request struct {
	Messages []Message        `json:"messages"`
	Tools    []ToolDefinition `json:"tools,omitempty"`
	Options  Options          `json:"options,omitzero"`
}

Request is the complete provider-neutral input to a chat model. It contains only serializable protocol values; executable tools and invocation state are supplied separately by higher-level runtimes. Construction and cloning snapshot every mutable nested protocol value before middleware or providers receive it.

func NewRequest

func NewRequest(messages ...Message) (*Request, error)

func (*Request) Clone

func (r *Request) Clone() *Request

func (Request) MarshalJSON

func (r Request) MarshalJSON() ([]byte, error)

func (*Request) UnmarshalJSON

func (r *Request) UnmarshalJSON(data []byte) error

func (*Request) Validate

func (r *Request) Validate() error

type Response

type Response struct {
	Output   *Output           `json:"output,omitempty"`
	Metadata *ResponseMetadata `json:"metadata,omitempty"`
}

Response is provider output with at most one generation output. Its zero value is valid so a stream can represent an empty or metadata-only chunk.

func NewResponse

func NewResponse(output *Output, responseMetadata *ResponseMetadata) (*Response, error)

func (*Response) Clone

func (r *Response) Clone() *Response

func (Response) MarshalJSON

func (r Response) MarshalJSON() ([]byte, error)

func (*Response) Text

func (r *Response) Text() string

func (*Response) UnmarshalJSON

func (r *Response) UnmarshalJSON(data []byte) error

func (*Response) Validate

func (r *Response) Validate() error

type ResponseAccumulator

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

ResponseAccumulator merges response deltas into one provider-neutral response. Its zero value is ready to use and it never mutates supplied chunks.

Text and reasoning merge only while adjacent. Tool-call arguments merge by stable call ID even when parallel calls are interleaved. Identity and finish fields use the latest non-empty value, metadata merges last-write-wins, and Usage is a cumulative snapshot whose latest non-zero value replaces the previous snapshot.

func (*ResponseAccumulator) Add

func (r *ResponseAccumulator) Add(chunk *Response) error

Add validates and atomically merges one stream chunk. An error leaves the accumulator unchanged.

func (*ResponseAccumulator) Response

func (r *ResponseAccumulator) Response() *Response

Response returns an independent snapshot, or nil before the first successful Add. Mutating the returned value cannot affect the accumulator.

type ResponseMetadata

type ResponseMetadata struct {
	ID    string       `json:"id,omitempty"`
	Model string       `json:"model,omitempty"`
	Usage Usage        `json:"usage,omitzero"`
	Extra metadata.Map `json:"extra,omitzero"`
}

ResponseMetadata holds provider identity, usage, and response-scoped extras.

func (ResponseMetadata) MarshalJSON

func (r ResponseMetadata) MarshalJSON() ([]byte, error)

func (*ResponseMetadata) UnmarshalJSON

func (r *ResponseMetadata) UnmarshalJSON(data []byte) error

type Role

type Role string

Role identifies a message's participant in a conversation.

const (
	// RoleSystem carries model instructions.
	RoleSystem Role = "system"
	// RoleUser carries user input.
	RoleUser Role = "user"
	// RoleAssistant carries model output.
	RoleAssistant Role = "assistant"
	// RoleTool carries results for tool calls requested by the assistant.
	RoleTool Role = "tool"
)

func (Role) Valid

func (r Role) Valid() bool

type StreamMiddleware

type StreamMiddleware func(next Streamer) Streamer

StreamMiddleware wraps the optional Streamer capability.

type Streamer

type Streamer interface {
	// Stream starts provider work lazily when the sequence is iterated. Each
	// yielded response is an independently owned delta accepted by
	// ResponseAccumulator. Stopping iteration releases provider resources before
	// the iterator returns; a terminal error is yielded at most once.
	Stream(ctx context.Context, request *Request) iter.Seq2[*Response, error]
}

Streamer is the optional streaming chat capability. It is independent of Model so an implementation is not forced to provide a synthetic synchronous Call path, and a call-only implementation is not forced to fake streaming.

Every successful yield is a valid response delta. Usage, when present, is a cumulative snapshot rather than a per-chunk increment. On failure the sequence yields (nil, err) once and terminates. Context errors retain their errors.Is identity. When the caller stops iteration, implementations must synchronously release provider resources without yielding a cancellation error or leaving a detached goroutine behind. ResponseAccumulator defines the provider-neutral aggregation semantics.

func WrapStream

func WrapStream(streamer Streamer, middlewares ...StreamMiddleware) Streamer

WrapStream composes stream middlewares around streamer using the same outermost-first order as Wrap.

type StreamerFunc

type StreamerFunc func(ctx context.Context, request *Request) iter.Seq2[*Response, error]

func (StreamerFunc) Stream

func (s StreamerFunc) Stream(ctx context.Context, request *Request) iter.Seq2[*Response, error]

type ToolCall

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

ToolCall is one complete, untrusted model proposal to invoke a named tool. Arguments retains the provider's JSON text so malformed final model output remains serializable. A runtime must promote the proposal through the bound Tool schema before exposing it to capabilities or execution.

func (ToolCall) Validate

func (t ToolCall) Validate() error

type ToolCallDelta

type ToolCallDelta struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Arguments string `json:"arguments,omitempty"`
}

ToolCallDelta is one streaming fragment of a ToolCall. It cannot be placed in a model Request; ResponseAccumulator is the boundary that assembles deltas into a complete, still-untrusted ToolCall.

func (ToolCallDelta) Validate

func (t ToolCallDelta) Validate() error

type ToolDefinition

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

ToolDefinition is the serializable description exposed to a model. Tool execution belongs to package tool and is deliberately absent here.

func (ToolDefinition) Clone

func (t ToolDefinition) Clone() ToolDefinition

func (ToolDefinition) MarshalJSON

func (t ToolDefinition) MarshalJSON() ([]byte, error)

func (*ToolDefinition) UnmarshalJSON

func (t *ToolDefinition) UnmarshalJSON(data []byte) error

func (ToolDefinition) Validate

func (t ToolDefinition) Validate() error

type ToolOutput

type ToolOutput struct {
	Content []Part          `json:"content,omitempty"`
	Details json.RawMessage `json:"details,omitempty"`
}

ToolOutput is the provider-neutral value produced by a Tool. Content is the model-visible ordered text/media representation. Details is optional JSON for structured consumers; providers use its encoded JSON as the model-visible fallback only when Content is empty.

Content deliberately reuses Part so media has one representation across the chat protocol. Only text and media parts are valid here; nested reasoning, calls, deltas, and results are rejected.

func NewJSONToolOutput

func NewJSONToolOutput(value json.RawMessage) (ToolOutput, error)

NewJSONToolOutput returns a structured output whose exact JSON encoding is preserved. The value must be one complete RFC 7493 JSON document.

func NewTextToolOutput

func NewTextToolOutput(text string) ToolOutput

NewTextToolOutput returns a text output. Empty text is represented by the valid zero ToolOutput rather than an invalid empty text Part.

func (ToolOutput) Clone

func (t ToolOutput) Clone() ToolOutput

func (ToolOutput) Text

func (t ToolOutput) Text() (string, bool)

Text returns the lossless text projection used by providers whose tool result protocol accepts only strings. It reports false when Content contains media so adapters cannot silently discard it. Details is encoded only when Content is empty.

func (ToolOutput) Validate

func (t ToolOutput) Validate() error

type ToolResult

type ToolResult struct {
	ID      string     `json:"id"`
	Name    string     `json:"name"`
	Output  ToolOutput `json:"output"`
	IsError bool       `json:"is_error,omitempty"`
}

ToolResult is one tool execution result correlated to a ToolCall by ID.

func (ToolResult) Clone

func (t ToolResult) Clone() ToolResult

func (ToolResult) Validate

func (t ToolResult) Validate() error

type Usage

type Usage struct {
	// InputTokens is the total processed input count. Provider cache-read and
	// cache-write counts, when reported, are breakdowns included in this total.
	InputTokens           int64  `json:"input_tokens,omitempty"`
	OutputTokens          int64  `json:"output_tokens,omitempty"`
	ReasoningTokens       *int64 `json:"reasoning_tokens,omitempty"`
	CacheReadInputTokens  *int64 `json:"cache_read_input_tokens,omitempty"`
	CacheWriteInputTokens *int64 `json:"cache_write_input_tokens,omitempty"`
}

Usage records provider-neutral token counts. Breakdown pointers distinguish an explicitly reported zero from an unsupported dimension.

func (Usage) MarshalJSON

func (u Usage) MarshalJSON() ([]byte, error)

func (Usage) TotalTokens

func (u Usage) TotalTokens() int64

func (*Usage) UnmarshalJSON

func (u *Usage) UnmarshalJSON(data []byte) error

func (Usage) Validate

func (u Usage) Validate() error

Jump to

Keyboard shortcuts

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