NAME
Database::BI::Summary - Narrative summary generation for an analysis result
SYNOPSIS
use Database::BI::Summary;
my $result = Database::BI::Summary::generate(
insights => $insights_aref, # from Database::BI::Insights::compute()
label => 'sales',
row_count => 39,
);
# $result = { text => "...", source => "template", cached => 0 }
DESCRIPTION
Generates a 2-4 sentence plain-English summary of an analysis result. Two paths are available:
- Template path (default, always offline)
-
Composes deterministic sentences from the insight kinds and headlines. No external service required.
- LLM path (opt-in)
-
Sends only aggregate facts (insight kinds, headlines, and detail strings) to the Anthropic Messages API. Raw row data, file paths, and the spec_key are never included in the LLM prompt. The LLM path falls back silently to the template path if the provider call fails.
Results are cached in a package-level hash keyed by an MD5 fingerprint of the insight content. Cached results are served for up to one hour.
FUNCTIONS
generate(%args)
my $result = Database::BI::Summary::generate(
insights => \@insights,
spec_key => 'table:sales',
label => 'sales',
row_count => 39,
use_ai => 0,
ai_config => undef,
);
Returns a hashref with keys text, source ('template', 'template_fallback', or 'llm'), and cached (1 if served from cache, 0 if freshly generated).
use_ai is only honoured when ai_config is provided and ai_config->{api_key} is non-empty.
API SPECIFICATION
INPUT
{
insights => 'arrayref', # optional; insight hashrefs from Database::BI::Insights::compute (default [])
spec_key => 'string', # optional; accepted for the caller's bookkeeping, never sent to the LLM
label => 'string', # optional; dataset name (default "dataset")
row_count => 'integer', # optional; rows analysed (default 0)
use_ai => 'boolean', # optional; ask for LLM prose
ai_config => 'hashref', # optional; { api_key, model, provider }
}
OUTPUT
{
text => 'string', # 2-4 sentences
source => 'template' | 'template_fallback' | 'llm',
cached => 0 | 1,
}
generate does not croak. A failed provider call gives the template text with source => 'template_fallback'. The same insights, label, row count and use_ai within an hour return the cached text with cached => 1.
AUTHOR
Nigel Horne <njh@nigelhorne.com>
LICENCE AND COPYRIGHT
Copyright 2026 Nigel Horne.
Usage is subject to the GPL2 licence terms.