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" )
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"`
// 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 ¶
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.