summarizer

package
v1.20.0 Latest Latest
Warning

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

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

Documentation

Overview

Package summarizer is the home of Harbor's production LLM-backed summarisation: the memory-subsystem `memory.Summarizer` and the planner-trajectory `planner.Summariser`.

Two interfaces — do not conflate

The package satisfies TWO unrelated interfaces that happen to share a name root:

  • `memory.Summarizer` (Summarizer, `New`) — the memory subsystem's rolling-summary compactor: conversation window (previous summary + evicted turns) in, free-text summary out.
  • `planner.Summariser` (TrajectorySummariser, `NewTrajectorySummariser`) — the runtime's trajectory compression producer: a run's Trajectory in, the five-field `planner.TrajectorySummary` out (structured-output JSON-schema mode).; invoked by `planner.CompressionRunner.MaybeCompress` from the steering RunLoop when `Budget.TokenBudget` is exceeded.

Different signatures, different inputs, different consumers — the shared home exists because both are "LLM client + versioned compaction prompt" compositions (the import direction is clean: summarizer → planner/memory → llm; nothing imports back).

The remainder of this doc describes the memory-subsystem Summarizer; see trajectory.go for the TrajectorySummariser.

Before the only `memory.Summarizer` Harbor shipped was `strategy.EchoSummarizer` — a deterministic test stub that concatenated turns into a single string. The runtime's `rolling_summary` strategy required an *injected* Summarizer with no default, so a `harbor dev` boot against `memory.strategy: rolling_summary` would have crashed with `nil Summarizer` (or worse, silently fallen back to a test stub if a future PR wired one).

Harbor closes that seam: `summarizer.New(client)` composes an `llm.LLMClient` with a versioned compaction prompt and returns a `memory.Summarizer` ready to wire into `memory.Open(..., Deps{Summarizer: ...})`. The dev server wires this when an operator configures `memory.strategy: rolling_summary` (the same conditional the spec laid down).

The compaction prompt

The prompt is versioned (`PromptVersion`) so a later prompt-craft improvement bumps the constant and the prior version stays available for golden-test pinning. The template uses an instruction-tuned shape that works against every native provider:

  • One system message anchors the summariser persona: "You are a concise meeting-minutes summariser. Compress the conversation turns below into a short, factual rolling summary..."
  • One user message stitches the previous summary (if any) + the evicted turns into a single payload. The Summarize implementation uses the request's `PreviousSummary` + `Turns` directly — no reformatting beyond a stable line-prefix scheme so the LLM sees consistent input shape across calls.

Concurrent reuse

`Summarizer` is a compiled artifact: the embedded `llm.LLMClient` and the compaction prompt template are set once at construction (`New`). One Summarizer is safe to share across N concurrent `Summarize` goroutines; the per-call state lives on the function stack and in `ctx`. Concurrent_test.go pins N≥100 under -race.

Identity (CLAUDE.md §6 rule 9)

`Summarize` is identity-mandatory: the request's `identity.Quadruple` is propagated into ctx via `identity.With` before the LLM call so the safety pass sees the same identity the memory subsystem's caller supplied. The LLM-edge audit + governance + retry layers all key on this identity; without it, the safety pass rejects with `ErrIdentityRequired`.

Fail-loud (CLAUDE.md §5)

An LLM call that fails surfaces the wrapped error verbatim — the summariser does NOT silently degrade to an echo or a truncation. Callers handle `Summarize` errors via the `memory.strategy.rolling_summary` health FSM: the strategy transitions from healthy → retry → degraded as failures accumulate.

§13 primitive-with-consumer

the `harbor dev` subcommand is the §13 first consumer of this Summarizer. The dev wiring constructs `summarizer.New(client)` and passes it through `memory.Deps.Summarizer` when the operator configured `memory.strategy: rolling_summary`. Without the summariser would be a dormant primitive; with it is wired end-to-end on the production code path.

trajectory.go — the production LLM-backed planner.Summariser. Distinct from the memory-subsystem Summarizer in summarizer.go: see the package godoc's two-interface disambiguation.

Index

Constants

View Source
const PromptVersion = "v1"

PromptVersion is the stable identifier for the compaction prompt template. Tests pin this; a later prompt-craft improvement bumps the constant. Format: `vN` where N is a monotonically-increasing integer.

View Source
const TrajectoryPromptVersion = "v1"

TrajectoryPromptVersion is the stable identifier for the trajectory compaction prompt template. Tests pin this; a later prompt-craft improvement bumps the constant. Format: `vN`, monotonically increasing — independent of the memory summarizer's PromptVersion.

Variables

This section is empty.

Functions

This section is empty.

Types

type Option

type Option func(*Summarizer)

Option configures a Summarizer at construction time.

func WithModel

func WithModel(model string) Option

WithModel pins the model name the Summarizer requests against the LLMClient. Empty model falls back to the client's resolved `ConfigSnapshot.Model` at request time (the snapshot's default model). Pinning at construction is useful for operators who want a cheap model for compaction independent of the planner's model choice.

func WithSystemPromptExtension added in v1.5.1

func WithSystemPromptExtension(extra string) Option

WithSystemPromptExtension APPENDS operator-supplied guidance to the baseline summariser system prompt — it never replaces it. The extra text is trimmed; an empty (or whitespace-only) value is a no-op that leaves the baseline prompt exactly as shipped.

When set, the composed system message is the baseline prompt, a fixed separator that frames the addition as additive ("extend the above; do not override it"), then the operator text. This lets operators steer what compaction preserves or emphasises without stripping the baseline role framing, conciseness, and preserve-goals guarantees. There is intentionally no full-replace form: the safety floor stays.

type Summarizer

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

Summarizer is the production LLM-backed `memory.Summarizer` Harbor ships. Construct via `New`; do not construct directly.

`Summarizer` is a compiled artifact: every field is set once at construction and never mutated. One Summarizer is safe to share across N concurrent Summarize goroutines.

func New

func New(client llm.LLMClient, opts ...Option) (*Summarizer, error)

New constructs a production Summarizer. The client is mandatory; a nil client returns an error rather than building a Summarizer that would nil-panic on the first Summarize call.

The returned Summarizer satisfies `memory.Summarizer`. Wire it via `memory.Open(..., Deps{Summarizer: s})` when configuring the `rolling_summary` strategy.

func (*Summarizer) Summarize

Summarize implements `memory.Summarizer`. It composes a chat request from the previous summary + the new turns, sends it through the LLM client (which runs through the safety pass + corrections + downgrade + retry + governance chain), and returns the assistant's reply verbatim as the new rolling summary.

Identity is mandatory: `id` is injected into ctx via `identity.With` so the LLM-edge safety pass sees the same identity the memory subsystem's caller supplied. A nil-Quadruple is the caller's responsibility to reject (callers MUST not pass empty identities — the memory subsystem itself enforces).

type TrajectoryOption added in v1.3.0

type TrajectoryOption func(*TrajectorySummariser)

TrajectoryOption configures a TrajectorySummariser at construction.

func WithTrajectoryHeavyOutputThreshold added in v1.3.0

func WithTrajectoryHeavyOutputThreshold(threshold int) TrajectoryOption

WithTrajectoryHeavyOutputThreshold threads the operator's resolved heavy-output threshold (`artifacts.heavy_output_threshold_bytes`) so the aggregate compaction-payload budget tracks the SAME limit the LLM-edge safety pass enforces — a summariser built against a non-default threshold must never compose a payload its own Complete call would reject with ErrContextLeak. The budget becomes threshold − trajectoryPayloadHeadroom, floored at one fragment cap. Non-positive is a no-op (the default applies).

func WithTrajectoryMaxSummaryTokens added in v1.3.0

func WithTrajectoryMaxSummaryTokens(n int) TrajectoryOption

WithTrajectoryMaxSummaryTokens caps the completion's MaxTokens — a guard against a runaway summary. Non-positive is a no-op (the provider default applies).

func WithTrajectoryModel added in v1.3.0

func WithTrajectoryModel(model string) TrajectoryOption

WithTrajectoryModel pins the model the summariser requests against the LLMClient — operators can route compaction to a cheaper (or stronger) model than the planner's. Empty falls back to the client's resolved default model at request time.

func WithTrajectorySystemPrompt added in v1.3.0

func WithTrajectorySystemPrompt(prompt string) TrajectoryOption

WithTrajectorySystemPrompt overrides the versioned compaction system prompt. The override is rendered verbatim; the caller owns keeping it aligned with the five-field output schema. Empty is a no-op (the versioned default stays).

type TrajectorySummariser added in v1.3.0

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

TrajectorySummariser is the production planner.Summariser Harbor ships — the producer half of trajectory compression (RFC §6.2, by design). It composes a compaction prompt over the trajectory's planner-facing projection, calls llm.LLMClient.Complete in structured-output JSON-schema mode, and parses the response into the five-field planner.TrajectorySummary.

Construct via NewTrajectorySummariser; do not construct directly.

TrajectorySummariser is a compiled artifact: every field is set once at construction and never mutated. One instance is safe to share across N concurrent Summarise goroutines; per-call state lives on the function stack and in ctx. trajectory_test.go pins N≥100 under -race.

Fail-loud contract (the planner.Summariser seam's): an LLM error propagates wrapped, never swallowed; a structurally valid but vacuous summary surfaces planner.ErrEmptySummary; a response that does not parse as the five-field object is a loud parse error. There is no silent fall-through to raw history.

func NewTrajectorySummariser added in v1.3.0

func NewTrajectorySummariser(client llm.LLMClient, opts ...TrajectoryOption) (*TrajectorySummariser, error)

NewTrajectorySummariser constructs the production trajectory summariser. The client is mandatory; a nil client returns an error rather than building a summariser that would nil-panic on the first Summarise call.

The returned summariser satisfies planner.Summariser. Wire it via planner.NewCompressionRunner(s) and set the runner on steering.RunSpec.Compression (plus Base.Budget.TokenBudget > 0) — or let the assembly do it from the `planner.token_budget` config knob.

func (*TrajectorySummariser) Summarise added in v1.3.0

Summarise implements planner.Summariser. It renders the trajectory's planner-facing projection (query, goal, per-step action + LLM-visible observation), sends it through the LLM client (which runs the safety pass + corrections + structured-output downgrade + retry + governance chain), and parses the assistant's JSON reply into the five-field planner.TrajectorySummary.

The payload deliberately renders Step.LLMObservation (the heavy-content-disciplined projection — what the planner itself saw) over the raw Step.Observation: raw observations may carry heavy content that must never reach the LLM edge (CLAUDE.md §13 / ErrContextLeak). When a step predates the projection split, the raw observation is used, truncated at trajectoryFragmentCap.

Identity is mandatory: rc.Quadruple's identity is propagated into ctx so the LLM-edge layers (safety, governance, audit) see the same identity the compression runner validated.

Jump to

Keyboard shortcuts

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