Summarization Is a Contract
Summarization looks like the ideal local AI feature.
The input is already in the browser. The desired output is smaller than the input. The task is useful for articles, documentation, conversations, reviews and support content. Sending the entire source to a remote provider can feel disproportionate when a browser-managed model can operate on the device.
So the implementation seems obvious:
const summarizer = await Summarizer.create();
const result = await summarizer.summarize(text);
The code is correct.
The feature is unspecified.
What kind of summary do we need? How long should it be? Is Markdown acceptable? Which facts must survive? Is the aim extraction, synthesis, a headline or a teaser? What happens when the source exceeds the model’s effective capacity? How will we detect a fluent summary that reverses the central claim?
A summary is not merely shorter text.
It is a compression contract.
1. Compression has an objective
Let the source document contain information $X$, and let the summary contain a smaller representation $S$.
The trivial compression function is:
A useful summarizer has to optimize several competing properties:
- $C$ measures coverage of important source content;
- $F$ measures factual faithfulness to the source;
- $R$ measures readability;
- $L$ measures compliance with the requested length and form.
Those quantities can conflict. Adding a qualification may improve faithfulness while making a headline too long. Removing detail may improve readability while deleting the exception that gives the claim its meaning.
The API cannot infer our product weights from the word “summarize.”
We need to declare what kind of loss is acceptable.
2. Summary type changes the job
The Summarizer API exposes four summary types:
| Type | Intended operation |
|---|---|
key-points |
Extract the most important points as a list |
tldr |
Provide a compact overview |
teaser |
Select intriguing material that encourages reading |
headline |
Express the main point as a headline |
These are not style variations on one target.
A good teaser can omit the conclusion on purpose. A good key-points summary should not. A headline compresses toward one central proposition. A TL;DR can preserve a small narrative arc.
If we evaluate all four against one reference paragraph, we will punish correct differences in function.
The first rule of summarization evaluation is therefore:
Specify what the compressed text is for before judging what it retained.
Browser AI Observatory records type as creation configuration, so a trace never contains an unlabelled “summary” result.
3. Length is semantic, not merely numeric
The API offers short, medium and long, but their expected forms vary with summary type. Chrome documents key-points lengths in bullet counts, TL;DR and teaser lengths in sentence counts, and headline lengths in approximate word limits.
That design encodes a useful idea: length should be expressed in units that fit the artifact.
key points → bullets
TL;DR → sentences
headline → words
A generic character maximum would be easier to implement but less meaningful.
Even these category-specific targets are structural limits, not evidence of semantic sufficiency. Three bullets can still repeat the same idea. Five sentences can omit the conclusion. A twelve-word headline can reverse who did what.
So our fixture separates:
- form checks: bullet count, word count, format;
- coverage checks: required concepts or claims;
- faithfulness checks: no contradiction or unsupported addition;
- usefulness checks: the result serves the reader’s actual need.
Only the first category is easy to automate without another semantic judge.
4. Format is part of the downstream system
The Summarizer API can produce Markdown or plain text.
That option affects more than appearance.
Markdown key points can be rendered as a list, copied into a document or parsed structurally. They can also contain links, code-like spans or malformed syntax. If the application injects generated Markdown as raw HTML, it creates a rendering and security problem unrelated to summary quality.
Plain text is easier to display safely but removes structural cues some tasks need.
Our panel renders output using textContent:
response.append(document.createTextNode(chunk));
This preserves the generated characters without interpreting them as HTML. A future Markdown renderer must sanitize its output and should retain the raw text in the trace.
The format contract is therefore two-sided:
model promises a representation
application promises safe interpretation
One cannot substitute for the other.
5. Preference exposes a real trade-off
The API includes a preference option with values such as auto, speed and capability.
That gives us an unusually useful experiment. We can keep the source, summary type, format and length fixed while changing the browser’s declared optimization preference.
const base = {
type: "key-points",
format: "markdown",
length: "medium",
expectedInputLanguages: ["en"],
expectedContextLanguages: ["en"],
outputLanguage: "en"
};
const fast = await Summarizer.create({
...base,
preference: "speed"
});
const capable = await Summarizer.create({
...base,
preference: "capability"
});
For each route, record:
- availability for the exact options;
- whether additional acquisition occurs;
- session creation time;
- time to first output;
- total generation time;
- output size;
- structural checks;
- coverage and faithfulness review.
We should not infer from the option name that capability produces the better answer in every case. We should test whether any quality improvement is visible on the task and worth the latency cost.
6. Context is not source text
The Summarizer API separates source input from context.
const summary = await summarizer.summarize(source, {
context: "The reader understands web development but is new to local AI."
});
The source contains the claims to compress.
The context describes how the compression should be interpreted or presented.
Mixing them creates two problems.
First, a context instruction can accidentally be summarized as though it were part of the document. Second, untrusted source text can be treated as control instructions if the application concatenates everything into one prompt.
A task-specific API gives us distinct fields, but it does not eliminate adversarial content. The model still processes language, and a source document can contain instructions aimed at changing its behavior.
The application should preserve provenance:
trusted application configuration
trusted task context
untrusted source document
generated summary
The observatory should show these roles separately when content capture is enabled.
7. Remove markup before summarizing
When summarizing a page, the convenient input is often:
document.querySelector("article").innerHTML
That sends tags, attributes, hidden structure and possibly unrelated controls into the model input.
Chrome’s documentation recommends removing unnecessary markup and using rendered text such as innerText where appropriate.
const article = document.querySelector("article");
const source = article?.innerText ?? "";
Even innerText is not a complete content extractor. Navigation labels, captions, related links and advertisements can still enter the input depending on the selected element. A reading tool needs a content-selection policy and a visible indication of what will be summarized.
The debugger should record extraction metrics before model invocation:
{
"type": "prompt.started",
"data": {
"apiId": "summarizer",
"input": {
"chars": 18422,
"bytes": 19108,
"redacted": true
},
"extractor": "article-inner-text/1"
}
}
If a summary fails because the extractor selected the wrong material, changing the model will not fix it.
8. Build the first canonical fixture
The extension includes a short architectural source:
A browser-managed AI API moves model selection, download, update, and inference runtime behind a browser capability boundary. The application still owns feature design, availability handling, consent, validation, fallback, and user-visible failure recovery. Local execution can improve privacy and latency, but those properties belong to the complete data flow and measured lifecycle rather than to the word local.
The fixture requests short Markdown key points and supplies this context:
Preserve the distinction between browser responsibility and application responsibility.
Its automatic assertions are intentionally transparent:
[
{ kind: "min-bullets", value: 3 },
{ kind: "contains", value: "browser" },
{ kind: "contains", value: "application" }
]
The human rubric is the part that matters most:
No claim that local execution is automatically private or faster.
Why not automate that rubric with not-contains: "private"?
Because a correct summary should be allowed to mention privacy. The failure is a relational change from can improve privacy under a complete data-flow analysis to is private because it is local. Word matching cannot distinguish those claims.
This is a compact example of a larger evaluation rule:
Automate the property you can actually measure, and leave the remaining judgment visibly unresolved.
9. Run the canonical contract in this chapter
Select Run with Browser AI from Chapter 06 to open:
/tools/ai/browser-ai-from-first-principles/06-chapter/
The page loads the same canonical Summarizer fixture used by Browser AI Observatory. The source, context, default options and assertions therefore describe one shared experiment rather than a website imitation.
The contract controls are executable:
type key-points | tldr | teaser | headline
format markdown | plain-text
length short | medium | long
preference auto | speed | capability
Choose a configuration and click Inspect Summarizer. The page calls Summarizer.availability(options) with the exact values currently displayed. Prepare Summarizer then calls Summarizer.create() with those same options and records acquisition progress and session-creation time.
Changing any creation option destroys the prepared instance and invalidates the old preflight. This is intentional. A session created for short Markdown key points is not evidence that a capability-oriented headline contract is available.
Run contract sends the source as source text and the fixture instruction as context. The two fields remain separate in both the API call and the event trace. Streaming chunks appear as text rather than interpreted Markdown, preserving the safe rendering boundary established earlier.
When the operation completes, the page evaluates four checks:
| Check | Decision |
|---|---|
| At least three Markdown bullets | Automatic |
| Contains “browser” | Automatic |
| Contains “application” | Automatic |
| Does not claim local is automatically private or faster | Human review |
The first three checks are transparent but weak. Passing them proves structural compliance and minimal lexical coverage. A summary can contain both required words and still reverse the relationship between them. The fourth check therefore remains yellow and unresolved; the interface does not silently convert an absent semantic judge into a pass.
The full event sequence enters the same Observatory trace used by earlier chapters:
capability.inspect.*
session.create.*
model.download.progress
prompt.started
prompt.chunk
prompt.finished
fixture.evaluated
The events retain apiId: "summarizer", session identity, prompt identity, timings, sizes and metrics-only capture metadata. Operational completion and fixture acceptance remain distinct events.
Replay mode is equally strict. The reviewed September 2 export contains Prompt API session evidence but no Summarizer events. The lab says so directly. It does not use a successful LanguageModel session as evidence that the canonical Summarizer contract ran.
This is the pattern the rest of the task-API chapters will follow: declare a task contract, inspect those exact options, create deliberately, run a canonical fixture, automate only defensible checks, and leave unresolved judgment visible.
10. Run repeated trials
One generative result tells us very little about stability.
For each configuration, run the fixture several times in a controlled environment. Preserve:
browser version
experimental flags
operating system
hardware class
API options
model readiness before run
trace events
output
automatic checks
human decision
Then calculate rates rather than anecdotes:
The observatory export gives us the event substrate. A later chapter will add batch execution and comparison reports.
11. Long documents require another architecture
A browser page can be much larger than the input the model can use effectively. Truncating at an arbitrary character count is simple and dangerous: conclusions, exceptions and later evidence disappear systematically.
A common alternative is hierarchical summarization:
flowchart TD
D[Document] --> C[Semantic chunks]
C --> S[Chunk summaries]
S --> F[Final synthesis]
This extends capacity but introduces another loss surface. Each chunk summary can omit a fact. The final synthesis only sees what survived the earlier stage. Cross-chunk relationships can disappear.
The debugger must preserve lineage:
document
├── chunk 1 → summary 1
├── chunk 2 → summary 2
└── chunk 3 → summary 3
↓
final summary
Without lineage, a developer sees only the final omission and cannot identify the stage where the information vanished.
We postpone this implementation until the context chapters because chunking depends on actual capacity and session behavior. The important point here is that “summarize long input” is not one model call with a larger string.
12. A successful API call is the start of evaluation
When summarize() resolves, the runtime has succeeded.
The product may still have failed because the result:
- omitted a required claim;
- introduced a claim absent from the source;
- removed a qualification;
- merged two actors;
- used the wrong summary function;
- produced unsafe markup;
- met length at the cost of meaning;
- sounded plausible enough that nobody noticed.
The observatory records runtime completion first, then fixture evaluation:
prompt.finished { outcome: completed }
fixture.evaluated { automatic pass: true, human checks: 1 }
feature.validation.finished { accepted: false }
The events do not contradict one another. They describe different layers.
Conclusion
Summarization is lossy compression under a task-specific contract.
The Summarizer API lets an application declare type, format, length, preference, languages and context. Those fields make the operation easier to inspect than an unstructured prompt. They do not decide which information the product can afford to lose.
A useful evaluation separates form, coverage, faithfulness and usefulness. It records operational state independently from behavioral acceptance. It repeats fixtures. It preserves extraction and chunk lineage. It refuses to turn a lexical check into a claim about meaning.
Chapter 06 now makes that contract executable. The reader can change one declared dimension, re-run the exact preflight and session lifecycle, inspect the resulting trace, and see which conclusions are automatic and which still require judgment.
The next chapter moves from compression to transformation. Writer, Rewriter and Proofreader all operate on text, but they have different authority over it. Treating them as interchangeable is how a request to fix grammar becomes an unrequested rewrite.
Sources and further reading
- Chrome for Developers, Summarize with built-in AI.
- Chrome for Developers, Inform users of model download.
- Chrome for Developers, Session compacting with the Prompt API.
- Chrome for Developers, Create AI evaluations.