conversation

package
v2.5.0 Latest Latest
Warning

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

Go to latest
Published: Jul 28, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// ApprovalStageCall means one or more tool_use blocks on this post are
	// still pending an Accept/Reject decision.
	ApprovalStageCall = "call"
	// ApprovalStageResult means every tool was executed and at least one
	// tool_result still needs a Share/Keep-private decision.
	ApprovalStageResult = "result"
	// ApprovalStageDone means no user decision remains: every executed tool's
	// result has decided_at set (or the post had no tool_use blocks at all).
	ApprovalStageDone = "done"
)

Approval-stage string values mirror the webapp ToolApprovalStage. A post-anchor assistant turn carries one of these so the UI can render the correct controls without re-deriving state on the client.

View Source
const (
	BlockTypeText        = "text"
	BlockTypeThinking    = "thinking"
	BlockTypeToolUse     = "tool_use"
	BlockTypeToolResult  = "tool_result"
	BlockTypeFile        = "file"
	BlockTypeImage       = "image"
	BlockTypeAnnotations = "annotations"
)

Block type constants identify the type of content in a ContentBlock.

View Source
const (
	StatusPending      = "pending"
	StatusAccepted     = "accepted"
	StatusRejected     = "rejected"
	StatusError        = "error"
	StatusSuccess      = "success"
	StatusAutoApproved = "auto_approved"
)

Tool call status string constants for JSON/JSONB representation.

View Source
const DefaultMaxFileSize = int64(5 * 1024 * 1024)
View Source
const InlineFileMaxBytes = int64(8 * 1024)

InlineFileMaxBytes is the upper bound on readable text that gets inlined directly into a turn. Files at or below this size are cheap enough to inline (and save the LLM a tool round trip); larger files are surfaced as metadata only and fetched on demand via the read_file tool. ~8 KiB ≈ 2000 tokens.

View Source
const UnsharedToolResultRedaction = "[result not shared by user]"

UnsharedToolResultRedaction replaces tool_result content the requester has not shared, preserving the tool_use/tool_result pairing required by LLM providers.

Variables

This section is empty.

Functions

func AssembleRequest

func AssembleRequest(
	conv *store.Conversation,
	turns []store.Turn,
	context *llm.Context,
	mmClient mmapi.Client,
	enableVision bool,
	maxFileSize int64,
	opts ...BuildOptions,
) (*llm.CompletionRequest, error)

AssembleRequest builds the CompletionRequest from already-loaded turns and externally-supplied rendering config. Exported so callers without a full Service (e.g. the /context endpoint) can reuse the runtime assembly path.

func BlocksToPost

func BlocksToPost(
	blocks []ContentBlock,
	role string,
	opts PostConversionOptions,
) llm.Post

BlocksToPost converts a slice of content blocks and a role string into an llm.Post. When opts.RedactUnshared is true, tool_result content whose Shared flag is not true is replaced with UnsharedToolResultRedaction, and tool_use arguments whose Shared flag is not true are replaced with an empty JSON object so the LLM cannot paraphrase private tool parameters into a channel-visible reply.

func BoolPtr

func BoolPtr(b bool) *bool

BoolPtr returns a pointer to the given bool value.

func ComputePostApprovalState

func ComputePostApprovalState(turns []store.Turn, postID string) string

ComputePostApprovalState inspects the conversation turns and returns the approval stage for the assistant post identified by postID.

A turn belongs to a post's "response" when it sits between the post's anchor turn (the assistant turn with matching post_id) and either the preceding user turn or another post's anchor. We walk backwards from the anchor to collect tool-round turns (no post_id) that belong to this response, then match their tool_use blocks against tool_result blocks anywhere in the conversation.

func DeriveLoadedMCPTools

func DeriveLoadedMCPTools(turns []store.Turn) []string

DeriveLoadedMCPTools returns the deduped, first-load-order list of MCP tool names that retained conversation history materialized via load_tool.

A name is included iff history contains an assistant tool_use block with Name == mcp.LoadToolName and a tool_result block whose ToolUseID matches that tool_use, whose Status is success, and whose Content decodes to an mcp.LoadToolResult with Loaded == true and a non-empty Name.

func Int64Ptr

func Int64Ptr(v int64) *int64

Int64Ptr returns a pointer to the given int64 value.

func RestoreLoadedMCPToolsFromTurns

func RestoreLoadedMCPToolsFromTurns(toolStore *llm.ToolStore, turns []store.Turn) []llm.Tool

RestoreLoadedMCPToolsFromTurns loads MCP tools retained in conversation history.

func RoleFromString

func RoleFromString(role string) llm.PostRole

RoleFromString converts a turn role string to an llm.PostRole.

func RoleToString

func RoleToString(role llm.PostRole) string

RoleToString converts an llm.PostRole to a turn role string.

func StatusFromString

func StatusFromString(s string) llm.ToolCallStatus

StatusFromString converts a status string to an llm.ToolCallStatus.

func StatusToString

func StatusToString(s llm.ToolCallStatus) string

StatusToString converts an llm.ToolCallStatus to a status string.

Types

type BotLookup

type BotLookup interface {
	IsAnyBot(userID string) bool

	// GetBotConfigByID returns the bot's EnableVision and MaxFileSize.
	// ok is false when botID is unknown.
	GetBotConfigByID(botID string) (enableVision bool, maxFileSize int64, ok bool)
}

BotLookup answers bot-membership and per-bot config queries.

type BuildOptions

type BuildOptions struct {
	ExcludeAfterPostID string

	// AllowUnsharedToolContent opts IN to sending tool_result content whose
	// Shared flag is not true to the LLM. The default is to redact — any
	// code path whose LLM response may reach other users (channel mentions,
	// channel follow-ups, regenerations in channels) MUST leave this false
	// so kept-private tool output cannot be paraphrased into a channel post.
	//
	// Set to true only in contexts where the LLM response is scoped to the
	// requester (e.g. the DM follow-up stream), since DM tool_results are
	// always shared=true anyway and nothing would be redacted in that case.
	AllowUnsharedToolContent bool
}

BuildOptions controls optional behavior of BuildCompletionRequest.

type Citation

type Citation struct {
	Type       string `json:"type"`
	URL        string `json:"url,omitempty"`
	Title      string `json:"title,omitempty"`
	StartIndex int    `json:"start_index"`
	EndIndex   int    `json:"end_index"`
}

Citation represents an inline citation in a text block.

type ContentBlock

type ContentBlock struct {
	Type string `json:"type"`

	// Text / Thinking fields
	Text      string     `json:"text,omitempty"`
	Signature string     `json:"signature,omitempty"` // thinking blocks only
	Citations []Citation `json:"citations,omitempty"` // text blocks only

	// ToolUse fields
	ID           string          `json:"id,omitempty"`
	Name         string          `json:"name,omitempty"`
	ServerOrigin string          `json:"server_origin,omitempty"`
	Input        json.RawMessage `json:"input,omitempty"`
	MCPBareName  string          `json:"mcp_bare_name,omitempty"`
	Status       string          `json:"status,omitempty"`
	Shared       *bool           `json:"shared,omitempty"` // pointer to distinguish unset from false

	// UserInteraction is the persisted form of llm.Tool.UserInteraction.
	UserInteraction string `json:"user_interaction,omitempty"`

	// WouldAutoExecute marks any pending tool_use block that passed the
	// auto-execution policy (see llm.ToolCall.WouldAutoExecute).
	WouldAutoExecute bool `json:"would_auto_execute,omitempty"`

	// DecidedAt (tool_result blocks) records when the share/keep-private
	// decision was made — either by the user clicking Share or Keep Private
	// in a channel, or implicitly at creation time (DMs, rejected tools,
	// auto_run_everywhere results). A nil value means the result still
	// needs a user decision; any non-nil value means the decision is final
	// and no further approval UI should appear. This distinguishes the
	// "undecided" and "decided to keep private" states, which both present
	// Shared=false but require opposite UI behavior.
	DecidedAt *int64 `json:"decided_at,omitempty"`

	// ToolResult fields
	ToolUseID string `json:"tool_use_id,omitempty"`
	Content   string `json:"content,omitempty"` // tool_result or file content

	// File / Image fields
	Filename string `json:"filename,omitempty"`
	MimeType string `json:"mime_type,omitempty"`
	FileID   string `json:"file_id,omitempty"` // image blocks: references Mattermost file attachment

	// Annotations fields
	WebSearchContext *WebSearchContext `json:"web_search_context,omitempty"`
}

ContentBlock is a flat struct representing any content block type. The Type field discriminates which fields are meaningful. Uses omitempty on all optional fields so JSON output only includes relevant fields.

func FilterForNonRequester

func FilterForNonRequester(blocks []ContentBlock) []ContentBlock

FilterForNonRequester returns a new slice of content blocks with private tool data redacted. Tool use blocks with shared != true have their Input field set to nil. Tool result blocks with shared != true have their Content field set to empty string. All other block types pass through unchanged. The original slice and its elements are never mutated. Returns nil if the input is nil.

func PostToBlocks

func PostToBlocks(post llm.Post, shared bool) []ContentBlock

PostToBlocks converts an llm.Post into a slice of content blocks. This is used when writing turns to the database from stream events or the current llm.Post model. The shared parameter controls whether tool blocks get shared=true or shared=false.

func SanitizeForDisplay

func SanitizeForDisplay(blocks []ContentBlock) []ContentBlock

SanitizeForDisplay returns a new slice of content blocks with LLM-generated string fields sanitized against Unicode bidi/spoofing attacks. Tool use blocks have their Input field sanitized, and tool result blocks have their Content field sanitized. The original slice is never mutated. Returns nil if the input is nil.

type CreateConversationParams

type CreateConversationParams struct {
	UserID       string
	BotID        string
	ChannelID    *string // nullable for non-channel conversations
	RootPostID   *string // nullable for non-thread conversations
	Operation    string  // e.g., "conversation", "thread_analysis", "search"
	SystemPrompt string  // already-formatted system prompt text
	UserMessage  string  // the first user message content
	UserPostID   *string // nullable: post ID for the user turn, if a post exists
	FileIDs      []string
}

CreateConversationParams contains parameters for creating a new conversation.

type CreateConversationResult

type CreateConversationResult struct {
	ConversationID string
	UserTurnID     string
}

CreateConversationResult is the return value of CreateConversation.

type GetOrCreateParams

type GetOrCreateParams struct {
	UserID       string
	BotID        string
	ChannelID    string
	RootPostID   string // the thread root post ID
	Operation    string
	SystemPrompt string  // formatted system prompt (used only if creating)
	UserMessage  string  // new user message
	UserPostID   *string // post ID for the new user turn
	FileIDs      []string
}

GetOrCreateParams contains parameters for GetOrCreateConversation.

type GetOrCreateResult

type GetOrCreateResult struct {
	Conversation *store.Conversation
	IsNew        bool
	UserTurnID   string // the newly created user turn
}

GetOrCreateResult is the return value of GetOrCreateConversation.

type PostConversionOptions

type PostConversionOptions struct {
	RedactUnshared bool
	MMClient       mmapi.Client
	EnableVision   bool
	MaxFileSize    int64
	ToolStore      *llm.ToolStore
}

PostConversionOptions configures how BlocksToPost converts content blocks.

type Service

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

Service manages conversation entities: creation, continuation, CompletionRequest building, turn writing, and title generation.

func NewService

func NewService(
	s Store,
	prompts *llm.Prompts,
	mmClient mmapi.Client,
	bots BotLookup,
) *Service

NewService creates a new conversation Service.

func (*Service) BuildChannelMentionRequest

func (s *Service) BuildChannelMentionRequest(
	conv *store.Conversation,
	context *llm.Context,
	threadData *mmapi.ThreadData,
	opts ...BuildOptions,
) (*llm.CompletionRequest, error)

BuildChannelMentionRequest builds a CompletionRequest for a channel mention. It reads the bot's own turns from the conversation and interleaves thread posts from other users/bots at the correct sequence points.

func (*Service) BuildCompletionRequest

func (s *Service) BuildCompletionRequest(
	conv *store.Conversation,
	context *llm.Context,
	opts ...BuildOptions,
) (*llm.CompletionRequest, error)

BuildCompletionRequest builds an llm.CompletionRequest from the conversation's system prompt and all its turns. Thin wrapper around AssembleRequest that fetches turns from the store and resolves the bot's attachment config.

func (*Service) CreateConversation

func (s *Service) CreateConversation(params CreateConversationParams) (*CreateConversationResult, error)

CreateConversation creates a new conversation and its initial user turn.

func (*Service) CreatePlaceholderAssistantTurn

func (s *Service) CreatePlaceholderAssistantTurn(
	conversationID string,
	postID *string,
) (string, error)

CreatePlaceholderAssistantTurn creates an empty assistant turn linked to the response post. Returns the turn ID. Called at stream start.

func (*Service) CreateTurn

func (s *Service) CreateTurn(turn *store.Turn) error

CreateTurn persists a new turn in the store with an explicit sequence.

func (*Service) CreateTurnAutoSequence

func (s *Service) CreateTurnAutoSequence(turn *store.Turn) error

CreateTurnAutoSequence persists a new turn, atomically assigning the next sequence number.

func (*Service) DeleteResponseTurns

func (s *Service) DeleteResponseTurns(conversationID, postID string) error

DeleteResponseTurns removes the post's anchor and any assistant/tool_result turns between it and the originating user turn. Callers must build any completion request before calling this — ExcludeAfterPostID needs the anchor.

func (*Service) FinalizeAssistantTurn

func (s *Service) FinalizeAssistantTurn(
	turnID string,
	content []ContentBlock,
	tokensIn, tokensOut int64,
) error

FinalizeAssistantTurn updates the placeholder turn with final content blocks and token counts. Called at stream end.

func (*Service) GenerateTitle

func (s *Service) GenerateTitle(
	conversationID string,
	lm llm.LanguageModel,
	userMessage string,
	context *llm.Context,
) error

GenerateTitle generates a short title for the conversation and saves it. This should be called asynchronously (in a goroutine) after conversation creation. The lm parameter provides the language model for title generation. The context parameter provides bot/user context for the LLM call.

func (*Service) GetConversation

func (s *Service) GetConversation(id string) (*store.Conversation, error)

GetConversation retrieves a conversation by ID. Returns an error if not found.

func (*Service) GetInitiatingUserTurn

func (s *Service) GetInitiatingUserTurn(conversationID, postID string) (*store.Turn, error)

GetInitiatingUserTurn returns the user turn that started the agent run whose assistant turn produced postID. Used to derive the run's deterministic TraceID at resume time so spans started on a different node still land in the original trace. Returns nil if no matching assistant turn or no preceding user turn is found.

func (*Service) GetOrCreateConversation

func (s *Service) GetOrCreateConversation(params GetOrCreateParams) (*GetOrCreateResult, error)

GetOrCreateConversation looks up an existing conversation by (RootPostID, BotID), or creates a new one if none exists.

func (*Service) GetPreviousUserTurn

func (s *Service) GetPreviousUserTurn(conversationID, currentUserTurnID string) (*store.Turn, error)

GetPreviousUserTurn returns the user turn that came immediately before currentUserTurnID in the conversation, or nil if currentUserTurnID is the first user turn. Used to attach a span link from a new run to the previous run's trace, so consecutive invocations in the same conversation are navigable in Tempo.

func (*Service) GetTurnByPostID

func (s *Service) GetTurnByPostID(postID string) (*store.Turn, error)

GetTurnByPostID returns the assistant turn anchored to postID, or nil.

func (*Service) GetTurns

func (s *Service) GetTurns(conversationID string) ([]store.Turn, error)

GetTurns returns all turns for a conversation, ordered by sequence.

func (*Service) UpdateConversationRootPostID

func (s *Service) UpdateConversationRootPostID(id string, rootPostID string) error

UpdateConversationRootPostID sets the RootPostID on a conversation. Used when the post ID is only known after post creation (e.g., thread analysis DM posts).

func (*Service) UpdateConversationTitle

func (s *Service) UpdateConversationTitle(id, title string) error

UpdateConversationTitle updates the title of a conversation.

func (*Service) UpdateTurnContent

func (s *Service) UpdateTurnContent(turnID string, content json.RawMessage) error

UpdateTurnContent updates the content JSON of a turn.

func (*Service) UpdateTurnPostID

func (s *Service) UpdateTurnPostID(id string, postID *string) error

UpdateTurnPostID sets or clears the PostID on a turn.

func (*Service) WriteToolTurns

func (s *Service) WriteToolTurns(
	conversationID string,
	toolTurns []toolrunner.ToolTurn,
	shared bool,
) error

WriteToolTurns persists tool execution rounds from the ToolRunner. The shared flag controls the `shared` field on tool content blocks:

  • true in DMs (everything visible to requester)
  • false in channels (non-requester sees redacted content until shared)

type Store

type Store interface {
	CreateConversation(conv *store.Conversation) error
	GetConversation(id string) (*store.Conversation, error)
	GetConversationByThreadBotUser(rootPostID, botID, userID string) (*store.Conversation, error)
	UpdateConversationTitle(id, title string) error
	UpdateConversationRootPostID(id string, rootPostID string) error
	CreateTurn(turn *store.Turn) error
	CreateTurnAutoSequence(turn *store.Turn) error
	GetTurnsForConversation(conversationID string) ([]store.Turn, error)
	GetTurnByPostID(postID string) (*store.Turn, error)
	UpdateTurnContent(id string, content json.RawMessage) error
	UpdateTurnTokens(id string, tokensIn, tokensOut int64) error
	UpdateTurnPostID(id string, postID *string) error
	DeleteResponseTurns(conversationID, postID string) error
	GetMaxSequenceForConversation(conversationID string) (int, error)
}

Store is the subset of store.Store that the conversation service needs.

type WebSearchContext

type WebSearchContext struct {
	Results         json.RawMessage `json:"results"`
	ExecutedQueries json.RawMessage `json:"executed_queries"`
	Count           int             `json:"count"`
}

WebSearchContext holds web search metadata for annotations blocks.

Jump to

Keyboard shortcuts

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