Documentation
¶
Index ¶
- Constants
- func AssembleRequest(conv *store.Conversation, turns []store.Turn, context *llm.Context, ...) (*llm.CompletionRequest, error)
- func BlocksToPost(blocks []ContentBlock, role string, opts PostConversionOptions) llm.Post
- func BoolPtr(b bool) *bool
- func ComputePostApprovalState(turns []store.Turn, postID string) string
- func DeriveLoadedMCPTools(turns []store.Turn) []string
- func Int64Ptr(v int64) *int64
- func RestoreLoadedMCPToolsFromTurns(toolStore *llm.ToolStore, turns []store.Turn) []llm.Tool
- func RoleFromString(role string) llm.PostRole
- func RoleToString(role llm.PostRole) string
- func StatusFromString(s string) llm.ToolCallStatus
- func StatusToString(s llm.ToolCallStatus) string
- type BotLookup
- type BuildOptions
- type Citation
- type ContentBlock
- type CreateConversationParams
- type CreateConversationResult
- type GetOrCreateParams
- type GetOrCreateResult
- type PostConversionOptions
- type Service
- func (s *Service) BuildChannelMentionRequest(conv *store.Conversation, context *llm.Context, threadData *mmapi.ThreadData, ...) (*llm.CompletionRequest, error)
- func (s *Service) BuildCompletionRequest(conv *store.Conversation, context *llm.Context, opts ...BuildOptions) (*llm.CompletionRequest, error)
- func (s *Service) CreateConversation(params CreateConversationParams) (*CreateConversationResult, error)
- func (s *Service) CreatePlaceholderAssistantTurn(conversationID string, postID *string) (string, error)
- func (s *Service) CreateTurn(turn *store.Turn) error
- func (s *Service) CreateTurnAutoSequence(turn *store.Turn) error
- func (s *Service) DeleteResponseTurns(conversationID, postID string) error
- func (s *Service) FinalizeAssistantTurn(turnID string, content []ContentBlock, tokensIn, tokensOut int64) error
- func (s *Service) GenerateTitle(conversationID string, lm llm.LanguageModel, userMessage string, ...) error
- func (s *Service) GetConversation(id string) (*store.Conversation, error)
- func (s *Service) GetInitiatingUserTurn(conversationID, postID string) (*store.Turn, error)
- func (s *Service) GetOrCreateConversation(params GetOrCreateParams) (*GetOrCreateResult, error)
- func (s *Service) GetPreviousUserTurn(conversationID, currentUserTurnID string) (*store.Turn, error)
- func (s *Service) GetTurnByPostID(postID string) (*store.Turn, error)
- func (s *Service) GetTurns(conversationID string) ([]store.Turn, error)
- func (s *Service) UpdateConversationRootPostID(id string, rootPostID string) error
- func (s *Service) UpdateConversationTitle(id, title string) error
- func (s *Service) UpdateTurnContent(turnID string, content json.RawMessage) error
- func (s *Service) UpdateTurnPostID(id string, postID *string) error
- func (s *Service) WriteToolTurns(conversationID string, toolTurns []toolrunner.ToolTurn, shared bool) error
- type Store
- type WebSearchContext
Constants ¶
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.
const ( BlockTypeText = "text" BlockTypeThinking = "thinking" BlockTypeToolUse = "tool_use" BlockTypeToolResult = "tool_result" BlockTypeFile = "file" BlockTypeImage = "image" BlockTypeAnnotations = "annotations" // BlockTypeServerToolUse is provider-executed activity. Payload matches // the live websocket event so persisted rounds render the same way. BlockTypeServerToolUse = "server_tool_use" )
Block type constants identify the type of content in a ContentBlock.
const ( StatusPending = "pending" StatusAccepted = "accepted" StatusRejected = "rejected" StatusError = "error" StatusSuccess = "success" StatusAutoApproved = "auto_approved" )
Tool call status string constants for JSON/JSONB representation.
const DefaultMaxFileSize = int64(5 * 1024 * 1024)
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.
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 ComputePostApprovalState ¶
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 ¶
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 RestoreLoadedMCPToolsFromTurns ¶
RestoreLoadedMCPToolsFromTurns loads MCP tools retained in conversation history.
func RoleFromString ¶
RoleFromString converts a turn role string to an llm.PostRole.
func RoleToString ¶
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
// 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"`
// Title and Description mirror llm.ToolCall so a reloaded conversation
// renders the same tool identity the live websocket event showed. Both
// are visible to non-requesters like Name. Description is not rendered
// anywhere yet.
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
// 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"`
// ServerTool is streamed activity. No approval flow; shares post-text
// visibility, so FilterForNonRequester does not redact it.
ServerTool *llm.ServerToolUse `json:"server_tool,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 Input and MCPBareName cleared; tool result blocks with shared != true have Content cleared. Tool identity (Name, Title, Description, ServerOrigin) stays visible, mirroring redactToolCalls on the live path so both paths render identically. The original slice is never mutated; nil in, nil out.
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 and MCP-server-supplied string fields sanitized against Unicode bidi/spoofing attacks: Input, Title, and Description on tool_use blocks, Content on tool_result blocks. Title/Description are already sanitized at capture; this is defense in depth that also covers older persisted turns. The original slice is never mutated; nil in, nil out.
func SequenceBlocks ¶ added in v2.7.0
func SequenceBlocks(segments []llm.TurnSegment, serverTools []llm.ServerToolUse) []ContentBlock
SequenceBlocks renders segments in arrival order, resolving server_tool ids against the snapshot. Missing activity is dropped, not rendered empty.
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 ¶
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 {
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 ¶
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 ¶
CreateTurn persists a new turn in the store with an explicit sequence.
func (*Service) CreateTurnAutoSequence ¶
CreateTurnAutoSequence persists a new turn, atomically assigning the next sequence number.
func (*Service) DeleteResponseTurns ¶
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 ¶
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 ¶
GetTurnByPostID returns the assistant turn anchored to postID, or nil.
func (*Service) UpdateConversationRootPostID ¶
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 ¶
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 ¶
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.