ux

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package ux provides user experience components for the Aleutian CLI.

This file defines the core event types for streaming LLM responses. These types form the foundation of the streaming architecture:

Parser → Reader → Renderer
  ↓        ↓         ↓
bytes → events → output

All types in this file are immutable value types with no methods. They are designed to be passed by value through channels and callbacks.

Tracing Convention:

All significant types include Id and CreatedAt fields following the orchestrator's datatypes package patterns. This enables:

  • Request/response correlation across services
  • Audit logging with timestamps
  • Debugging with unique identifiers

Package ux provides user experience components for the Aleutian CLI.

This file defines integrity verification types for hash chain validation. The hash chain provides tamper-evident logging for streaming conversations.

Hash Chain Design:

Each StreamEvent has a Hash computed from its content and a PrevHash
linking to the previous event. This creates a chain similar to blockchain:

Event[0] → Event[1] → Event[2] → ... → Event[N]
  Hash₀     Hash₁     Hash₂           HashN
    ↑         ↑         ↑               ↑
    └─────────┴─────────┴───────────────┘
           Each PrevHash links to previous Hash

If any event is modified, its hash changes, breaking the chain.

Package ux provides rich terminal output styling for the Aleutian CLI.

Package ux provides user experience components for the Aleutian CLI.

This file contains parsers for streaming response formats. Parsers are responsible for converting raw bytes/lines into StreamEvent structs.

Single Responsibility:

Parsers ONLY parse. They do not perform I/O, rendering, or state management.
This separation enables easy testing and format extensibility.

Supported Formats:

  • SSE (Server-Sent Events): Standard format for HTTP streaming
  • Future: JSONL, NDJSON

Package ux provides user experience components for the Aleutian CLI.

This file contains stream readers that consume io.Reader sources and emit parsed events via callbacks.

Single Responsibility:

Readers handle I/O and event sequencing. They use parsers to convert
bytes to events, but do not render output. This separation enables
flexible composition with different renderers.

Context Support:

All readers accept context.Context for cancellation and timeout.
When context is cancelled, reading stops and the error is returned.

Package ux provides user experience components for the Aleutian CLI.

This file contains stream renderers that display streaming events to various outputs (terminal, buffer, etc.).

Single Responsibility:

Renderers ONLY render. They do not parse, read, or manage HTTP.
Each method handles exactly one event type, enabling clean composition.

Renderer Types:

  • TerminalStreamRenderer: Interactive terminal with spinners and colors
  • MachineStreamRenderer: Machine-readable KEY: value format
  • BufferStreamRenderer: In-memory buffer for testing

Index

Constants

This section is empty.

Variables

View Source
var (
	// Primary palette (brightest to darkest)
	ColorTealBright  = lipgloss.Color("#2CD7C7") // Bright teal - highlights, success
	ColorTealPrimary = lipgloss.Color("#20B9B4") // Primary teal - main brand color
	ColorTealVibrant = lipgloss.Color("#1D9EA3") // Vibrant teal - interactive elements
	ColorTealMedium  = lipgloss.Color("#1D9DA0") // Medium teal - secondary elements
	ColorTealDeep    = lipgloss.Color("#16858E") // Deep teal - borders, accents
	ColorTealOcean   = lipgloss.Color("#157483") // Ocean teal - subtle accents

	// Dark palette (for backgrounds, muted elements)
	ColorDeepSea  = lipgloss.Color("#104855") // Deep sea blue
	ColorAbyss    = lipgloss.Color("#0C424E") // Abyss - darker backgrounds
	ColorMidnight = lipgloss.Color("#0D2F39") // Midnight - deep backgrounds
	ColorSlate    = lipgloss.Color("#2C4A54") // Slate - muted text, borders
	ColorDarkest  = lipgloss.Color("#0F1923") // Darkest - near black

	// Semantic colors (keeping standard conventions for clarity)
	ColorSuccess = lipgloss.Color("#2CD7C7") // Bright teal for success
	ColorWarning = lipgloss.Color("#F4D03F") // Gold/amber for warnings
	ColorError   = lipgloss.Color("#E74C3C") // Red for errors
	ColorMuted   = lipgloss.Color("#2C4A54") // Slate for muted text
)

Aleutian color palette - deep ocean teals and arctic waters

View Source
var Styles = struct {
	// Text styles
	Title     lipgloss.Style
	Subtitle  lipgloss.Style
	Bold      lipgloss.Style
	Muted     lipgloss.Style
	Success   lipgloss.Style
	Warning   lipgloss.Style
	Error     lipgloss.Style
	Highlight lipgloss.Style

	// Box styles
	Box        lipgloss.Style
	InfoBox    lipgloss.Style
	WarningBox lipgloss.Style
	ErrorBox   lipgloss.Style

	// Status indicators
	StatusOK      lipgloss.Style
	StatusWarning lipgloss.Style
	StatusError   lipgloss.Style
	StatusPending lipgloss.Style
}{

	Title:     lipgloss.NewStyle().Bold(true).Foreground(ColorTealBright),
	Subtitle:  lipgloss.NewStyle().Foreground(ColorTealPrimary),
	Bold:      lipgloss.NewStyle().Bold(true),
	Muted:     lipgloss.NewStyle().Foreground(ColorSlate),
	Success:   lipgloss.NewStyle().Foreground(ColorSuccess),
	Warning:   lipgloss.NewStyle().Foreground(ColorWarning),
	Error:     lipgloss.NewStyle().Foreground(ColorError),
	Highlight: lipgloss.NewStyle().Foreground(ColorTealBright).Bold(true),

	Box: lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(ColorTealDeep).
		Padding(0, 1),
	InfoBox: lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(ColorTealPrimary).
		Padding(0, 1),
	WarningBox: lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(ColorWarning).
		Padding(0, 1),
	ErrorBox: lipgloss.NewStyle().
		Border(lipgloss.RoundedBorder()).
		BorderForeground(ColorError).
		Padding(0, 1),

	StatusOK:      lipgloss.NewStyle().SetString("✓").Foreground(ColorSuccess),
	StatusWarning: lipgloss.NewStyle().SetString("⚠").Foreground(ColorWarning),
	StatusError:   lipgloss.NewStyle().SetString("✗").Foreground(ColorError),
	StatusPending: lipgloss.NewStyle().SetString("○").Foreground(ColorSlate),
}

Styles provides pre-configured lipgloss styles

Functions

func AskChoice

func AskChoice(question string, options []PromptOption) (string, error)

AskChoice presents an interactive selection prompt and returns the selected value

func AskConfirm

func AskConfirm(question string, defaultYes bool) (bool, error)

AskConfirm presents a yes/no confirmation prompt

func AskInput

func AskInput(question string, placeholder string) (string, error)

AskInput prompts for text input

func Box

func Box(title, content string)

Box prints text in a rounded box

func ChatError

func ChatError(err error)

ChatError prints a chat error (convenience function)

func ChatHeader

func ChatHeader(mode ChatMode, pipeline string, sessionID string)

ChatHeader prints the chat session header (convenience function)

func ChatNoSources

func ChatNoSources()

ChatNoSources prints a message when no sources were found (convenience function)

func ChatPrompt

func ChatPrompt() string

ChatPrompt returns the styled prompt string (convenience function)

func ChatResponse

func ChatResponse(answer string)

ChatResponse prints the assistant's response (convenience function)

func ChatSessionEnd

func ChatSessionEnd(sessionID string)

ChatSessionEnd prints session end info (convenience function)

func ChatSessionResume

func ChatSessionResume(sessionID string, turnCount int)

ChatSessionResume prints session resume info (convenience function)

func ChatSources

func ChatSources(sources []SourceInfo)

ChatSources prints the sources used in a RAG response (convenience function)

func Error

func Error(text string)

Error prints an error message

func FileStatus

func FileStatus(path string, status Icon, reason string)

FileStatus prints a file with its scan status

func Info

func Info(text string)

Info prints an informational message

func InitPersonality

func InitPersonality()

InitPersonality initializes personality from environment and defaults

func IsInteractive

func IsInteractive() bool

IsInteractive returns true if we should show interactive prompts

func Muted

func Muted(text string)

Muted prints muted/secondary text

func ProgressBar

func ProgressBar(current, total int, width int) string

ProgressBar renders a simple progress bar

func SetPersonality

func SetPersonality(p Personality)

SetPersonality updates the current personality settings

func SetPersonalityLevel

func SetPersonalityLevel(level PersonalityLevel)

SetPersonalityLevel updates just the personality level

func ShouldShowColors

func ShouldShowColors() bool

ShouldShowColors returns true if we should use colors

func ShouldShowProgress

func ShouldShowProgress() bool

ShouldShowProgress returns true if we should show progress indicators

func Success

func Success(text string)

Success prints a success message with checkmark

func Summary

func Summary(approved, skipped, total int)

Summary prints a summary line with counts

func Title

func Title(text string)

Title prints a styled title

func Warning

func Warning(text string)

Warning prints a warning message

func WarningBox

func WarningBox(title, content string)

WarningBox prints text in a warning-styled box

func WithSpinner

func WithSpinner(message string, fn func() error) error

WithSpinner runs a function with a spinner, handling success/error automatically

Types

type AuditLogger

type AuditLogger interface {
	// LogVerificationAttempt logs a verification attempt.
	//
	// # Inputs
	//
	//   - event: Verification event details
	//
	// # Outputs
	//
	//   - error: Non-nil if logging failed (should not block verification)
	LogVerificationAttempt(event *VerificationAuditEvent) error
}

AuditLogger logs verification events for compliance.

Description

Enterprise extension for compliance audit logging. All verification attempts are logged with full context.

Thread Safety

Implementations must be safe for concurrent use.

Enterprise Use Cases

  • SOC 2 audit requirements
  • GDPR access logging
  • HIPAA audit trails

type ChainVerificationResult

type ChainVerificationResult struct {
	Valid             bool   `json:"valid"`
	ChainLength       int    `json:"chain_length"`
	FinalHash         string `json:"final_hash,omitempty"`
	InvalidEventIndex int    `json:"invalid_event_index"`
	ExpectedHash      string `json:"expected_hash,omitempty"`
	ActualHash        string `json:"actual_hash,omitempty"`
	ErrorMessage      string `json:"error_message,omitempty"`
}

ChainVerificationResult contains detailed results from chain verification.

Description

Returned by ChainVerifier.Verify to provide detailed information about the verification process, including where any failures occurred.

Fields

  • Valid: Whether the entire chain is valid
  • ChainLength: Number of events verified
  • FinalHash: The hash of the last event in the chain
  • InvalidEventIndex: Index of first invalid event (-1 if all valid)
  • ExpectedHash: What the hash should have been (if invalid)
  • ActualHash: What the hash actually was (if invalid)
  • ErrorMessage: Human-readable error description

Thread Safety

Immutable after creation. Safe for concurrent read access.

type ChainVerifier

type ChainVerifier interface {
	// Verify checks the integrity of a sequence of stream events.
	//
	// # Description
	//
	// Verifies that the hash chain is unbroken and valid.
	//
	// # Inputs
	//
	//   - events: Ordered list of stream events from the session
	//
	// # Outputs
	//
	//   - *ChainVerificationResult: Detailed verification results
	//
	// # Examples
	//
	//   verifier := NewFullChainVerifier(hashComputer)
	//   result := verifier.Verify(events)
	//   if !result.Valid {
	//       log.Warn("chain broken", "error", result.ErrorMessage)
	//   }
	//
	// # Limitations
	//
	//   - Implementation-specific verification depth
	//
	// # Assumptions
	//
	//   - Events are in chronological order
	//   - First event has empty PrevHash
	Verify(events []StreamEvent) *ChainVerificationResult
}

ChainVerifier verifies the integrity of a hash chain.

Description

Abstracts the verification of event chains, allowing different verification strategies (quick PrevHash check vs full recompute).

Thread Safety

Implementations must be safe for concurrent use.

func NewFullChainVerifier

func NewFullChainVerifier() ChainVerifier

NewFullChainVerifier creates a verifier that recomputes all hashes.

Description

Creates a comprehensive verifier that recomputes each event's hash and verifies both hash correctness and chain links.

Outputs

  • ChainVerifier: Full verification implementation

Examples

verifier := NewFullChainVerifier()
result := verifier.Verify(events)

Limitations

  • Slower than quick verification (O(n) hash computations)

Assumptions

  • Events contain correct Content and CreatedAt fields

type ChatMode

type ChatMode int

ChatMode represents the chat operation mode

const (
	ChatModeRAG ChatMode = iota
	ChatModeDirect
)

type ChatUI

type ChatUI interface {
	// Header displays the chat session header with mode and configuration.
	// Deprecated: Use HeaderWithConfig for new code.
	Header(mode ChatMode, pipeline, sessionID string)

	// HeaderWithConfig displays the chat session header with full configuration.
	// This method supports displaying TTL, dataspace, and other metadata.
	HeaderWithConfig(config HeaderConfig)

	// Prompt returns the styled input prompt string
	Prompt() string

	// Response displays the assistant's response
	Response(answer string)

	// Sources displays the sources used in a RAG response
	Sources(sources []SourceInfo)

	// NoSources displays a message when no sources were found
	NoSources()

	// Error displays a chat error message
	Error(err error)

	// SessionResume displays session resume information
	SessionResume(sessionID string, turnCount int)

	// SessionEnd displays session end information
	SessionEnd(sessionID string)

	// SessionEndRich displays rich session end information with stats.
	//
	// This is the "maximalist" session end experience, showing:
	//   - Session ID with copy hint
	//   - Session statistics (messages, tokens, duration)
	//   - Commands for interacting with the session (resume, history)
	//
	// Use this instead of SessionEnd when you have accumulated stats.
	SessionEndRich(sessionID string, stats *SessionStats)

	// ShowAutoCorrection displays a notification that a typo was auto-corrected.
	//
	// # Description
	//
	// Called when a high-confidence typo correction is applied automatically.
	// Informs the user what was corrected so they can undo if needed.
	//
	// # Inputs
	//
	//   - original: The original (misspelled) word
	//   - corrected: The corrected word that will be used
	ShowAutoCorrection(original, corrected string)

	// ShowCorrectionSuggestion displays a suggestion for a possible typo.
	//
	// # Description
	//
	// Called for lower-confidence corrections that require user confirmation.
	// The user can retype their query if the suggestion is correct.
	//
	// # Inputs
	//
	//   - original: The original (possibly misspelled) word
	//   - suggested: The suggested correction
	ShowCorrectionSuggestion(original, suggested string)
}

ChatUI defines the interface for chat user interface operations. Implementations handle rendering chat elements to different outputs.

func NewChatUI

func NewChatUI() ChatUI

NewChatUI creates a new terminal-based ChatUI

func NewChatUIWithWriter

func NewChatUIWithWriter(w io.Writer, personality PersonalityLevel) ChatUI

NewChatUIWithWriter creates a ChatUI with a custom writer (for testing)

type DataSpaceStats added in v1.0.0

type DataSpaceStats struct {
	DocumentCount int   `json:"document_count"`  // Actually chunk count
	LastUpdatedAt int64 `json:"last_updated_at"` // Unix ms timestamp
}

DataSpaceStats contains aggregated metrics for a dataspace.

Description

DataSpaceStats captures aggregate information about chunks within a dataspace. This is fetched from the orchestrator and displayed in the chat header.

Note: DocumentCount actually represents chunk count, not unique source documents. A single uploaded file may produce many chunks for vector search.

Fields

  • DocumentCount: Number of chunks in the dataspace (not unique documents)
  • LastUpdatedAt: Unix milliseconds of most recent document ingestion

type HSMProvider

type HSMProvider interface {
	// SignWithHSM signs content using an HSM-stored key.
	//
	// # Inputs
	//
	//   - keyLabel: Label of the key in HSM
	//   - content: Content to sign
	//
	// # Outputs
	//
	//   - []byte: Signature bytes
	//   - error: Non-nil if HSM operation failed
	SignWithHSM(keyLabel string, content []byte) ([]byte, error)

	// VerifyWithHSM verifies a signature using HSM.
	//
	// # Inputs
	//
	//   - keyLabel: Label of the public key in HSM
	//   - content: Original content
	//   - signature: Signature to verify
	//
	// # Outputs
	//
	//   - bool: True if signature valid
	//   - error: Non-nil if HSM operation failed
	VerifyWithHSM(keyLabel string, content []byte, signature []byte) (bool, error)
}

HSMProvider provides Hardware Security Module integration.

Description

Enterprise extension for PKCS#11 HSM integration. Keys never leave the HSM, providing highest security level.

Thread Safety

Implementations must be safe for concurrent use.

Enterprise Use Cases

  • FIPS 140-2 Level 3 compliance
  • PCI-DSS key management
  • Government security requirements

type HashComputer

type HashComputer interface {
	// ComputeEventHash computes the hash for a stream event.
	//
	// # Description
	//
	// Computes hash using: SHA256(Content || CreatedAt || PrevHash)
	//
	// # Inputs
	//
	//   - content: The event content
	//   - createdAt: Creation timestamp (Unix ms)
	//   - prevHash: Hash of previous event
	//
	// # Outputs
	//
	//   - string: 64-character hex hash
	//
	// # Examples
	//
	//   computer := NewSHA256HashComputer()
	//   hash := computer.ComputeEventHash("Hello", 1735657200000, "abc...")
	//
	// # Limitations
	//
	//   - Hash algorithm is implementation-specific
	//
	// # Assumptions
	//
	//   - Inputs are valid (no nil checks)
	ComputeEventHash(content string, createdAt int64, prevHash string) string

	// ComputeContentHash computes a simple hash of content.
	//
	// # Description
	//
	// Computes SHA256 hash of the provided content string.
	//
	// # Inputs
	//
	//   - content: The content to hash
	//
	// # Outputs
	//
	//   - string: 64-character hex hash
	//
	// # Examples
	//
	//   hash := computer.ComputeContentHash("The answer is 42.")
	//
	// # Limitations
	//
	//   - Empty content produces a valid hash
	//
	// # Assumptions
	//
	//   - Content is valid UTF-8
	ComputeContentHash(content string) string
}

HashComputer computes cryptographic hashes.

Description

Abstracts hash computation for testability and algorithm flexibility.

Thread Safety

Implementations must be safe for concurrent use.

func NewSHA256HashComputer

func NewSHA256HashComputer() HashComputer

NewSHA256HashComputer creates a hash computer using SHA-256.

Description

Creates the production hash computer implementation.

Outputs

  • HashComputer: SHA-256 implementation

Examples

computer := NewSHA256HashComputer()
hash := computer.ComputeContentHash("Hello")

Limitations

  • SHA-256 only (no algorithm selection)

Assumptions

  • SHA-256 is available in the runtime

type HeaderConfig added in v1.0.0

type HeaderConfig struct {
	Mode           ChatMode
	Pipeline       string
	SessionID      string
	TTL            string
	DataSpace      string
	DataSpaceStats *DataSpaceStats // Optional stats from orchestrator
}

HeaderConfig contains configuration for displaying the chat header.

Description

HeaderConfig groups all optional parameters for the chat header display. This allows extending the header with new fields without breaking existing callers of the Header() method.

Fields

  • Mode: Required. RAG or Direct chat mode.
  • Pipeline: RAG pipeline name (e.g., "verified", "reranking"). Empty for direct mode.
  • SessionID: Session identifier for resume. May be empty for new sessions.
  • TTL: Session TTL as configured (e.g., "5m", "24h"). Empty if no TTL.
  • DataSpace: Dataspace being queried (e.g., "wheat", "work"). Empty for all docs.
  • DataSpaceStats: Optional aggregated stats for the dataspace.

type Icon

type Icon string

Icon provides themed status icons

const (
	IconSuccess  Icon = "✓"
	IconWarning  Icon = "⚠"
	IconError    Icon = "✗"
	IconPending  Icon = "○"
	IconArrow    Icon = "→"
	IconBullet   Icon = "•"
	IconAnchor   Icon = "⚓"
	IconShip     Icon = "⛵"
	IconWave     Icon = "〰"
	IconChat     Icon = "💬"
	IconInfo     Icon = "ℹ"
	IconDocument Icon = "📄"
	IconTime     Icon = "⏱"
)

func (Icon) Render

func (i Icon) Render() string

Render returns the icon with appropriate styling

type IntegrityInfo

type IntegrityInfo struct {
	ChainHash         string            `json:"chain_hash"`
	ContentHash       string            `json:"content_hash"`
	TurnHashes        map[int]string    `json:"turn_hashes,omitempty"`
	SourceHashes      map[string]string `json:"source_hashes,omitempty"`
	ChainLength       int               `json:"chain_length"`
	IntegrityVerified bool              `json:"integrity_verified"`
	VerificationError string            `json:"verification_error,omitempty"`
	VerifiedAt        int64             `json:"verified_at,omitempty"`
}

IntegrityInfo contains hash chain and integrity verification information.

Description

Surfaces the cryptographic integrity features to users, showing them that their conversation is protected by a hash chain. This builds trust and enables verification of data integrity.

The hash chain works like a blockchain:

  • Each StreamEvent has a SHA-256 Hash of its content
  • Each event's PrevHash links to the previous event
  • The ChainHash is the final hash of the entire stream
  • Any tampering breaks the chain (hash mismatch)

Existing Implementation (pkg/ux/events.go)

This surfaces values already computed by the streaming infrastructure:

  • StreamEvent.Hash (line 191): SHA-256 of Content || CreatedAt || PrevHash
  • StreamEvent.PrevHash (line 195): Links to previous event
  • StreamResult.ChainHash (line 282): Final hash from last event
  • StreamResult.ContentHash (line 287): SHA-256 of accumulated answer

Fields

  • ChainHash: Final hash of the streaming chain (64-char hex)
  • ContentHash: SHA-256 of last response content
  • TurnHashes: Hash of each Q&A turn
  • SourceHashes: Hash of each retrieved source
  • ChainLength: Number of events in chain
  • IntegrityVerified: Whether verification passed
  • VerificationError: Details if verification failed
  • VerifiedAt: When verification was performed

Privacy

Hashes are safe to display - they cannot be reversed to reveal content. They serve as fingerprints that prove content hasn't been modified.

Thread Safety

IntegrityInfo is NOT thread-safe. Use external synchronization if modifying from multiple goroutines.

func NewIntegrityInfo

func NewIntegrityInfo(result *StreamResult, verified bool) *IntegrityInfo

NewIntegrityInfo creates an IntegrityInfo from a StreamResult.

Description

Extracts hash chain information from a completed stream result. This is the primary way to create IntegrityInfo after streaming.

Inputs

  • result: The completed StreamResult containing hash chain data
  • verified: Whether the chain has been verified

Outputs

  • *IntegrityInfo: Populated integrity information

Examples

result := &StreamResult{ChainHash: "abc...", ContentHash: "def..."}
info := NewIntegrityInfo(result, true)

Limitations

  • TurnHashes and SourceHashes are not populated by this function
  • Caller must populate those fields separately if needed

Assumptions

  • result is non-nil and contains valid hash data

func NewIntegrityInfoFromVerification

func NewIntegrityInfoFromVerification(verification *ChainVerificationResult) *IntegrityInfo

NewIntegrityInfoFromVerification creates IntegrityInfo from verification result.

Description

Creates an IntegrityInfo with verification results populated. Use after calling Verify on a ChainVerifier.

Inputs

  • verification: Result from ChainVerifier.Verify

Outputs

  • *IntegrityInfo: Populated with verification results

Examples

verifier := NewFullChainVerifier(hashComputer)
verification := verifier.Verify(events)
info := NewIntegrityInfoFromVerification(verification)

Limitations

  • ContentHash is not set (not available in verification result)

Assumptions

  • verification is non-nil

func (*IntegrityInfo) AddSourceHash

func (i *IntegrityInfo) AddSourceHash(sourceName, content string)

AddSourceHash adds a hash for a retrieved source.

Description

Stores the content hash for a retrieved document source. Used to verify source content hasn't been modified.

Inputs

  • sourceName: The source identifier/name
  • content: The source content

Outputs

None. Modifies SourceHashes in place.

Examples

info := NewIntegrityInfo(result, true)
info.AddSourceHash("document.pdf", "The content...")

Limitations

  • Overwrites existing hash for the same source name

Assumptions

  • SourceHashes map is initialized

func (*IntegrityInfo) AddTurnHash

func (i *IntegrityInfo) AddTurnHash(turnNumber int, question, answer string)

AddTurnHash adds a hash for a conversation turn.

Description

Computes and stores a hash for a Q&A turn. The hash is computed from the concatenation of question and answer.

Inputs

  • turnNumber: 1-indexed turn number
  • question: The user's question
  • answer: The LLM's answer

Outputs

None. Modifies TurnHashes in place.

Examples

info := NewIntegrityInfo(result, true)
info.AddTurnHash(1, "What is 2+2?", "2+2 equals 4.")

Limitations

  • Overwrites existing hash for the same turn number

Assumptions

  • TurnHashes map is initialized

func (*IntegrityInfo) FormatForDisplay

func (i *IntegrityInfo) FormatForDisplay() string

FormatForDisplay returns a formatted string for UI display.

Description

Creates a human-readable summary of the integrity information suitable for display in the session summary.

Outputs

  • string: Formatted integrity summary

Examples

info := &IntegrityInfo{ChainLength: 47, IntegrityVerified: true}
fmt.Println(info.FormatForDisplay())
// "✓ Verified | Chain: 47 events | Hash: a3f2c8d9...e7f8a9b0"

Limitations

  • Hash is truncated for display

Assumptions

  • ChainHash is a valid hex string or empty

func (*IntegrityInfo) GetSourceHash

func (i *IntegrityInfo) GetSourceHash(sourceName string) (string, bool)

GetSourceHash returns the hash for a specific source.

Description

Retrieves the previously stored hash for a source document.

Inputs

  • sourceName: The source identifier

Outputs

  • string: The source hash, or empty string if not found
  • bool: True if the source hash exists

Examples

hash, ok := info.GetSourceHash("document.pdf")
if ok {
    fmt.Println("Document hash:", hash)
}

Limitations

  • Returns empty string for non-existent sources

Assumptions

  • SourceHashes map is initialized

func (*IntegrityInfo) GetTurnHash

func (i *IntegrityInfo) GetTurnHash(turnNumber int) (string, bool)

GetTurnHash returns the hash for a specific turn.

Description

Retrieves the previously stored hash for a conversation turn.

Inputs

  • turnNumber: 1-indexed turn number

Outputs

  • string: The turn hash, or empty string if not found
  • bool: True if the turn hash exists

Examples

hash, ok := info.GetTurnHash(1)
if ok {
    fmt.Println("Turn 1 hash:", hash)
}

Limitations

  • Returns empty string for non-existent turns

Assumptions

  • TurnHashes map is initialized

type KeyedHashComputer

type KeyedHashComputer interface {
	// ComputeHMAC computes a keyed hash for content.
	//
	// # Inputs
	//
	//   - keyID: Identifier for the key to use (for key rotation)
	//   - content: Content to hash
	//
	// # Outputs
	//
	//   - string: Hex-encoded HMAC
	//   - error: Non-nil if key not found or HSM unavailable
	ComputeHMAC(keyID string, content string) (string, error)

	// VerifyHMAC verifies a keyed hash.
	//
	// # Inputs
	//
	//   - keyID: Identifier for the key used
	//   - content: Original content
	//   - expectedHMAC: HMAC to verify against
	//
	// # Outputs
	//
	//   - bool: True if HMAC matches
	//   - error: Non-nil if verification could not be performed
	VerifyHMAC(keyID string, content string, expectedHMAC string) (bool, error)
}

KeyedHashComputer computes keyed hashes (HMAC) for enhanced security.

Description

Enterprise extension for HMAC-based verification. Unlike simple SHA-256, HMAC requires a secret key, providing authentication in addition to integrity verification.

Thread Safety

Implementations must be safe for concurrent use.

Enterprise Use Cases

  • Multi-tenant environments requiring tenant-specific keys
  • Regulatory compliance requiring keyed verification (FIPS 140-2)
  • Non-repudiation with organizational keys

type Personality

type Personality struct {
	// Level controls overall verbosity (full, standard, minimal, machine)
	Level PersonalityLevel

	// Theme is the color theme (reserved for future use)
	Theme string

	// ShowTips enables "message in a bottle" style tips
	ShowTips bool

	// NauticalMode enables nautical terminology throughout
	NauticalMode bool
}

Personality holds the current UX personality configuration

func DefaultPersonality

func DefaultPersonality() Personality

DefaultPersonality returns the default personality settings

func GetPersonality

func GetPersonality() Personality

GetPersonality returns the current personality settings

type PersonalityLevel

type PersonalityLevel string

PersonalityLevel defines the verbosity and richness of CLI output

const (
	// PersonalityFull enables all visual flourishes, nautical theming, and rich formatting
	PersonalityFull PersonalityLevel = "full"

	// PersonalityStandard enables colors, icons, and boxes but minimal theming
	PersonalityStandard PersonalityLevel = "standard"

	// PersonalityMinimal uses icons and basic formatting only
	PersonalityMinimal PersonalityLevel = "minimal"

	// PersonalityMachine outputs plain text suitable for scripting and parsing
	PersonalityMachine PersonalityLevel = "machine"
)

func ParsePersonalityLevel

func ParsePersonalityLevel(s string) PersonalityLevel

ParsePersonalityLevel converts a string to PersonalityLevel

type ProgressSpinner

type ProgressSpinner struct {
	*Spinner
	// contains filtered or unexported fields
}

ProgressSpinner combines a spinner with progress tracking

func NewProgressSpinner

func NewProgressSpinner(message string, total int) *ProgressSpinner

NewProgressSpinner creates a spinner that shows progress

func (*ProgressSpinner) Increment

func (p *ProgressSpinner) Increment()

Increment advances the progress counter

func (*ProgressSpinner) SetProgress

func (p *ProgressSpinner) SetProgress(current int)

SetProgress sets the current progress value

type PromptOption

type PromptOption struct {
	Label       string
	Description string
	Value       string
	Recommended bool
}

PromptOption represents a selectable option in a prompt

type SSEParser

type SSEParser interface {
	// ParseLine parses a single line of SSE input.
	//
	// Parameters:
	//   - line: A single line from the SSE stream (without trailing newline)
	//
	// Returns:
	//   - *StreamEvent: The parsed event, or nil for empty/comment lines
	//   - error: Non-nil if JSON parsing failed
	//
	// Line handling:
	//   - Empty lines: Returns nil, nil (event delimiter)
	//   - Comment lines (":"): Returns nil, nil (ignored)
	//   - Data lines ("data: "): Parses JSON payload
	//   - Other lines: Treated as raw token content
	ParseLine(line string) (*StreamEvent, error)

	// ParseRawJSON parses a raw JSON payload into a StreamEvent.
	//
	// Use this when you have JSON without the "data: " prefix.
	// Automatically generates Id and sets CreatedAt.
	//
	// Parameters:
	//   - jsonData: Raw JSON bytes
	//
	// Returns:
	//   - *StreamEvent: The parsed event
	//   - error: Non-nil if JSON parsing failed
	ParseRawJSON(jsonData []byte) (*StreamEvent, error)
}

SSEParser parses Server-Sent Events format into StreamEvent structs.

SSE Format Reference (https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events):

data: {"type":"token","content":"Hello"}\n
\n
data: {"type":"token","content":" world"}\n
\n

Each line starting with "data: " contains a JSON payload. Empty lines are event delimiters (ignored by this parser). Lines starting with ":" are comments (ignored).

Thread Safety:

SSEParser implementations must be safe for concurrent use.
The default implementation is stateless and inherently thread-safe.

Example:

parser := NewSSEParser()
event, err := parser.ParseLine(`data: {"type":"token","content":"Hi"}`)
if err != nil {
    log.Fatal(err)
}
if event != nil {
    fmt.Println(event.Content) // "Hi"
}

func NewSSEParser

func NewSSEParser() SSEParser

NewSSEParser creates a new SSE parser.

The returned parser is stateless and can be safely shared across goroutines.

Example:

parser := NewSSEParser()
event, _ := parser.ParseLine(`data: {"type":"done","session_id":"sess-123"}`)

type SecretAction

type SecretAction string

SecretAction is the user's decision about a file with secrets

const (
	SecretActionSkip     SecretAction = "skip"
	SecretActionRedact   SecretAction = "redact"
	SecretActionProceed  SecretAction = "proceed"
	SecretActionShowMore SecretAction = "show"
)

func AskSecretAction

func AskSecretAction(opts SecretPromptOptions) (SecretAction, error)

AskSecretAction presents the bidirectional prompt for files with detected secrets

type SecretFinding

type SecretFinding struct {
	LineNumber  int
	PatternID   string
	PatternName string
	Confidence  string
	Match       string
	Reason      string
}

SecretFinding represents a detected secret in a file

type SecretPromptOptions

type SecretPromptOptions struct {
	FilePath      string
	Findings      []SecretFinding
	ShowRedact    bool
	ShowForceSkip bool
}

SecretPromptOptions holds options for the secret detection prompt

type SessionStats

type SessionStats struct {
	MessageCount         int
	TotalTokens          int
	ThinkingTokens       int
	SourcesUsed          int
	Duration             time.Duration
	FirstResponseLatency time.Duration
	AverageResponseTime  time.Duration
}

SessionStats aggregates metrics from a chat session for display.

Description

SessionStats captures accumulated metrics across all exchanges in a chat session. It's designed to be displayed when the session ends, giving users visibility into their session's performance and usage.

Fields

  • MessageCount: Number of user messages sent
  • TotalTokens: Total tokens generated across all responses
  • ThinkingTokens: Total thinking tokens (Claude extended thinking)
  • SourcesUsed: Number of unique sources referenced
  • Duration: Total session duration
  • FirstResponseLatency: Time to first token of first response
  • AverageResponseTime: Average time per response

type SignatureVerifier

type SignatureVerifier interface {
	// VerifySignature verifies a digital signature.
	//
	// # Inputs
	//
	//   - content: Content that was signed
	//   - signature: Base64-encoded signature
	//   - signerID: Identifier for the signer's public key
	//
	// # Outputs
	//
	//   - bool: True if signature is valid
	//   - error: Non-nil if verification could not be performed
	VerifySignature(content string, signature string, signerID string) (bool, error)
}

SignatureVerifier verifies digital signatures on content.

Description

Enterprise extension for cryptographic signature verification. Supports RSA, ECDSA, and Ed25519 signatures for non-repudiation.

Thread Safety

Implementations must be safe for concurrent use.

Enterprise Use Cases

  • Legal non-repudiation requirements
  • Regulatory compliance (eIDAS, ESIGN)
  • Multi-party verification

type SourceInfo

type SourceInfo struct {
	Id            string  `json:"id,omitempty"`
	CreatedAt     int64   `json:"created_at,omitempty"`
	Source        string  `json:"source"`
	Distance      float64 `json:"distance,omitempty"`
	Score         float64 `json:"score,omitempty"`
	Hash          string  `json:"hash,omitempty"`
	VersionNumber *int    `json:"version_number,omitempty"`
	IsCurrent     *bool   `json:"is_current,omitempty"`
}

SourceInfo represents a source citation from RAG retrieval.

Description

SourceInfo captures metadata about a document retrieved during RAG processing. Each source has a unique ID and timestamp for database storage and audit trails.

Fields

  • Id: Unique identifier for this source record (UUID v4).
  • CreatedAt: Unix timestamp in milliseconds when source was retrieved.
  • Source: Document name, path, or URL identifying the source.
  • Distance: Vector distance (lower = more similar). Used by some pipelines.
  • Score: Relevance score (higher = more relevant). Used by reranking pipelines.
  • Hash: SHA-256 hash of source content for tamper detection.
  • VersionNumber: Document version (1, 2, 3...). Nil for legacy docs.
  • IsCurrent: True if this is the latest version. Nil for legacy docs.

type Spinner

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

Spinner provides an animated loading indicator

func NewSpinner

func NewSpinner(message string) *Spinner

NewSpinner creates a new spinner with the given message

func (*Spinner) Start

func (s *Spinner) Start()

Start begins the spinner animation

func (*Spinner) Stop

func (s *Spinner) Stop()

Stop halts the spinner animation

func (*Spinner) StopWithError

func (s *Spinner) StopWithError(message string)

StopWithError stops and prints an error message

func (*Spinner) StopWithSuccess

func (s *Spinner) StopWithSuccess(message string)

StopWithSuccess stops and prints a success message

func (*Spinner) StopWithWarning

func (s *Spinner) StopWithWarning(message string)

StopWithWarning stops and prints a warning message

func (*Spinner) UpdateMessage

func (s *Spinner) UpdateMessage(message string)

UpdateMessage changes the spinner message while running

func (*Spinner) WithType

func (s *Spinner) WithType(t SpinnerType) *Spinner

WithType sets the spinner animation type

type SpinnerType

type SpinnerType int

SpinnerType defines the animation style

const (
	SpinnerDots SpinnerType = iota
	SpinnerWave
	SpinnerAnchor
	SpinnerCompass
)

type StreamCallback

type StreamCallback func(event StreamEvent) error

StreamCallback is invoked for each streaming event.

Implementations should be fast and non-blocking. If processing takes significant time, consider buffering events in a channel.

Return nil to continue processing. Return an error to stop the stream early; the error will be propagated to the StreamReader caller.

Example:

callback := func(event StreamEvent) error {
    switch event.Type {
    case StreamEventToken:
        fmt.Print(event.Content)
    case StreamEventError:
        return errors.New(event.Error)
    }
    return nil
}

type StreamEvent

type StreamEvent struct {
	// Id is the unique identifier for this event.
	// Generated by the parser or constructor, used for tracing.
	Id string `json:"id"`

	// CreatedAt is the Unix timestamp (milliseconds) when this event was created.
	// This is the local time when the client parsed/created the event.
	CreatedAt int64 `json:"created_at"`

	// Type categorizes this event.
	// This field is always set and determines which other fields are valid.
	Type StreamEventType `json:"type"`

	// Index is the zero-based position of this event in the stream.
	// Set by the StreamReader as events are processed.
	Index int `json:"index"`

	// Content holds token or thinking text.
	// Set when Type is StreamEventToken or StreamEventThinking.
	Content string `json:"content,omitempty"`

	// Message holds status update text.
	// Set when Type is StreamEventStatus.
	Message string `json:"message,omitempty"`

	// Sources holds retrieved knowledge base sources.
	// Set when Type is StreamEventSources.
	Sources []SourceInfo `json:"sources,omitempty"`

	// SessionID holds the conversation session identifier.
	// Set when Type is StreamEventDone.
	SessionID string `json:"session_id,omitempty"`

	// Error holds the error message.
	// Set when Type is StreamEventError.
	Error string `json:"error,omitempty"`

	// RequestID correlates this event with the originating request.
	// May be set by the server or propagated from the request.
	RequestID string `json:"request_id,omitempty"`

	// Hash is the SHA-256 hash of this event for tamper-evident logging.
	// Formula: SHA256(Content || CreatedAt || PrevHash)
	Hash string `json:"hash,omitempty"`

	// PrevHash is the hash of the previous event in the chain.
	// First event has empty PrevHash; subsequent events chain to previous.
	PrevHash string `json:"prev_hash,omitempty"`
}

StreamEvent represents a single event from a streaming LLM response.

This is an immutable value type designed to flow through the streaming pipeline. Each event carries exactly one piece of information; the Type field determines which content field is populated.

Field population by Type:

Type=status:   Message is set
Type=token:    Content is set
Type=thinking: Content is set
Type=sources:  Sources is set
Type=done:     SessionID is set (may be empty)
Type=error:    Error is set

Tracing:

Every event has an Id (UUID) and CreatedAt timestamp for tracing.
The Id is generated when the event is created (by parser or constructor).
CreatedAt is the local timestamp when the event was created.

Example:

event := StreamEvent{
    Id:        uuid.New().String(),
    CreatedAt: time.Now().UnixMilli(),
    Type:      StreamEventToken,
    Content:   "Hello",
}

func NewDoneEvent

func NewDoneEvent(sessionID string) StreamEvent

NewDoneEvent creates a done event with the given session ID.

Automatically generates Id and sets CreatedAt to current time.

func NewErrorEvent

func NewErrorEvent(errMsg string) StreamEvent

NewErrorEvent creates an error event with the given error message.

Automatically generates Id and sets CreatedAt to current time.

func NewSourcesEvent

func NewSourcesEvent(sources []SourceInfo) StreamEvent

NewSourcesEvent creates a sources event with the given sources.

Automatically generates Id and sets CreatedAt to current time.

func NewStatusEvent

func NewStatusEvent(message string) StreamEvent

NewStatusEvent creates a status event with the given message.

Automatically generates Id and sets CreatedAt to current time.

func NewThinkingEvent

func NewThinkingEvent(content string) StreamEvent

NewThinkingEvent creates a thinking event with the given content.

Automatically generates Id and sets CreatedAt to current time.

func NewTokenEvent

func NewTokenEvent(content string) StreamEvent

NewTokenEvent creates a token event with the given content.

Automatically generates Id and sets CreatedAt to current time.

func (StreamEvent) CreatedAtTime

func (e StreamEvent) CreatedAtTime() time.Time

CreatedAtTime returns CreatedAt as a time.Time value.

func (StreamEvent) IsTerminal

func (e StreamEvent) IsTerminal() bool

IsTerminal returns true if this event ends the stream.

type StreamEventType

type StreamEventType string

StreamEventType categorizes streaming events from LLM responses.

Events flow through the streaming pipeline in a defined order:

  1. StatusEvent(s) - Optional progress updates ("Searching...", "Generating...")
  2. SourcesEvent - Optional retrieved sources (RAG only, may arrive before or during tokens)
  3. ThinkingEvent(s) - Optional reasoning tokens (Claude extended thinking)
  4. TokenEvent(s) - The actual response tokens
  5. DoneEvent or ErrorEvent - Terminal event, exactly one must occur

Example event sequence for RAG chat:

status:"Retrieving documents..." → sources:[doc1,doc2] → token:"The" → token:" answer" → done
const (
	// StreamEventStatus indicates a progress update.
	//
	// These events provide user feedback during long operations.
	// Renderers typically display these as spinner messages.
	//
	// Example payload: {"type":"status","message":"Searching knowledge base..."}
	StreamEventStatus StreamEventType = "status"

	// StreamEventToken indicates a response token from the LLM.
	//
	// Tokens arrive incrementally as the model generates its response.
	// Renderers typically print these immediately for a streaming effect.
	//
	// Example payload: {"type":"token","content":"Hello"}
	StreamEventToken StreamEventType = "token"

	// StreamEventThinking indicates reasoning/thinking tokens.
	//
	// These are generated by Claude's extended thinking feature.
	// Renderers may display these differently (muted, collapsible, or hidden).
	//
	// Example payload: {"type":"thinking","content":"Let me analyze this..."}
	StreamEventThinking StreamEventType = "thinking"

	// StreamEventSources indicates retrieved knowledge base sources.
	//
	// Only present in RAG responses. May arrive before, during, or after tokens.
	// Renderers should display these inline as they arrive.
	//
	// Example payload: {"type":"sources","sources":[{"source":"doc.pdf","score":0.95}]}
	StreamEventSources StreamEventType = "sources"

	// StreamEventDone indicates successful stream completion.
	//
	// This is a terminal event - no more events will follow.
	// Contains the final session ID for multi-turn conversations.
	//
	// Example payload: {"type":"done","session_id":"sess-abc123"}
	StreamEventDone StreamEventType = "done"

	// StreamEventError indicates an error during streaming.
	//
	// This is a terminal event - no more events will follow.
	// The stream should be considered failed after this event.
	//
	// Example payload: {"type":"error","error":"Model overloaded, please retry"}
	StreamEventError StreamEventType = "error"
)

func (StreamEventType) IsTerminal

func (t StreamEventType) IsTerminal() bool

IsTerminal returns true if this event type ends the stream.

After a terminal event (Done or Error), no more events will arrive. Callers should stop reading and finalize rendering.

func (StreamEventType) String

func (t StreamEventType) String() string

String returns the string representation of the event type.

type StreamReader

type StreamReader interface {
	// Read processes a stream, invoking callback for each event.
	//
	// Parameters:
	//   - ctx: Context for cancellation. When cancelled, stops reading.
	//   - r: The source to read from. Caller is responsible for closing.
	//   - callback: Invoked for each parsed event. Return error to stop.
	//
	// Returns:
	//   - error: nil on successful completion, otherwise the error that
	//     stopped reading (context cancellation, parse error, or callback error)
	//
	// The stream is considered complete when:
	//   - EOF is reached
	//   - A terminal event (done/error) is received
	//   - Context is cancelled
	//   - Callback returns an error
	Read(ctx context.Context, r io.Reader, callback StreamCallback) error

	// ReadAll reads the entire stream and returns aggregated result.
	//
	// This is a convenience method that collects all events into a
	// StreamResult. Use Read() when you need real-time event processing.
	//
	// Parameters:
	//   - ctx: Context for cancellation.
	//   - r: The source to read from. Caller is responsible for closing.
	//
	// Returns:
	//   - *StreamResult: Aggregated result with answer, sources, etc.
	//   - error: nil on success, otherwise the error that stopped reading.
	//
	// Note: If the stream ends with an error event, the error is captured
	// in StreamResult.Error and this method returns nil (not an error).
	ReadAll(ctx context.Context, r io.Reader) (*StreamResult, error)
}

StreamReader reads streaming responses and invokes callbacks.

This interface abstracts the reading of streaming LLM responses. Implementations handle the specific wire format (SSE, JSONL, etc.) and emit parsed StreamEvent structs.

Thread Safety:

StreamReader implementations must be safe for concurrent use.
However, a single Read/ReadAll operation should not be called
concurrently on the same reader instance.

Example:

reader := NewSSEStreamReader(NewSSEParser())

err := reader.Read(ctx, httpResp.Body, func(event StreamEvent) error {
    switch event.Type {
    case StreamEventToken:
        fmt.Print(event.Content)
    case StreamEventError:
        return errors.New(event.Error)
    }
    return nil
})

func NewSSEStreamReader

func NewSSEStreamReader(parser SSEParser) StreamReader

NewSSEStreamReader creates a new SSE stream reader.

Parameters:

  • parser: The SSE parser to use for line parsing.

Returns a StreamReader that handles SSE format.

Example:

reader := NewSSEStreamReader(NewSSEParser())

type StreamRenderer

type StreamRenderer interface {
	// OnStatus renders a status update (e.g., "Searching...").
	//
	// In interactive mode, may start or update a spinner.
	// In machine mode, prints "STATUS: message".
	//
	// Thread-safe. May be called concurrently with other methods.
	OnStatus(ctx context.Context, message string)

	// OnToken renders a single token from the LLM response.
	//
	// In interactive mode, prints immediately for streaming effect.
	// In machine mode, buffers until Finalize().
	//
	// Tokens should be rendered in order; out-of-order rendering
	// may produce garbled output.
	OnToken(ctx context.Context, token string)

	// OnThinking renders thinking/reasoning tokens (Claude extended thinking).
	//
	// May be styled differently (muted, collapsible) or hidden based on config.
	// In machine mode, buffers until Finalize().
	OnThinking(ctx context.Context, content string)

	// OnSources renders retrieved knowledge base sources inline.
	//
	// Called when sources event arrives (may be before, during, or after tokens).
	// In interactive mode, displays sources immediately for visibility.
	OnSources(ctx context.Context, sources []SourceInfo)

	// OnDone signals stream completion with optional session ID.
	//
	// Stops spinners, flushes buffers, prints final newlines.
	// This is typically the last On* method called (unless OnError).
	OnDone(ctx context.Context, sessionID string)

	// OnError renders an error that occurred during streaming.
	//
	// Stops spinners and displays error message.
	// After OnError, only Finalize() should be called.
	OnError(ctx context.Context, err error)

	// Finalize performs cleanup (stop spinners, flush output).
	//
	// MUST be called when streaming ends, even if abnormally.
	// Safe to call multiple times; subsequent calls are no-ops.
	// Typically called with defer immediately after creating renderer.
	Finalize()

	// Result returns the accumulated result after streaming completes.
	//
	// Contains the full answer, sources, session ID, and metadata.
	// May be called before Finalize() to get partial results.
	Result() *StreamResult
}

StreamRenderer renders streaming events to an output destination.

Each method handles exactly one event type. The renderer owns all output-related state (spinners, buffers, formatters). Callers should invoke methods in the order events are received.

Thread Safety:

Implementations must be safe for concurrent calls. Multiple goroutines
may invoke methods simultaneously when processing events from channels.

Lifecycle:

  1. Create renderer with New*StreamRenderer()
  2. Call On* methods as events arrive
  3. Call Finalize() when stream ends (always, even on error)
  4. Call Result() to get aggregated result

Example:

renderer := NewTerminalStreamRenderer(os.Stdout, GetPersonality())
defer renderer.Finalize()

for event := range events {
    switch event.Type {
    case StreamEventToken:
        renderer.OnToken(ctx, event.Content)
    case StreamEventDone:
        renderer.OnDone(ctx, event.SessionID)
    }
}

result := renderer.Result()

func NewBufferStreamRenderer

func NewBufferStreamRenderer() StreamRenderer

NewBufferStreamRenderer creates a renderer that buffers events to memory.

This constructor creates a renderer for testing purposes. It captures all events without producing any output, allowing tests to verify event processing logic.

Returns:

A StreamRenderer that captures events for later inspection. The returned
renderer has an Id and CreatedAt already set on its internal result.

Example:

renderer := NewBufferStreamRenderer()
defer renderer.Finalize()

renderer.OnToken(ctx, "Hello")
renderer.OnToken(ctx, " world")
renderer.OnDone(ctx, "sess-123")

result := renderer.Result()
if result.Answer != "Hello world" {
    t.Error("unexpected answer")
}

// Inspect individual events
bufRenderer := renderer.(*bufferStreamRenderer)
events := bufRenderer.Events()
if len(events) != 3 {
    t.Errorf("expected 3 events, got %d", len(events))
}

func NewTerminalStreamRenderer

func NewTerminalStreamRenderer(w io.Writer, personality PersonalityLevel) StreamRenderer

NewTerminalStreamRenderer creates a renderer for interactive terminal output.

This constructor creates a renderer optimized for interactive terminal use. It supports spinners, colors, and real-time streaming based on personality.

Parameters:

  • w: The output writer. If nil, defaults to os.Stdout.
  • personality: Controls output styling. Use GetPersonality().Level for the user's configured personality, or hardcode for specific behavior.

Returns:

A StreamRenderer that displays events interactively. The returned renderer
has an Id and CreatedAt already set on its internal result.

Example:

// Use user's configured personality
renderer := NewTerminalStreamRenderer(os.Stdout, GetPersonality().Level)
defer renderer.Finalize()

// Force machine-readable output
renderer := NewTerminalStreamRenderer(os.Stdout, PersonalityMachine)

type StreamResult

type StreamResult struct {
	// Id is the unique identifier for this result.
	Id string `json:"id"`

	// CreatedAt is the Unix timestamp (milliseconds) when streaming began.
	CreatedAt int64 `json:"created_at"`

	// CompletedAt is the Unix timestamp (milliseconds) when streaming ended.
	CompletedAt int64 `json:"completed_at"`

	// RequestID correlates this result with the originating request.
	RequestID string `json:"request_id,omitempty"`

	// Answer is the complete response text (all tokens concatenated).
	Answer string `json:"answer"`

	// Thinking is the complete thinking/reasoning text (Claude extended thinking).
	// May be empty if thinking was not enabled or not generated.
	Thinking string `json:"thinking,omitempty"`

	// Sources is the list of retrieved knowledge base sources.
	// Empty for non-RAG responses.
	Sources []SourceInfo `json:"sources,omitempty"`

	// SessionID is the conversation session identifier.
	// Used for multi-turn conversation continuity.
	SessionID string `json:"session_id,omitempty"`

	// Error is set if the stream ended with an error event.
	// If non-empty, Answer may be incomplete or empty.
	Error string `json:"error,omitempty"`

	// TotalTokens is the number of token events received.
	TotalTokens int `json:"total_tokens"`

	// ThinkingTokens is the number of thinking token events received.
	ThinkingTokens int `json:"thinking_tokens"`

	// TotalEvents is the total number of events received.
	TotalEvents int `json:"total_events"`

	// ModelID identifies which model generated the response.
	ModelID string `json:"model_id,omitempty"`

	// FirstTokenAt is when the first token event arrived (Unix ms).
	FirstTokenAt int64 `json:"first_token_at,omitempty"`

	// ChainHash is the final hash of the event chain.
	// This is the Hash from the last (done/error) event.
	// Used to verify the complete stream integrity.
	ChainHash string `json:"chain_hash,omitempty"`

	// ContentHash is SHA-256 hash of the accumulated answer content.
	// Used for content integrity verification separate from the chain.
	ContentHash string `json:"content_hash,omitempty"`
}

StreamResult contains the aggregated result after stream completion.

This struct accumulates all events from a streaming response into a single result object. It's populated by StreamReader.ReadAll() or by a StreamRenderer after processing all events.

Tracing:

Id is a unique identifier for this result (UUID).
CreatedAt is when aggregation began (first event received).
CompletedAt is when the stream ended (done/error event).

Example:

result := StreamResult{
    Id:        uuid.New().String(),
    CreatedAt: startTime.UnixMilli(),
    Answer:    "The capital of France is Paris.",
    Sources:   []SourceInfo{{Source: "geography.pdf", Score: 0.95}},
    SessionID: "sess-abc123",
}

func NewStreamResult

func NewStreamResult() *StreamResult

NewStreamResult creates a new StreamResult with Id and CreatedAt set.

Call this at the start of streaming to initialize the result. Update fields as events arrive, then set CompletedAt when done.

func NewStreamResultWithRequestID

func NewStreamResultWithRequestID(requestID string) *StreamResult

NewStreamResultWithRequestID creates a new StreamResult with a request ID.

Use this when you have a request ID to correlate with server logs.

func RenderStreamToResult

func RenderStreamToResult(ctx context.Context, reader StreamReader, source io.Reader) (*StreamResult, error)

RenderStreamToResult is a convenience function that reads a stream and returns the aggregated result.

This function combines StreamReader and internal buffering into a single call. Use for simple cases where you just need the final result without custom rendering.

Parameters:

  • ctx: Context for cancellation. When cancelled, reading stops.
  • reader: StreamReader to use for parsing the stream format.
  • source: io.Reader containing the stream data. Caller is responsible for closing this reader.

Returns:

  • *StreamResult: The aggregated result containing answer, sources, etc.
  • error: Non-nil if reading failed (parse error, context cancelled, etc.)

Example:

reader := NewSSEStreamReader(NewSSEParser())
result, err := RenderStreamToResult(ctx, reader, httpResp.Body)
if err != nil {
    return err
}
fmt.Println(result.Answer)

func (StreamResult) CompletedAtTime

func (r StreamResult) CompletedAtTime() time.Time

CompletedAtTime returns CompletedAt as a time.Time value.

func (StreamResult) CreatedAtTime

func (r StreamResult) CreatedAtTime() time.Time

CreatedAtTime returns CreatedAt as a time.Time value.

func (StreamResult) Duration

func (r StreamResult) Duration() time.Duration

Duration returns the total streaming duration.

func (StreamResult) FirstTokenAtTime

func (r StreamResult) FirstTokenAtTime() time.Time

FirstTokenAtTime returns FirstTokenAt as a time.Time value.

func (StreamResult) HasError

func (r StreamResult) HasError() bool

HasError returns true if the stream ended with an error.

func (StreamResult) TimeToFirstToken

func (r StreamResult) TimeToFirstToken() time.Duration

TimeToFirstToken returns the duration from stream start to first token.

Returns zero if no tokens were received or timing data is unavailable.

func (StreamResult) TokensPerSecond

func (r StreamResult) TokensPerSecond() float64

TokensPerSecond returns the average token generation rate.

Returns zero if no tokens were received or timing data is unavailable.

type TimestampAuthority

type TimestampAuthority interface {
	// GetTimestamp requests a trusted timestamp for content hash.
	//
	// # Inputs
	//
	//   - contentHash: Hash of content to timestamp
	//
	// # Outputs
	//
	//   - TimestampToken: RFC 3161 timestamp token
	//   - error: Non-nil if TSA unavailable
	GetTimestamp(contentHash string) (*TimestampToken, error)

	// VerifyTimestamp verifies a timestamp token.
	//
	// # Inputs
	//
	//   - token: Previously obtained timestamp token
	//   - contentHash: Hash to verify against
	//
	// # Outputs
	//
	//   - bool: True if timestamp is valid
	//   - error: Non-nil if verification failed
	VerifyTimestamp(token *TimestampToken, contentHash string) (bool, error)
}

TimestampAuthority provides trusted timestamping services.

Description

Enterprise extension for RFC 3161 trusted timestamps. Proves that content existed at a specific point in time.

Thread Safety

Implementations must be safe for concurrent use.

Enterprise Use Cases

  • Legal evidence timestamping
  • Regulatory compliance (MiFID II, SOX)
  • Audit trail integrity

type TimestampToken

type TimestampToken struct {
	// Token is the DER-encoded timestamp token
	Token []byte `json:"token"`

	// Timestamp is the time asserted by the TSA
	Timestamp time.Time `json:"timestamp"`

	// TSAName is the name of the Timestamp Authority
	TSAName string `json:"tsa_name"`

	// SerialNumber is the unique token serial number
	SerialNumber string `json:"serial_number"`
}

TimestampToken represents an RFC 3161 timestamp token.

Description

Contains the timestamp response from a Timestamp Authority. Used for proving content existed at a specific time.

type VerificationAuditEvent

type VerificationAuditEvent struct {
	// SessionID being verified
	SessionID string `json:"session_id"`

	// UserID who requested verification (from auth context)
	UserID string `json:"user_id"`

	// TenantID for multi-tenant deployments
	TenantID string `json:"tenant_id"`

	// Timestamp of verification attempt
	Timestamp time.Time `json:"timestamp"`

	// Success indicates if verification passed
	Success bool `json:"success"`

	// FailureReason if verification failed
	FailureReason string `json:"failure_reason,omitempty"`

	// IPAddress of requester
	IPAddress string `json:"ip_address"`

	// RequestID for correlation
	RequestID string `json:"request_id"`
}

VerificationAuditEvent contains details for audit logging.

Description

Captures all relevant information about a verification attempt for compliance audit trails.

type VerificationAuthorizer

type VerificationAuthorizer interface {
	// CanVerify checks if a user can verify a session.
	//
	// # Inputs
	//
	//   - userID: User requesting verification
	//   - sessionID: Session to verify
	//
	// # Outputs
	//
	//   - bool: True if authorized
	//   - error: Non-nil if authorization check failed
	CanVerify(userID string, sessionID string) (bool, error)
}

VerificationAuthorizer checks authorization for verification operations.

Description

Enterprise extension for access control on verification. Ensures users can only verify sessions they have access to.

Thread Safety

Implementations must be safe for concurrent use.

Enterprise Use Cases

  • Multi-tenant session isolation
  • Role-based access control
  • Data sovereignty compliance

Jump to

Keyboard shortcuts

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