chat

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package chat owns the AI assistant feature: the chat pane state (input, viewport, runs, tool rounds, slash completions, prompt history), rendering, context building, and read-only tool execution against the session service. The root shell supplies an immutable context snapshot (connection scope, database info, schema, query, results) before every update and applies the component's request events; write confirmations and interactive query execution stay root-owned, and root replies with the exported chat messages the component consumes.

Index

Constants

View Source
const (
	ToolPhaseTimeout        = 2 * time.Minute
	FinalizationTimeout     = 20 * time.Second
	MaxToolCalls            = 64
	RepeatedToolResultLimit = 3
)

Phase timeouts and budgets for assistant tool rounds.

View Source
const SpinnerInterval = 80 * time.Millisecond

SpinnerInterval is the progress spinner frame period.

Variables

View Source
var SpinnerFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}

SpinnerFrames are the braille frames for the assistant progress spinner.

Functions

func FormatResult

func FormatResult(res sharedsql.Result) string

FormatResult renders a query result as the tabular text tools and write results carry.

func HistoryOptionLabel

func HistoryOptionLabel(run *Run, title string) string

HistoryOptionLabel renders a conversation title for the /history picker, prefixing a spinner glyph while that conversation's agent run is active. The glyph is static while the picker is open: the picker is not rebuilt on every tick.

func OwnsMessage

func OwnsMessage(msg tea.Msg) bool

OwnsMessage reports whether msg belongs to the chat feature. The root routes every owned message into the component's Update.

func ReadStreamEvent

func ReadStreamEvent(ch <-chan ai.StreamEvent, conversationID string) tea.Msg

ReadStreamEvent reads one event from the stream channel and returns it as a StreamMsg.

func ResultsContext

func ResultsContext(ctx Context) string

ResultsContext returns the visible results block for providers without tool support; tool-capable providers get get_visible_results instead.

func SQL

func SQL(messages []ai.Message) string

SQL extracts the assistant's latest fenced SQL statement, newest first.

func SpinnerTick

func SpinnerTick() tea.Cmd

SpinnerTick re-arms the assistant progress spinner tick.

func TruncateContext

func TruncateContext(value string) string

TruncateContext caps the assistant context at the shared limit.

func TruncateTitle

func TruncateTitle(prompt string) string

TruncateTitle caps a conversation title at 60 runes.

Types

type Block

type Block struct {
	Source blockSource
	Block  string
}

Block is one cached rendered viewport block for a chat message.

type Client

type Client interface {
	AgentForPrompt(string) string
	Chat(context.Context, ai.Request) (ai.Response, error)
	Complete(context.Context, ai.Request) (ai.Response, error)
	ChatStream(context.Context, ai.Request) (<-chan ai.StreamEvent, error)
	SupportsTools(string) bool
	GenerateTitle(context.Context, string) (string, error)
}

Client is the AI provider the assistant talks to. The root injects it through SetAI; the component never touches the provider package's internals beyond this interface.

type ClipboardRequested

type ClipboardRequested struct{ Text string }

ClipboardRequested asks the root to write text to both clipboards.

type Completion

type Completion struct {
	Items    []CompletionItem
	Matches  []CompletionItem
	Prefix   string
	Selected int
}

Completion is the slash-command suggestion dropdown while typing.

func NewCompletion

func NewCompletion(items []CompletionItem) Completion

NewCompletion builds a completion list from command items.

func (Completion) Accept

func (c Completion) Accept() CompletionItem

Accept returns the selected match.

func (*Completion) Dismiss

func (c *Completion) Dismiss()

Dismiss closes the dropdown but remembers the prefix.

func (*Completion) Filter

func (c *Completion) Filter(prefix string)

Filter narrows matches to those with a case-insensitive prefix match.

func (*Completion) Move

func (c *Completion) Move(delta int)

Move shifts the selection by delta, wrapping.

func (Completion) Visible

func (c Completion) Visible() bool

Visible reports whether the dropdown has matches.

type CompletionItem

type CompletionItem struct {
	Label      string
	InsertText string
	Detail     string
	Kind       string // display category, e.g. "command"
}

CompletionItem is one slash-command suggestion.

type ConfirmationRequested

type ConfirmationRequested struct {
	Statement  string
	Generation int64
}

ConfirmationRequested asks the root to show the write-confirmation dialog for one assistant sql_write call. Generation is the turn id the root reports back through WriteResultMsg.

type Context

type Context struct {
	ConnectionID string
	Database     sharedsql.DatabaseInfo
	Schema       []sharedsql.SchemaObject
	Query        string
	Results      sharedsql.Result
}

Context is the immutable snapshot the root hands to the component for one update: the connection scope, the database product/version, the schema objects, the current SQL editor value, and the visible results. The component never reaches into root state beyond this snapshot.

type Event

type Event interface {
	// contains filtered or unexported methods
}

Event is a typed request from the chat component to the root shell. The root applies the side effect; the component never touches root state.

type Executor

type Executor interface {
	ExecuteReadOnly(ctx context.Context, statement string) (sharedsql.Result, error)
}

Executor is the root-implemented read-only database boundary: the component requests tool queries through it, and the root owns the service call. The write path stays event-based (ConfirmationRequested plus root execution).

type History

type History interface {
	NewConversation(context.Context, string, string) (ai.Conversation, error)
	AppendMessage(context.Context, string, string, ai.Message) error
	Conversations(context.Context, string) ([]ai.Conversation, error)
	Messages(context.Context, string, string) ([]ai.Message, error)
	DeleteConversation(context.Context, string, string) error
	Clear(context.Context, string) error
	RenameConversation(context.Context, string, string, string) error
}

History persists conversations per connection scope. The component reads and writes it through this interface only.

type HistoryDeletedMsg

type HistoryDeletedMsg struct {
	Err            error
	ConnectionID   string
	ConversationID string // conversation the delete targeted ("" for clear-all)
	Clear          bool
}

HistoryDeletedMsg reports a conversation delete or clear.

type HistoryLoadedMsg

type HistoryLoadedMsg struct {
	ConnectionID  string
	Conversations []ai.Conversation
	Err           error
}

HistoryLoadedMsg carries the /history conversation list.

type HistoryPickerOutcome

type HistoryPickerOutcome struct {
	Picked string // selected conversation id; empty when the picker closed
	Closed bool
}

HistoryPickerOutcome is one root-applied outcome of a history-picker update: the picker closed without a selection, or a conversation was picked and the root loads it through the component.

type MessagesLoadedMsg

type MessagesLoadedMsg struct {
	ConnectionID   string
	ConversationID string
	Messages       []ai.Message
	Seq            int64
	Err            error
}

MessagesLoadedMsg carries one conversation's messages.

type Mode

type Mode uint8

Mode is the chat pane's modal input mode (insert/normal), mirroring the root's form-mode semantics for the chat input only.

const (
	ModeNormal Mode = iota
	ModeInsert
)

type Model

type Model struct {
	Input    textarea.Model
	Viewport viewport.Model
	Client   Client
	History  History
	ActiveID string // conversation shown; "" is the fresh unsent view
	Runs     map[string]*Run
	NextGen  int64 // globally unique turn ids across concurrent runs
	LoadSeq  int64 // bumps on each /history selection; drops stale loads
	Enabled  bool
	Visible  bool

	ShareResults  bool
	HistoryChoice string
	ChatMode      Mode

	Completion Completion // slash-command suggestions while typing
	// HistoryPicker is the /history conversation picker overlay.
	HistoryPicker *huh.Form
	// KeepInsert keeps the chat input in insert mode after a release
	// click inside the chat pane.
	KeepInsert bool

	// PromptHistory is the newest-first list of accepted user prompts for
	// this process; HistoryIndex == -1 means not browsing recall.
	PromptHistory []string
	HistoryIndex  int

	// YoloWrites: when true, sql_write executes without per-statement modal.
	YoloWrites bool

	// AppContext is the root application context run commands derive
	// from, so quitting cancels in-flight streams and history writes.
	AppContext context.Context

	// Session state the root keeps current (the chat-scoped mirror of the
	// connection): the executor read-only tools query through, the
	// read-only flag (gates the sql_write tool), the opened target
	// (connection info), and the last failed statement the assistant
	// context summarizes.
	Executor        Executor
	ReadOnly        bool
	Target          string
	LastFailedQuery string
	LastFailedError string
	// contains filtered or unexported fields
}

Model is the chat feature component: the input, the message viewport, the conversation runs, tool-round state, and the pane interactions.

func New

func New() Model

New builds the chat component with an empty conversation map.

func (*Model) AcceptChatCompletion

func (cm *Model) AcceptChatCompletion()

AcceptChatCompletion replaces the chat input with the selected suggestion.

func (*Model) ActiveRun

func (cm *Model) ActiveRun() *Run

ActiveRun returns the run backing the visible conversation, creating an empty run for the fresh (unsent) view when needed.

func (Model) Commands

func (cm Model) Commands() []CompletionItem

Commands returns the slash-command completion candidates. The YOLO and result-sharing suggestions are state-aware: they offer the action that makes sense now.

func (Model) CompletionOverlay

func (cm Model) CompletionOverlay() string

CompletionOverlay renders the slash-command dropdown above the input.

func (Model) ContextText

func (cm Model) ContextText(ctx Context) string

ContextText builds the assistant context from the snapshot plus the component's session state: database product/version, the last failed query, the schema objects, and the current SQL editor value.

func (Model) DatabaseTools

func (cm Model) DatabaseTools(ctx Context) []ai.ToolDefinition

DatabaseTools returns tool definitions for AI assistant database introspection and writes. Read-only connections expose only read tools.

func (*Model) DeleteHistory

func (cm *Model) DeleteHistory(ctx Context, clear bool) tea.Cmd

DeleteHistory deletes the visible conversation, or clears every conversation when clear is set.

func (Model) Draw

func (cm Model) Draw(canvas uv.ScreenBuffer, layout uikit.Layout)

Draw renders nothing: the chat pane is a lipgloss pane, not a canvas overlay. The contract mirrors the other feature components.

func (*Model) EnterInsertMode

func (cm *Model) EnterInsertMode() tea.Cmd

EnterInsertMode switches the chat input into insert mode and focuses it.

func (*Model) EnterNormalMode

func (cm *Model) EnterNormalMode()

EnterNormalMode switches the chat input into normal mode.

func (Model) ExecuteTool

func (cm Model) ExecuteTool(ctx context.Context, call ai.ToolCall, snapshot Context) ai.ToolResult

ExecuteTool runs one read-only tool call and returns the result.

func (*Model) ExitInsertMode

func (cm *Model) ExitInsertMode()

ExitInsertMode leaves insert mode and blurs the input.

func (Model) GatherConnectionInfo

func (cm Model) GatherConnectionInfo(ctx context.Context, snapshot Context) (string, error)

GatherConnectionInfo builds the connection info text from the snapshot and one read-only query per product.

func (*Model) HandleToolResult

func (cm *Model) HandleToolResult(ctx Context, run *Run, msg ToolResultMsg) (Model, Event, tea.Cmd)

HandleToolResult processes the outcome of an async read-only tool execution.

func (*Model) HandleWriteResult

func (cm *Model) HandleWriteResult(ctx Context, run *Run, msg WriteResultMsg) (Model, Event, tea.Cmd)

HandleWriteResult processes the outcome of an async sql_write execution (YOLO or confirmed).

func (Model) HistoryPickerContent

func (cm Model) HistoryPickerContent() string

HistoryPickerContent renders the open /history conversation picker, or "" when none is open. The root draws the picker overlay; the component renders its body.

func (Model) InsertMode

func (cm Model) InsertMode() bool

InsertMode reports whether the chat input is in insert mode (the root needs it for the write-confirmation escape precedence).

func (*Model) IsActive

func (cm *Model) IsActive(run *Run) bool

IsActive reports whether run backs the visible conversation.

func (*Model) LoadHistory

func (cm *Model) LoadHistory(ctx Context) tea.Cmd

LoadHistory loads the /history conversation list.

func (*Model) LoadMessages

func (cm *Model) LoadMessages(ctx Context, conversationID string) tea.Cmd

LoadMessages loads one conversation's messages and selects it.

func (*Model) MessageBlock

func (cm *Model) MessageBlock(message ai.Message) string

MessageBlock renders one message into its viewport block.

func (*Model) NewConversation

func (cm *Model) NewConversation()

NewConversation cancels and drops the fresh (unsent) run and resets the visible conversation to the fresh view.

func (*Model) ProcessNextToolCall

func (cm *Model) ProcessNextToolCall(ctx Context, run *Run) (Model, Event, tea.Cmd)

ProcessNextToolCall executes the next pending tool call in the current round. sql_write with YOLO off emits ConfirmationRequested; read-only calls execute against the session service; sql_write executes through the root-owned write path (SQLRequested/ConfirmationRequested) and the result arrives as WriteResultMsg.

func (*Model) RecallPromptHistory

func (cm *Model) RecallPromptHistory(direction int) (string, bool)

RecallPromptHistory moves up (direction > 0) or down (direction < 0) through PromptHistory, mirroring the query editor recall: recall starts only at an index of -1, never wraps, and returning to the newest entry clears the recalled value.

func (*Model) RecordPromptHistory

func (cm *Model) RecordPromptHistory(prompt string)

RecordPromptHistory stores an accepted user prompt, newest first, capped at the shared limit, and exits any active recall.

func (*Model) RefreshView

func (cm *Model) RefreshView()

RefreshView re-renders the visible conversation into the viewport.

func (*Model) RenderContent

func (cm *Model) RenderContent(content string) string

RenderContent renders assistant message content. Non-table markdown goes through glamour; GFM table blocks are rendered with lipgloss/v2/table for proper column alignment within the chat viewport width.

func (*Model) Reset

func (cm *Model) Reset()

Reset stops every run and clears all chat state (disconnect path).

func (*Model) Resize

func (cm *Model) Resize(layout uikit.Layout)

Resize refits the input and viewport to the pane geometry.

func (*Model) RunByGen

func (cm *Model) RunByGen(gen int64) *Run

RunByGen finds the run that owns the given turn id.

func (*Model) SetAI

func (cm *Model) SetAI(client Client, history History)

SetAI configures the provider and history store and enables the pane.

func (*Model) SetContext

func (cm *Model) SetContext(ctx context.Context)

SetContext records the root application context run commands derive from. The root calls it once at construction.

func (*Model) StartChat

func (cm *Model) StartChat(ctx Context) (Model, Event, tea.Cmd)

StartChat begins an assistant turn for the current input value. It returns the batch of commands that stream the response; status transitions surface as StatusChanged events.

func (*Model) StreamBlock

func (cm *Model) StreamBlock(content string) string

StreamBlock renders the streaming tail of the active assistant turn.

func (*Model) Update

func (cm *Model) Update(msg tea.Msg, layout uikit.Layout, keys uikit.KeyMatcher, ctx Context) (Model, Event, tea.Cmd)

Update handles the chat messages and pane input. The root routes every chat-owned message and every key press while the chat pane is focused here; the component emits events for root side effects (status, writes, schema refresh, clipboard, editor application).

func (*Model) UpdateChatCompletion

func (cm *Model) UpdateChatCompletion()

UpdateChatCompletion shows slash-command suggestions while the chat input starts with "/", and clears them otherwise. When the input text is unchanged it leaves the current matches and selection alone: events that carry no text (e.g. key releases echoed through the textarea) must not reset the dropdown to its first item.

func (Model) UpdateHistoryPicker

func (cm Model) UpdateHistoryPicker(msg tea.Msg) (Model, HistoryPickerOutcome, tea.Cmd)

UpdateHistoryPicker drives the open /history conversation picker: Escape closes it, every other message passes through to the form, and a completed selection emits the picked conversation. The root loads the conversation through LoadMessages.

func (Model) View

func (cm Model) View(layout uikit.Layout) string

View renders the chat pane body: the message viewport, the input, and the completion dropdown overlay. The root frames the pane and renders the mode badge.

type PendingWrite

type PendingWrite struct {
	Generation int64
	Call       ai.ToolCall
	Statement  string
}

PendingWrite holds state for a sql_write call awaiting user confirmation. The dialog itself is a root overlay; the component reports the request through ConfirmationRequested and waits for the write result message.

type PersistMsg

type PersistMsg struct{ Err error }

PersistMsg reports an error persisting an AI message to history.

type ResponseMsg

type ResponseMsg struct {
	ConversationID string
	Response       ai.Response
	Err            error
}

ResponseMsg carries a non-streaming completion.

type Run

type Run struct {
	ConversationID string
	ConnectionID   string
	Messages       []ai.Message
	// BlockCache holds rendered viewport blocks per message so
	// RefreshView re-renders only appended/replaced messages instead of
	// the whole conversation on every stream delta.
	BlockCache  []Block
	CachedWidth int

	StreamBuffer string // accumulated streaming content
	Loading      bool
	Canceled     bool
	Cancel       context.CancelFunc
	Gen          int64 // turn id; checked on async completions
	SpinnerFrame int   // progress spinner frame while loading
	RoundState   *ToolRoundState
	PendingWrite *PendingWrite
	// contains filtered or unexported fields
}

Run is the full state of one conversation — the active one or any conversation still executing in the background. Runs are independent: switching the visible conversation never interrupts another run. ConnectionID is the profile scope captured when the turn began; a run keeps persisting to it even if the model later disconnects.

type SQLRequested

type SQLRequested struct {
	Statement string
	ReadOnly  bool
	Source    string
}

SQLRequested asks the root to run a statement through its execution paths: Source "editor" applies the statement to the SQL editor (the assistant's apply-SQL command); other sources run through the interactive startQueryStatement path.

type SchemaRequested

type SchemaRequested struct{}

SchemaRequested asks the root to refresh the schema sidebar.

type SpinnerTickMsg

type SpinnerTickMsg struct{}

SpinnerTickMsg advances the assistant progress spinner while loading.

type StatusChanged

type StatusChanged struct{ Text string }

StatusChanged asks the root to record a status line transition.

type StreamMsg

type StreamMsg struct {
	Ch             <-chan ai.StreamEvent
	ConversationID string
	Delta          string
	Response       ai.Response
	Done           bool
	Err            error
}

StreamMsg is sent for each streaming delta from the AI.

type TitleMsg

type TitleMsg struct {
	ConnectionID   string
	ConversationID string
	Title          string
	Err            error
}

TitleMsg reports completion of asynchronous conversation title generation.

type ToolContinueMsg

type ToolContinueMsg struct{ Gen int64 }

ToolContinueMsg signals Update to resume tool-round processing.

type ToolPhaseExpiredMsg

type ToolPhaseExpiredMsg struct {
	Gen   int64
	State *ToolRoundState
}

ToolPhaseExpiredMsg switches an expired tool phase to finalization. State is provided only when the initial tool request expired.

type ToolResultMsg

type ToolResultMsg struct {
	Gen      int64
	CallID   string
	CallName string
	Content  string
	Err      string
}

ToolResultMsg carries the result of an async read-only tool execution.

type ToolRoundState

type ToolRoundState struct {
	Gen                     int64
	Messages                []ai.Message
	AgentID                 string
	Client                  Client
	History                 History
	RootContext             context.Context
	ChatContext             context.Context
	Cancel                  context.CancelFunc
	ToolCancel              context.CancelFunc
	FinalizationCancel      context.CancelFunc
	ContextText             string
	ToolsDefs               []ai.ToolDefinition
	ConversationID          string
	ToolCalls               []ai.ToolCall
	NextCall                int
	ToolCallCount           int
	ToolDeadline            time.Time
	Finalizing              bool
	LastToolResultSignature string
	RepeatedToolResults     int
}

ToolRoundState holds resumable state for one assistant run with tools. It is set by StartChat and cleared when the run ends.

func (*ToolRoundState) RecordToolResult

func (rs *ToolRoundState) RecordToolResult(call ai.ToolCall, content string) bool

RecordToolResult tracks repeated identical tool results and reports whether the repetition limit was reached.

func (*ToolRoundState) ReleaseContexts

func (rs *ToolRoundState) ReleaseContexts()

ReleaseContexts cancels the tool-phase and finalization contexts.

func (*ToolRoundState) SkipRemainingToolCalls

func (rs *ToolRoundState) SkipRemainingToolCalls(reason string)

SkipRemainingToolCalls appends skip results for the calls left in the round (finalization path).

type ToolStartMsg

type ToolStartMsg struct {
	Gen   int64
	State ToolRoundState
}

ToolStartMsg is sent by StartChat's closure with the first Complete response containing tool calls. Update stores the state.

type WriteRequest

type WriteRequest struct {
	Statement  string
	Generation int64
	Deadline   time.Time
}

WriteRequest is the root-side record of a pending assistant write awaiting confirmation: the statement, the turn id to report back, and the tool-phase deadline the execution must honor.

type WriteResultMsg

type WriteResultMsg struct {
	Gen      int64
	CallID   string
	CallName string
	Content  string
	Err      string
	Declined bool // user declined the write; stop round
}

WriteResultMsg carries the result of an async sql_write execution.

Jump to

Keyboard shortcuts

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